Отображение изображений в ListView (или что-то лучше!) в WPF MVVM с использованием привязки данных

Я пытаюсь отобразить некоторые изображения в ListView и не удалось. Я использую WPF MVVM, а ListView-это пережиток от простого отображения костюмов и данных ранга. (См. мой предыдущий пост: MVVM в WPF-как предупредить ViewModel об изменениях в модели... или должен? если вы заинтересованы!) То есть я мог бы использовать что-то другое, кроме ListView (если это совет), но я все равно хотел бы знать, как это сделать с ListView, предполагая, что это выполнимо. Моя собственность я привязка к в 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;
    }
}

в своем XAML код, который я использую:

<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>

эту идею бесстыдно украли у:WPF-привязка изображений по горизонтали к ListView однако, он даже не кажется databind, о чем свидетельствует моя точка останова в PlayerCardImages не попадает.

Я также попробовал следующий XAML с несколько большей удачей:

 <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>

этот код определенно проходит через databinding-my точка останова в начале программы и каждый раз, когда элементы добавляются в коллекцию. Но изображения не отображаются. Вместо того, чтобы мучить вас больше XAML, который не работает, возможно, я мог бы попросить кого-то указать мне на некоторые код/примеры/документы, которые показывают, как связать список изображений с ListView (или другим элементом управления, если вы действительно считаете, что ListView неуместен). Обратите внимание, что моя коллекция-это то, к чему я привязан. Я замечаю, что со многими примерами они привязаны к подсвойство. Т. е. у них может быть коллекция альбомов, и для каждого альбома они привязываются к его свойству image (см.:отображение элементов в виде изображений в WPF ListView).

любые идеи или помощь будет высоко ценится.

-Дэйв

дополнительная информация.

основываясь на предложениях Клеменса, у меня теперь есть этот код для 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;

        }

я использовал точный XAML, который он предложил. Это почти работает. Я говорю "почти", потому что я заметил странное поведение, при котором иногда показывалась 1 карта, а иногда нет (я никогда не получал 2 карты). Все получение карт из файлов и привязки, похоже, работает, и я отследил то, что я думаю, является ключом к последней оставшейся ошибке (и это странно). Если в отладчике я изучаю результаты и далее открываю результаты[0] в отладчике, я получаю эту карту! Мне действительно нужно открыть [0] (вы видите информацию о высоте, ширине и т. д.) чтобы это сработало. Кроме того, если я откройте [1], вместо этого я получу эту карту. Почему открытие информации об отладчике будет иметь какой-либо эффект? Для тех из вас, кто может спросить: что случится, если вы откроете обе карты в отладчике... это не работает. Я получаю исключение тайм-аута операции. Я скажу, что, возможно, мои файлы изображений большие. 10Kbytes до 30 Kbytes. В этом проблема? Я предполагаю, что нет, и что это тонкая проблема с чтением изображений или привязки. Что происходит? Спасибо, Дэйв

2 ответов


во-первых, вы не должны использовать элементы управления изображением в ViewModel. У вас уже есть элемент управления Image в DateTemplate вашего представления. Вы хотите связать Source свойство этого изображения conntrol, и свойство источника этой привязки не может быть другим изображением.

вместо этого ваш ViewModel будет либо использовать ImageSource (или производный класс, например BitmapImage) как тип изображения, как это:

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;
    }
}

или просто URIs изображения или пути, как существует автоматическое преобразование из string to ImageSource встроенный в WPF:

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

теперь вы свяжетеItemsSource свойства PlayerCardGames коллекция, и в DataTemplate вы привязываете непосредственно к элементу коллекции. DataContext ListView должен быть установлен в экземпляр объекта, который определяет PlayerCardGames собственность.

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

UPDATE: поскольку возникает проблема с загрузкой файлов изображений, вы можете попробовать следующее метод. Он загружает изображения синхронно, и вы можете пройти через отладчик.

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;
}

вы можете использовать этот метод в вашей PlayerCardGames свойство getter, как это:

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

Я действительно не пытался воспроизвести вашу проблему, но я буду, если это не решит ее:

в вашем первом блоке xaml я думаю, что вы перепутали привязки. Это было бы так, как я ожидаю, ItemsSource для ObservableCollection изображений, Источник Изображения для изображения.

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

во втором блоке Вы вообще опустили исходную привязку:

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