【问题标题】:How do you bind multiple colors to fill a rectangle in WPF?如何绑定多种颜色以填充 WPF 中的矩形?
【发布时间】:2014-11-10 02:31:31
【问题描述】:

我将 ItemsControl 绑定到名为 SwatchThumbnails 的可观察集合,该集合将包含数据以生成矩形。矩形可以有 1-3 种颜色与之关联,并且在渲染时需要显示那么多颜色。我想我知道如何将一种颜色绑定到填充上,但我需要一种方法来绑定最多三种颜色并让它们均匀显示。如何做到这一点?

这是我的 XAML:

<ScrollViewer x:Name="sv_Thumbnails" Grid.ColumnSpan="2" Grid.Row="1" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Auto">
    <ItemsControl x:Name="ug_Thumbnails" ItemsSource="{Binding SwatchThumbnails, ElementName=mainWindow}">
         <ItemsControl.ItemsPanel >
             <ItemsPanelTemplate>
                <UniformGrid Columns="6" RenderTransformOrigin="0,0.5" Cursor="Hand">
                  <Rectangle Width="50" Height="50" Fill="{Binding Color}" Margin="0 0 5 0" />
                </UniformGrid>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</ScrollViewer>

我最初是通过 C# 代码将矩形形状插入到 UniformGrid 中,所以我有为分割颜色而编写的原始代码。基本上,这是我生成矩形的原始代码:

        System.Windows.Shapes.Rectangle swatch = new System.Windows.Shapes.Rectangle();
        swatch.Width = 50;
        swatch.Height = 50;
        swatch.Margin = new Thickness(0, 5, 5, 0);
        swatch.StrokeThickness = 1;
        swatch.Stroke = System.Windows.Media.Brushes.Gray;
        swatch.Name = "s_" + _name.ToString();
        double groupsize = 100 / _colors.Count();
        DrawingBrush blackBrush = new DrawingBrush();
        DrawingGroup checkersDrawingGroup = new DrawingGroup();
        List<SolidColorBrush> brushes = _colors;
        double location = 0;
        for (int i = 0; i < _colors.Count(); i++)
        {
            GeometryDrawing drawing = new GeometryDrawing(brushes[i], null,
                new RectangleGeometry(new Rect(0, location, groupsize, groupsize)));
            checkersDrawingGroup.Children.Add(drawing);
            location += groupsize;
        }
        blackBrush.Drawing = checkersDrawingGroup;
        swatch.Fill = blackBrush;
        swatch.MouseUp += new MouseButtonEventHandler(loadSwatchResources);

最后,这是我的可观察集合和样本缩略图定义。

    private ObservableCollection<SwatchThumbnail> swatchThumbnails = new ObservableCollection<SwatchThumbnail>();
    public ObservableCollection<SwatchThumbnail> SwatchThumbnails 
    {
        get { return swatchThumbnails; }
        set { swatchThumbnails = value; }
    }

public class SwatchThumbnail : INotifyPropertyChanged
{
    public string name { get; set; }
    public List<Color> colors { get; set; }
    public bool selected { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;
}

【问题讨论】:

    标签: c# wpf xaml


    【解决方案1】:

    您想要的每个缩略图的背景实际上看起来像一个垂直的LinearGradientBrush,所以这里的最佳选择是LinearGradientBrush,而不是DrawingBrush,这对于这种情况来说太复杂(而且过度杀伤)。您需要一些 Colors 属性(实现了 INotifyPropertyChanged)来保存颜色范围。要将其转换为LinearGradientBrush,您需要Converter。我知道所有色带都应该具有相同的高度(和全宽)。幸运的是,LinearGradientBrush 中的Offset 是相对于整个高度的(按某种比例),因此我们不需要有关相关矩形的实际高度的任何信息。这是转换器类:

    public class ColorsToLinearGradientBrushConverter : IValueConverter {
       public object Convert(object value, Type targetType, object parameter,
                                                            CultureInfo culture){
          var colors = (List<Color>) value;
          var brush = new LinearGradientBrush(){ StartPoint = new Point(),
                                                 EndPoint = new Point(0,1)};
          var w = 1d / colors.Count;
          for(var i = 0; i < colors.Count - 1; i++){
             var offset = w * (i+1);
             var stop1 = new GradientStop(colors[i], offset);
             var stop2 = new GradientStop(colors[i+1], offset);
             brush.GradientStops.Add(stop1);
             brush.GradientStops.Add(stop2);
          }
          return brush;
       }
       //this way back is not needed (bind OneWay)
       public object ConvertBack(object value Type targetType, object parameter,
                                                               CultureInfo culture){
          throw new NotImplementedException();
       }
    }
    

    请注意,Colors 属性的类型应为 List&lt;Color&gt;(您的原始属性的类型为 List&lt;SolidColorBrush&gt;)。

    将您的转换器定义为 XAML 中的一些资源,并将其正常用于绑定(我希望您知道这一点)。这是 XAML 代码:

    <ScrollViewer x:Name="sv_Thumbnails" Grid.ColumnSpan="2" Grid.Row="1"
        VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Auto">
       <ScrollViewer.Resources>
         <local:ColorsToLinearGradientBrushConverter x:Key="brushConverter"/>
       </ScrollViewer.Resources>
       <ItemsControl x:Name="ug_Thumbnails" ItemsSource="{Binding SwatchThumbnails, 
                                                          ElementName=mainWindow}">
         <!--ItemsControl.ItemsPanel>
             <ItemsPanelTemplate>
                <UniformGrid Columns="6" RenderTransformOrigin="0,0.5" Cursor="Hand">
                </UniformGrid>
            </ItemsPanelTemplate>
         </ItemsControl.ItemsPanel-->
    
         <ItemsControl.ItemTemplate>
            <DataTemplate>
              <Rectangle Width="50" Height="50" Fill="{Binding Colors,
                                         Converter={StaticResource brushConverter}}" 
                             Margin="0 0 5 0" />
            </DataTemplate>
         </ItemsControl.ItemTemplate>
       </ItemsControl>
    </ScrollViewer>
    

    请注意,您必须在此处使用一些 ItemTemplate,您的原始 XAML 只有 ItemsPanel,它会更改项目的整个容器。如果这是您想要的,只需正常指定该面板,但请记住它仅与 如何排列和布局 项目有关。这意味着我们不能仅基于此为每个项目绑定背景。

    更新:我假设你正确地实现了SwatchThumbnail。但看起来你没有。因此,请尝试以下实现,看看它是否有效:

    public class SwatchThumbnail : INotifyPropertyChanged {
      public event PropertyChangedEventHandler PropertyChanged;
      protected void OnPropertyChanged(string prop){
        var handler = PropertyChanged;
        if(handler != null) handler(this, new PropertyChangedEventArgs(prop));
      }
      string _name;
      public string name {
        get { return _name;}
        set { 
           if(_name != value) {
              _name = value;
              OnPropertyChanged("name");
           }
        }
      }
      List<SolidColorBrush> _colors;
      public List<SolidColorBrush> colors {
        get {
           return _colors;
        }
        set {
           _colors = value;
           OnPropertyChanged("colors");
        }
      }
      bool _selected;
      public bool selected {
        get {
           return selected;
        }
        set {
           if(_selected != value){
              _selected = value;
              OnPropertyChanged("selected");
           }
        }
      }
    }
    

    【讨论】:

    • 感谢您,并为我的延误感到抱歉。我正在尝试您的解决方案,但 Rectangle 行项目给了我一个错误。 “Property ItemsTemplate 不支持“矩形”类型的值。这对我来说真的很奇怪。
    • @Yecats 是的,这是我的愚蠢错误,请查看更新后的代码。
    • 谢谢它让我克服了错误,但我认为绑定有问题。我已经确认 observablecollection 中包含三个项目。但是,当我运行该程序时,什么都没有显示。转换器上的断点永远不会触发,这让我认为绑定可能已关闭?
    • @Yecats 你如何实现SwatchThumbnail 类?我的意思是你是否实现了INotifyPropertyChanged(或者至少让它从实现该接口的某个基类继承)?还可以尝试查看 Output 窗口,看看那里是否有任何错误通知。当然如果没有触发转换器,就不会渲染任何东西。
    • 好的,我不需要实现 INotifiyPropertyChanged。相反,我将 c# 更改为不设置 DataContext 而是设置 ItemSource 并开始工作。 :) 感谢您的帮助!
    【解决方案2】:

    据我所知,最简单的方法是使用DrawingBrush 而不是SolidColorBrush

    您的 DrawingBrush 将描述您用来填充 Rectangle 的图案(在您的情况下,您将使用三个 SolidColor 画笔来绘制 DrawingBrush 的内容)。 然后应该将矩形的 Fill 属性绑定到 DrawingBrush。

    请参阅有关如何使用 DrawingBrush 作为矩形填充的说明here

    【讨论】:

      猜你喜欢
      • 2012-07-03
      • 1970-01-01
      • 2017-04-27
      • 2012-08-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多