Displaying images in a ListView (or something better!) In WPF MVVM using data binding

I am trying to display some images in a ListView and have not been successful. I use WPF MVVM, and ListView is a break in the simple display of claims and rank data. (See My previous post: MVVM in WPF. How to warn ViewModel about changes in Model ... or should I , if you're interested!) That is, I could use something other than ListView (if this is advice), but I would still like to know how to do this with a ListView, considering it doable. My property that I am attached to is in the ViewModel:

public ObservableCollection<Image> PlayerCardImages{ get{ ObservableCollection<Image> results = new ObservableCollection<Image>(); foreach (CardModel card in PlayerCards) { Image img = new Image(); BitmapImage bi3 = new BitmapImage(); bi3.BeginInit(); // TODO: Pick card based on suit/rank. Just get 1 image working now bi3.UriSource = new Uri("diamond-1.png", UriKind.Relative); bi3.EndInit(); img.Stretch = Stretch.Fill; img.Source = bi3; results.Add(img); } return results; } } 

In my XAML codec, I use:

 <Window.Resources> <DataTemplate x:Key="ImageCell"> <StackPanel Orientation="Horizontal"> <Image Source="{Binding PlayerCardImages}" Width="200" Height="200" Stretch="Fill" ToolTip="Add tooltip"/> </StackPanel> </DataTemplate> </Window.Resources> <StackPanel Orientation="Vertical"> <Label Content="Player Cards"/> <ListView Name="lvwTitles" ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" SelectionMode="Single" ItemTemplate="{StaticResource ImageCell}" Height="59"> </ListView> </StackPanel> 

This idea was shamelessly stolen from: WPF - to bind images horizontally to a ListView However, it does not even appear in data binding, as evidenced by my breakpoint in PlayerCardImages, which is not a hit.

I also tried the following XAML with slightly better luck:

  <StackPanel Orientation="Vertical"> <Label Content="Player Cards"/> <ListView AlternationCount="2" DataContext="{StaticResource PlayerCardsGroups }" ItemsSource="{Binding}" > <ListView.GroupStyle> <StaticResourceExtension ResourceKey="CardGroupStyle" /> </ListView.GroupStyle> <ListView.View> <GridView> <GridViewColumn> <Image Height="50" Width="40"></Image> </GridViewColumn> </GridView> </ListView.View> </ListView> </StackPanel> <Window.Resources> <CollectionViewSource x:Key="PlayerCardsGroups" Source="{Binding Path=PlayerCardImages}"> </CollectionViewSource> <GroupStyle x:Key="CardGroupStyle"> <GroupStyle.HeaderTemplate> <DataTemplate> <TextBlock x:Name="txt" Background="{StaticResource Brush_HeaderBackground}" FontWeight="Bold" Foreground="White" Margin="1" Padding="4,2,0,2" Text="Cards" /> </DataTemplate> </GroupStyle.HeaderTemplate> </GroupStyle> <Style x:Key="CardItemStyle" TargetType="{x:Type ListViewItem}"> <!-- Stretch the content of each cell so that we can right-align text in the Total Sales column. --> <Setter Property="HorizontalContentAlignment" Value="Stretch" /> <!-- Bind the IsSelected property of a ListViewItem to the IsSelected property of a CustomerViewModel object. --> <Style.Triggers> <MultiTrigger> <MultiTrigger.Conditions> <Condition Property="ItemsControl.AlternationIndex" Value="1" /> <Condition Property="IsSelected" Value="False" /> <Condition Property="IsMouseOver" Value="False" /> </MultiTrigger.Conditions> <Setter Property="Background" Value="#EEEEEEEE" /> </MultiTrigger> </Style.Triggers> </Style> </Window.Resources> 

this code definitely goes through data binding - my breakpoint gets to the beginning of the program and whenever elements are added to the collection. But images are not displayed. Instead of trying to use more XAML, which doesn't work, maybe I could ask someone to give me code / examples / documents that show how to link the list of images to a ListView (or another control, if you really feel that the ListView is unacceptable). Please note that my collection is the material to which I attach. I notice that with many examples, they are related to subtasking. That is, they can have a collection of albums, and for each album they are attached to the image of the property (see Displaying elements as images in the WPF ListView ).

Any ideas or help would be greatly appreciated.

-Dave

Additional Information.

Based on Clemens' suggestions, I now have this code for PlayerCardImages:

 public ObservableCollection<ImageSource> PlayerCardImages { get { var results = new ObservableCollection<ImageSource>(); //if (PlayerCards.Count == 0) // return results; //else //{ // results.Add(new BitmapImage(new Uri(@"Images\\" + "diamond-1.png", UriKind.Relative))); //} foreach (var card in PlayerCards) { results.Add(new BitmapImage(new Uri(@"Images\\" + GetCardFileName(card), UriKind.Relative))); } return results; } 

I used the exact XAML that he suggested. It almost works. I say "almost" because I noticed strange behavior that sometimes showed one card and sometimes not (I never had 2 cards). All the maps from the files and the bindings seem to work, and I kept track of what, in my opinion, is the key for the last remaining error (and this is BIZARRE). If in the debugger I check the results and then open the results [0] in the debugger, I get this card! I really have to open [0] (you see information about height, width, etc.) for this to work. Also, if I open [1], I get a card instead. Why does opening debugging information have any effect? For those of you who may ask what happens if you open both cards in the debugger ... this will not work. I get an exception from synchronization. I will say that perhaps my image files are large. 10 Kbytes to 30 Kbytes. This is problem? I guess not, and that this is a subtle issue with reading in images or snapping. What's happening? Thanks Dave

+7
source share
2 answers

First, you should not use Image controls in your ViewModel. You already have an Image control in the DateTemplate of your view. You want to bind the Source property of this conntrol image, and the source property of this binding cannot be another image.

Instead, your ViewModel will use ImageSource (or a derived class, such as BitmapImage ) as the image type, for example:

 public ObservableCollection<ImageSource> PlayerCardImages { get { var results = new ObservableCollection<ImageSource>(); foreach (var card in PlayerCards) { results.Add(new BitmapImage(new Uri(card.ImageUrl))); } return results; } } 

Or just a URI or image path, as there is an automatic conversion from string to ImageSource built into WPF:

 public ObservableCollection<string> PlayerCardImages { get { var results = new ObservableCollection<string>(); foreach (var card in PlayerCards) { results.Add(card.ImageUrl); } return results; } } 

Now you bind your Listbox ItemsSource property to the PlayerCardGames collection, and in the DataTemplate you bind directly to the collection item. The ListView DataContext must be set to an instance of the object that defines the PlayerCardGames property.

 <ListView ItemsSource="{Binding PlayerCardGames}"> <ListView.ItemTemplate> <DataTemplate> <StackPanel> <Image Source="{Binding}" /> </StackPanel> </DataTemplate> </ListView.ItemTemplate> </ListView> 

UPDATE. Since there seems to be a problem loading image files, you can try the following method. It downloads images synchronously, and you can go through the debugger.

 public static ImageSource LoadImage(string fileName) { var image = new BitmapImage(); using (var stream = new FileStream(fileName, FileMode.Open)) { image.BeginInit(); image.CacheOption = BitmapCacheOption.OnLoad; image.StreamSource = stream; image.EndInit(); } return image; } 

You can use this method in your PlayerCardGames property PlayerCardGames as follows:

 foreach (var card in PlayerCards) { results.Add(LoadImage(@"Images\\" + GetCardFileName(card))); } 
+10
source

I really have not tried to reproduce your problem, but I will, if that does not solve it:

In your first xaml block, I think you messed up the bindings. This will be the way I expect ItemsSource for the ObservableCollection of Images, Image source for the image.

 <Image Source="{Binding}" ... /> <ListView Name="lvwTitles" ItemsSource="{Binding PlayerCardImages}" ... /> 

In your second block, you did not specify a Source binding at all:

 <Image Source="{Binding}" Height="50" Width="40" /> 
0
source

All Articles