Как связать несколько цветов, чтобы заполнить прямоугольник в WPF?

1

Я привязываю 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>

Я изначально вставлял форму прямоугольника в 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;
}
Теги:
xaml
wpf

2 ответа

1
Лучший ответ

Фон, который вы хотите для каждого миниатюры, на самом деле выглядит как вертикальный LinearGradientBrush поэтому лучшим выбором здесь является LinearGradientBrush, а не DrawingBrush который слишком сложный (и излишний) для этого сценария. Вам понадобится свойство Colors (с реализованным INotifyPropertyChanged), в котором находится диапазон цветов. Чтобы преобразовать это в LinearGradientBrush, вам нужен Converter. Я понимаю, что все цветовые полосы должны иметь равную высоту (и полную ширину). К счастью, Offset в LinearGradientBrush относительное (по некоторому отношению) к всей высоте, поэтому нам не нужна информация о фактической высоте соответствующего прямоугольника. Вот класс конвертера:

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<Color> (ваше исходное свойство имеет тип List<SolidColorBrush>).

Определите свой конвертер как некоторый ресурс в своем 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 который изменяет весь контейнер элементов. Если это то, что вы хотите, просто укажите эту панель как обычно, но помните, что она просто связана с тем, как упорядочивать и компоновать элементы. Это означает, что мы не можем привязывать Background для каждого элемента, основываясь только на этом.

Обновление: я предположил, что вы правильно 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");
       }
    }
  }
}
  • 0
    Спасибо за это и извините за задержку. Я пробую ваше решение, но позиция Rectangle выдает ошибку. «Свойство ItemsTemplate не поддерживает значение типа« прямоугольник ». Мне это кажется странным.
  • 0
    @ Да, это моя глупая ошибка, пожалуйста, посмотрите обновленный код.
Показать ещё 11 комментариев
0

Насколько я знаю, самым простым способом было бы использовать DrawingBrush вместо SolidColorBrush.

Ваш DrawingBrush будет описывать шаблон, который вы используете для заполнения Rectangle (в вашем случае вы будете использовать ваши три кисти SolidColor для рисования содержимого DrawingBrush). Свойство Fill вашего прямоугольника должно быть привязано к DrawingBrush.

См. Описание того, как использовать DrawingBrush как Rectangle здесь.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню