C# 使用数据绑定在 WPF MVVM 中的 ListView(或更好的东西!)中显示图像

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15594289/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 17:18:55  来源:igfitidea点击:

Displaying Images in ListView (or something better!) in WPF MVVM using databinding

c#wpfimagelistviewmvvm

提问by Dave

I am trying to display some images in a ListView and have not been successful. I am using WPF MVVM and the ListView is a holdover from simply displaying suits and rank data. (See my previous post: MVVM in WPF - How to alert ViewModel of changes in Model... or should I?if you are interested!) That is, I could use something other than ListView (if that is the advice) but I would still like to know how to do it with ListView, assuming it's doable. My property I'm binding to in the ViewModel is:

我试图在 ListView 中显示一些图像但没有成功。我正在使用 WPF MVVM 并且 ListView 是简单地显示西装和排名数据的保留。(请参阅我之前的帖子:WPF 中的 MVVM - 如何提醒 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;
    }
}

In my XAMLcode I'm using:

在我的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>

This idea was shamelessly stolen from: WPF - bind images horizontally to ListViewHowever, it doesn't even appear to databind as evidenced by my breakpoint in PlayerCardImages not being hit.

这个想法是从以下内容中无耻地窃取的:WPF - 将图像水平绑定到 ListView然而,它甚至似乎没有数据绑定,正如我在 PlayerCardImages 中的断点没有被击中所证明的那样。

I also tried the following XAML with somewhat better luck:

我还尝试了以下 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>

this code definitely goes through databinding - my breakpoint is hit at the beginning of the program and whenever items are added to the collection. But no images is displayed. Rather than torture you with more XAML that does not work, perhaps I could ask someone to point me to some code/examples/docs that show how to bind a list of Images to a ListView (or another control if you really feel that ListView is inappropriate). Notice that my collection is the stuff I'm binding to. I notice that with many examples, they are binding to a subproperty. I.e. they may have a collection of albums and for each album they bind to it's property image (see: Showing items as images in a WPF ListView).

这段代码肯定会通过数据绑定——我的断点在程序开始时以及每当项目添加到集合时被击中。但是不显示图像。与其用更多不起作用的 XAML 折磨你,也许我可以请人给我指点一些代码/示例/文档,这些代码/示例/文档展示了如何将图像列表绑定到 ListView(或其他控件,如果你真的觉得 ListView 是不当)。请注意,我的收藏是我要绑定的东西。我注意到在许多示例中,它们绑定到子属性。即他们可能有一个相册集合,并且对于每个相册,他们绑定到它的属性图像(请参阅:在 WPF ListView 中将项目显示为图像)。

Any ideas or help would be much appreciated.

任何想法或帮助将不胜感激。

-Dave

-戴夫

Additional info.

附加信息。

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

根据克莱门斯的建议,我现在有了 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 he suggested. It almost works. I say "almost" because I noticed strange behavior whereby sometimes 1 card would show and sometimes not (I never got 2 cards). All the getting cards from files and binding seems to be working and I tracked down what I think is key to the last remaining bug (and it's BIZARRE). If in the debugger, I examine results, and further open up results[0] in the debugger, I get that card displayed! I actually have to open up [0] (you see info about height, width, etc.) for this to work. Furthermore if I open up [1], I get that card displayed instead. Why would opening up the debugger info have any effect? For those of you who might ask, what happens if you open up both cards in the debugger... that doesn't work. I get a operation timed out exception. I will say that perhaps my image files are big. 10Kbytes to 30 Kbytes. Is that the problem? I'm guessing not, and that it's a subtle problem with reading in the images or binding. What is going on? Thanks, Dave

我使用了他建议的确切 XAML。它几乎有效。我说“几乎”是因为我注意到奇怪的行为,有时一张牌会显示,有时不会(我从来没有得到 2 张牌)。所有从文件和绑定中获取卡片似乎都在工作,我找到了我认为是最后一个剩余错误的关键(它是 BIZARRE)。如果在调试器中检查结果,并在调试器中进一步打开 results[0],我会显示该卡片!实际上,我必须打开 [0](您会看到有关高度、宽度等的信息)才能使其正常工作。此外,如果我打开 [1],则会显示该卡片。为什么打开调试器信息会有任何影响?对于那些可能会问的人,如果在调试器中打开两张卡会发生什么……那不起作用。我收到一个操作超时异常。我会说也许我的图像文件很大。10 KB 到 30 KB。这是问题吗?我猜不是,这是阅读图像或装订的一个微妙问题。到底是怎么回事?谢谢,戴夫

采纳答案by Clemens

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 Sourceproperty of this Image conntrol, and the source property of this binding can't be another Image.

首先,您不应在 ViewModel 中使用 Image 控件。您已经在视图的 DateTemplate 中有一个 Image 控件。你要绑定Source这个Image控件的属性,这个绑定的source属性不能是另一个Image。

Instead your ViewModel would either use ImageSource(or a derived class like BitmapImage) as image type, like this:

相反,您的 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;
    }
}

Or simply the image URIs or paths, as there is an automatic conversion from stringto ImageSourcebuilt into WPF:

或者只是图像 URI 或路径,因为WPF 中内置了从string到的自动转换ImageSource

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

You would now bind your Listbox's ItemsSourceproperty to the PlayerCardGamescollection, and in the DataTemplate you bind directly to the collection item. The ListView's DataContext has to be set to an instance of the object that defines the PlayerCardGamesproperty.

您现在将 Listbox 的ItemsSource属性绑定到PlayerCardGames集合,并在 DataTemplate 中直接绑定到集合项。ListView 的 DataContext 必须设置为定义PlayerCardGames属性的对象的实例。

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


UPDATE: As there seems to be a problem with loading the image files, you may try the following method. It loads images synchronously and you are able to step through with 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 PlayerCardGamesproperty getter like this:

您可以在您的PlayerCardGames属性 getter 中使用此方法,如下所示:

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

回答by Mike Fuchs

I didn't really try to reproduce your problem, but I will if this is not solving it:

我并没有真正尝试重现您的问题,但是如果这不能解决问题,我会这样做:

In your first xaml block, I think you mixed up the bindings. This would be how I expect it, ItemsSource to the ObservableCollection of Images, Image source to the Image.

在您的第一个 xaml 块中,我认为您混淆了绑定。这就是我所期望的,ItemsSource 到 ObservableCollection of Images,Image source 到 Image。

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

In your second block, you omitted the Source binding altogether:

在您的第二个块中,您完全省略了 Source 绑定:

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