Question

I was exploring DataTemplate and found Styling and Templating on MSDN. It says -

"In this sample application, there is a ListBox control that is bound to a list of photos:

<ListBox ItemsSource="{Binding Source={StaticResource MyPhotos}}"
     Background="Silver" Width="600" Margin="10" SelectedIndex="0"/>  

This ListBox currently looks like the following:"

enter image description here

//...some stuff about DataTemplate...//

In our sample application, each custom Photo object has a Source property of type string that specifies the file path of the image. Currently, the photo objects appear as file paths."

"For the photos to appear as images, you create a DataTemplate as a resource:"

<Window.Resources>
...
<!--DataTemplate to display Photos as images
    instead of text strings of Paths-->
<DataTemplate DataType="{x:Type local:Photo}">
  <Border Margin="3">
    <Image Source="{Binding Source}"/>
  </Border>
</DataTemplate>
...
</Window.Resources>  

OK, fair enough, so I declared the Photo class -

public class Photo
{
    public string Source { get; set; }
} 

My XAML sense is not very good (it's bad actually). What I don't get here is the way ItemsSource of the ListBox is set. As far as my XAML goes, "MyPhotos" here have to be an x:Key to an IEnumerable object of Photo type, something like -

<Window.Resources>
...
<local:PhotoList x:Key="MyPhotos"/>
...
</Window.Resources>  

But I don't have any idea how the PhotoList class should look like in this case. So, what the Resource would actually look like that cause the ListBox to be populated with the Source strings of the photos?

FYI : Implementing things in my own way, my DataTemplate exploration got successful, and though this thing here was not my primary concern, I just want to know what I don't know. Can anyone shed some light?

EDIT in response to the Answer provided by H.B.:

The point of the question is to get the result, as shown in the image, before applying the DataTemplate and to get an idea of the Resource that make that happen.

If I make the PhotoList itself an IEnumerable, like -

public class PhotoList : List<Photo>
{
    //some property to hold the collection of Photo objects??
}

and instantiate it within the Resource, then to what the ItemsSource actually gets bound? How it's gonna work? Sorry I didn't understand. I hope you'll elaborate a bit more on this.

So, I went for the DataSourceProvider option. But since the Data property has no setter, I tried the following -

public class Photo
{
    public string Source { get; set; }
}

public class PhotoList : DataSourceProvider
{
    private List<Photo> _photos;

    public PhotoList()
    {
        _photos = new List<Photo>
                      {
                          new Photo{ Source = @"D:\AppImage\1.jpg"},
                          new Photo{ Source = @"D:\AppImage\2.jpg"},
                          new Photo{ Source = @"D:\AppImage\3.jpg"},
                          new Photo{ Source = @"D:\AppImage\4.jpg"},
                          new Photo{ Source = @"D:\AppImage\5.jpg"},
                      };
        ((List<Photo>)this.Data).AddRange(_photos);
    }
}  

And definitely I'm doing something wrong. Because, now i get the Cannot create an instance of "PhotoList" error message while instantiating PhotoList within the Resource -

<Window.Resources>
    <local:PhotoList x:Key="MyPhotos"/>
</Window.Resources>  

Please help me out here.

Was it helpful?

Solution

The PhotoList itself either has to be an IEnumerable of Photo objects, or it needs to provide one, this can be done by inheriting from DataSourceProvider and exposing the list that way.


Edit: Obviously if you just inherit from List<T> nothing will display unless you add items, when i said it should be an IEnumerable i meant an immediate implementation that actually enumerates strings right away. If you just want to create a list in XAML you may as well use x:Array.

Also here would be an example of using the DataSourceProvider:

public class PhotoList : DataSourceProvider
{
    protected override void BeginQuery()
    {
        var files = Directory.GetFiles(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures))
                        .Where(f => Path.GetExtension(f) == ".png")
        OnQueryFinished(files);
    }
}

This will populate your list with the paths of PNG-files in the personal "Pictures" folder.

Also you will not get the DataTemplate-less appearance of the example if you use a class like Photo unless you override ToString to return the source URL because by default if there is no DataTemplate it will just display the full name of the class.


In any case you need Photo objects in your collection for the DataTemplate to apply properly. Also you usually do not have collections directly in XAML but some data object which is the DataContext of the Window/UserControl.

OTHER TIPS

 <Window.Resources>
    <ph:PhotoList x:Key="MyPhotos"  Path="D:\Visual Studio Projects\WPF APPLICATIONS\ImageViewer\ImageViewer\Images">      
    </ph:PhotoList>    
 </Window.Resources>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top