【问题标题】:Images in a List in WPF MVVMWPF MVVM 列表中的图像
【发布时间】:2011-03-26 23:53:27
【问题描述】:

我有一个关于如何在 WPF MVVM 中最好地完成某事的问题。我的 ViewModel 中有一系列整数。为了举例,我们称它们为:

public int Yellow
{
    get;set;
}
public int Red
{
    get;set;
}
public int Green
{
    get;set;
}

我还有一些非常简单的小图像:红色圆圈、黄色圆圈和绿色圆圈。这个想法是在视图上有一个区域,其中包含许多这些图像,基于上述属性。因此,如果视图模型的这个实例有 3 个黄色、2 个红色和 1 个绿色,我想要在我的列表框中有 6 个图像,黄色圆圈的 3 个,红色的 2 个,绿色的 1 个。现在,我让它工作了,但是使用了一些非常笨拙的代码,我使用丑陋的 for 循环在 ViewModel 中构建图像列表。在 WPF 中是否有更优雅的方式来完成这项任务?理想情况下,我根本不想在 ViewModel 中引用图像...

【问题讨论】:

    标签: wpf image data-binding mvvm


    【解决方案1】:

    一种可能性是使用ValueConverter。它非常灵活、解耦并且有助于让 xaml 变得简单。这里是这样一个值转换器的代码:

    public class ImageCountValueConverter : IValueConverter{
        public string ImagePath {
            get;
            set;
        }
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
            if(null == value){
                return Enumerable.Empty<string>();
            } else if (value is int) {
                List<string> list = new List<string>();
                int v = (int)value;
                for (int i = 0; i < v; i++) {
                    if (parameter is string) {
                        list.Add((string)parameter);
                    } else {
                        list.Add(ImagePath);
                    }
                }
                return list;
            } else {
                Type t = value.GetType();
                throw new NotSupportedException("The \"" + t.Name+ "\" type is not supported");
            }
        }
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) {
            throw new NotImplementedException();
        }
    }
    

    标记看起来像这样:

    <StackPanel>
      <ItemsControl ItemsSource="{Binding Yellow,Converter={StaticResource ImageCount_ValueConverter},ConverterParameter=/image/yellow.png}" >
          <ItemsControl.ItemTemplate>
              <DataTemplate>
                  <Image Source="{Binding}" Stretch="None"/>
              </DataTemplate>
          </ItemsControl.ItemTemplate>
      </ItemsControl>
    
      <ItemsControl ItemsSource="{Binding Red,Converter={StaticResource ImageCount_ValueConverter},ConverterParameter=/image/red.png}" >
    
      ...
    

    声明类似于:

      <Window.Resources>
            <local:ImageCountValueConverter x:Key="ImageCount_ValueConverter" ImagePath="/image/sampleImage.png"/>            
        </Window.Resources>
    

    选项

    根据您的要求,您还可以扩展或更改它以使用 ImageSource 而不是字符串,甚至提供List&lt;Brush&gt; 作为输出,然后在通过绑定设置画笔的 DataTemplate 中使用形状。

    【讨论】:

      【解决方案2】:

      您可以使用ImageBrush 来平铺带有图像的矩形,并将矩形的宽度绑定到所需图像的副本数。像这样的:

      <StackPanel Orientation="Horizontal">
          <StackPanel.LayoutTransform>
              <ScaleTransform ScaleX="20" ScaleY="20"/>
          </StackPanel.LayoutTransform>
          <Rectangle Width="{Binding Yellow}" Height="1">
              <Rectangle.Fill>
                  <ImageBrush
                      ImageSource="Yellow.png"
                      Viewport="0,0,1,1"
                      ViewportUnits="Absolute"
                      TileMode="Tile"/>
              </Rectangle.Fill>
          </Rectangle>
          <Rectangle Width="{Binding Red}" Height="1">
              <Rectangle.Fill>
                  <ImageBrush
                      ImageSource="Red.png"
                      Viewport="0,0,1,1"
                      ViewportUnits="Absolute"
                      TileMode="Tile"/>
              </Rectangle.Fill>
          </Rectangle>
          <Rectangle Width="{Binding Green}" Height="1">
              <Rectangle.Fill>
                  <ImageBrush
                      ImageSource="Green.png"
                      Viewport="0,0,1,1"
                      ViewportUnits="Absolute"
                      TileMode="Tile"/>
              </Rectangle.Fill>
          </Rectangle>
      </StackPanel>
      

      更新:正如 Ray 在他的评论中指出的那样,如果您只是想绘制圆圈,那么使用 DrawingBrush 将获得比使用图像更好的缩放行为:

      <StackPanel Orientation="Horizontal">
          <StackPanel.LayoutTransform>
              <ScaleTransform ScaleX="20" ScaleY="20"/>
          </StackPanel.LayoutTransform>
          <StackPanel.Resources>
              <EllipseGeometry x:Key="Circle" RadiusX="1" RadiusY="1"/>
          </StackPanel.Resources>
          <Rectangle Width="{Binding Yellow}" Height="1">
              <Rectangle.Fill>
                  <DrawingBrush ViewportUnits="Absolute" TileMode="Tile">
                      <DrawingBrush.Drawing>
                          <GeometryDrawing
                              Brush="Yellow"
                              Geometry="{StaticResource Circle}"/>
                      </DrawingBrush.Drawing>
                  </DrawingBrush>
              </Rectangle.Fill>
          </Rectangle>
          <!-- etc. -->
      

      【讨论】:

      • 这看起来可行。还有其他解决方案吗?
      • Quartermeister 的解决方案与我的建议 (+1) 基本相同。还有其他解决方案,但没有一个如此干净、简单和优雅。你对他的回答不满意有什么原因吗?
      • 我确实想到了对 Quartermeister 解决方案的一种可能改进:如果您使用带有圆形绘图的 DrawingBrush 而不是带有 .png 文件的 ImageBrush,您将获得更好的缩放行为。 @Quartermeister:如果您愿意,欢迎您将其添加到您的答案中。
      • @Ray:好点子!他说他有图像,所以正在考虑图像文件,但如果它们真的只是圆圈,那么 DrawingBrush 会更简单。我会更新我的答案。
      • 我对答案的不满并没有像寻求进一步掌握 WPF 一样。在我的研究中,我遇到了几个可能有效的组件(ValueConverters、Triggers、Brushes 等),并且有点希望能得到一堆包含不同技术的不同示例。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-02
      • 2012-12-15
      • 2016-01-29
      • 1970-01-01
      相关资源
      最近更新 更多