使用数据绑定在 WPF MVVM 中的 ListView(或更好的东西!)中显示图像 [英] Displaying Images in ListView (or something better!) in WPF MVVM using databinding

查看:23
本文介绍了使用数据绑定在 WPF MVVM 中的 ListView(或更好的东西!)中显示图像的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在 ListView 中显示一些图像,但没有成功.我正在使用 WPF MVVM 并且 ListView 是简单地显示西装和排名数据的保留.(见我之前的帖子:WPF 中的 MVVM - 如何提醒 ViewModel 模型中的更改...或者我应该吗? 如果您有兴趣!)也就是说,我可以使用 ListView 以外的其他东西(如果这是建议)但我仍然想知道如何用 ListView 做到这一点,假设它是可行的.我在 ViewModel 中绑定的属性是:

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:

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 代码中:

In my XAML code I'm using:

<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 但是,我在 PlayerCardImages 中的断点没有被击中就证明了它甚至没有进行数据绑定.

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

我还尝试了以下 XAML,但运气好一些:

I also tried the following XAML with somewhat 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>

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

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

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

Any ideas or help would be much appreciated.

-戴夫

其他信息.

根据 Clemens 的建议,我现在有了 PlayerCardImages 的代码:

Based on suggestions by Clemens, 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;

        }

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

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

推荐答案

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

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

相反,您的 ViewModel 将使用 ImageSource(或像 BitmapImage) 作为图像类型,像这样:

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

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

或者只是图像 URI 或路径,因为 WPF 内置了从 stringImageSource 的自动转换:

Or simply the image URIs or paths, 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;
    }
}

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

You would now bind your Listbox's ItemsSource property to the PlayerCardGames collection, 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 PlayerCardGames property.

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

您可以像这样在 PlayerCardGames 属性 getter 中使用此方法:

You can use this method in your PlayerCardGames property getter like this:

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

这篇关于使用数据绑定在 WPF MVVM 中的 ListView(或更好的东西!)中显示图像的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆