Question

I have a DataTamplate for my ItemsControl that merely contains an Image with some other meta data. What I am trying to do is bind to the ItemsControl and have the Images be displayed with the Convas.Left and Canvas.Top that is bound via the data I give.

I have been trying my best to remove any Panels from the control via the ItemsPanelTemplate, so I can use the Attached Properties in the parent canvas, but it seems that you will always get a StackPanel by default.

Anyone out there have any good ideas?

Thanks, Dave

Was it helpful?

Solution

The layout of items in an ItemsControl is controlled via the ItemsControl.ItemsPanel property which is of type ItemsPanelTemplate. The default value for the ItemsControl.ItemsPanel property is indeed an instance of ItemsPanelTemplate that specifies a StackPanel but this is completely customizable.

The code example (on this MSDN page) shown below the paragraph that starts "The following example creates an ItemsControl." is very useful in understanding what the ItemsControl.Template, ItemsControl.ItemsPanel and ItemsControl.ItemTemplate properties are for.

There are a few ways to achieve what you describe in the second sentence of the first paragraph of your question. Here is a full example:

Page.xaml:

<UserControl x:Class="ItemsControlImages.Page"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:app="clr-namespace:ItemsControlImages">

    <UserControl.Resources>
        <DataTemplate x:Key="CountryTemplate">
            <Canvas>
                <Image Canvas.Top="{Binding Location.Y}"
                       Canvas.Left="{Binding Location.X}"
                       Source="{Binding FlagImage}" />

                <StackPanel Canvas.Top="{Binding Location.Y}"
                            Canvas.Left="{Binding Location.X}">
                    <TextBlock Text="{Binding Title}" />
                    <TextBlock Text="{Binding Location}" />

                    <StackPanel.RenderTransform>
                        <TranslateTransform Y="-32.0" />
                    </StackPanel.RenderTransform>
                </StackPanel>
            </Canvas>
        </DataTemplate>
    </UserControl.Resources>

    <Canvas x:Name="LayoutRoot">
        <Canvas.Background>
            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                <GradientStop Color="#FFB2C6D5"/>
                <GradientStop Color="#FF1483D9" Offset="1"/>
            </LinearGradientBrush>
        </Canvas.Background>

        <ItemsControl ItemTemplate="{StaticResource CountryTemplate}">
            <app:Country Title="Argentina" Location="56,218" FlagImage="Images/ARG.png" />
            <app:Country Title="China" Location="368,66" FlagImage="Images/CHN.png" />
            <app:Country Title="Ireland" Location="192,90" FlagImage="Images/IRE.png" />
            <app:Country Title="New Zealand" Location="404,225" FlagImage="Images/NZ.png" />
            <app:Country Title="United States" Location="40,80" FlagImage="Images/USA.png" />
        </ItemsControl>
    </Canvas>
</UserControl>

Country.cs:

using System.ComponentModel;
using System.Windows;

namespace ItemsControlImages
{
    public class Country : INotifyPropertyChanged
    {
        private string title;
        private string flagImage;
        private Point location;

        public string Title
        {
            get
            {
                return this.title;
            }
            set
            {
                if ((object.ReferenceEquals(this.title, value) != true))
                {
                    this.title = value;
                    this.RaisePropertyChanged("Title");
                }
            }
        }

        public string FlagImage
        {
            get
            {
                return this.flagImage;
            }
            set
            {
                if ((object.ReferenceEquals(this.flagImage, value) != true))
                {
                    this.flagImage = value;
                    this.RaisePropertyChanged("FlagImage");
                }
            }
        }

        public Point Location
        {
            get
            {
                return this.location;
            }
            set
            {
                if ((this.location.Equals(value) != true))
                {
                    this.location = value;
                    this.RaisePropertyChanged("Location");
                }
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        protected void RaisePropertyChanged(string propertyName)
        {
            PropertyChangedEventHandler propertyChanged = this.PropertyChanged;

            if (propertyChanged != null)
            {
                propertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion
    }
}

That's all you need (along with the images in an Images folder) for this end result:

alt text http://www.freeimagehosting.net/uploads/bec683b75e.png

Note even though the Images are in an ItemsControl they are positioned at the coordinates shown by binding the Left and Top attached properties of their parent Canvas to the value of the X and Y coordinates from the custom Location property.

For more information on this sample and customizing the ItemsControl using templates in general you can check out this blog post.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top