Как условно изменить цвет переднего плана кнопки в WPF?

#c# #wpf #xaml #data-binding

#c# #wpf #xaml #привязка к данным

Вопрос:

У меня есть кнопка внутри ItemsControl (элемент управления видом разбивки на страницы) У меня есть свойство, названное current_page в моей модели представления.

Я хочу сравнить current_page значение с Content Button тем, что есть (1 / 2 / 3 ) и хотите изменить цвет переднего плана кнопки.

Пожалуйста, взгляните на код.

Моя ViewModel

 public PaginationModel pagination
{
  get { return _pagination; }
  set { _pagination = value; OnPropertyChanged("pagination"); }
}
  

Моя модель разбивки на страницы

 public class PaginationModel: INotifyPropertyChanged {
  private int _total_items;
  public int total_items {
    get {
      return _total_items;
    }
    set {
      _total_items = value;
      OnPropertyChanged("total_items");
    }
  }

  private int _items_per_page;
  public int items_per_page {
    get {
      return _items_per_page;
    }
    set {
      _items_per_page = value;
      OnPropertyChanged("items_per_page");
    }
  }

  private int _current_page;
  public int current_page {
    get {
      return _current_page;
    }
    set {
      if (value <= total_pages   1 amp;amp; value > 0) {
        _current_page = value;
        OnPropertyChanged("current_page");
      }
    }
  }

  private int _total_pages;
  public int total_pages {
    get {
      return _total_pages;
    }
    set {
      _total_pages = value;
      OnPropertyChanged("total_pages");
    }
  }

  private ObservableCollection < string > _PageList;

  public ObservableCollection < string > PageList {
    get {
      _PageList = new ObservableCollection < string > ();
      for (int i = 0; i < total_pages; i  ) {
        _PageList.Add((i   1).ToString());
      }
      return _PageList;
    }
    set {
      _PageList = value;
      OnPropertyChanged("PageList");
    }
  }
}
  

Мой макет

 <ItemsControl ItemsSource="{Binding pagination.PageList}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal" Margin="0">
                <Button Content="{Binding}"  CommandParameter="{Binding RelativeSource={RelativeSource Self}}"
                                             Command="{Binding DataContext.ChangePage, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"
                                             Width="20"
                                             Margin="10,0"></Button>
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>
  

Шаблон стиля кнопки

 <Style TargetType="{x:Type Button}" >
    <Setter Property="Foreground" Value="{StaticResource WordOrangeBrush}"  />
    <Setter Property="Background" Value="Transparent"></Setter>
    <Setter Property="BorderThickness" Value="0"></Setter>
    <Setter Property="FontFamily" Value="{StaticResource HelveticaNeue}"></Setter>
    <Setter Property="FontSize" Value="14"></Setter>
    <Setter Property="Foreground" Value="#ff9f00"></Setter>
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type Button}">
                <Border Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}">
                    <ContentPresenter VerticalAlignment="Center" HorizontalAlignment="Center" />
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
    <Style.Triggers>
        <Trigger Property="IsMouseOver" Value="True">
            <Setter Property="Background" Value="Transparent"></Setter>
            <Setter Property="Foreground" Value="#ff9f00" />
        </Trigger>
        <Trigger Property="IsMouseOver" Value="False">
            <Setter Property="Background" Value="Transparent"></Setter>
            <Setter Property="Foreground" Value="White" />
        </Trigger>
        <!--I want to bind current page value in place of 1-->
        <DataTrigger Binding="{Binding}" Value="1">
            <Setter Property="Foreground" Value="Red"/>
        </DataTrigger>
    </Style.Triggers>
</Style>
  

Комментарии:

1. Поскольку триггер не работает при запуске свойств Button , DataTrigger он не должен быть частью кнопки ControlTemplate , которая является «внутренним» контекстом управления, независимым от любого типа данных. Поскольку триггер срабатывает при свойствах текущей кнопки DataContext , DataTrigger он должен находиться за пределами Button , где он может получить доступ к DataContext подобному в Style таргетинге Button или, может быть, даже лучше, в DataTemplate шаблоне элемента. Здесь вы находитесь в контексте, который специализируется на конкретном DataContext , т.Е. типе данных.

2. Вы могли бы сделать так, чтобы listbox, а не itemscontrol, привязывал Foreground к свойству selecteditem . Сделайте PageList списком viewmodels, а не просто строкой. Добавьте к этому свойство кисти. Или используйте конвертер, чтобы преобразовать число в кисть. С запасным значением WordOrangeBush.

Ответ №1:

Решение для преобразования значений

Вы не можете привязать свойство Value a DataTrigger к свойству, потому что оно не является свойством зависимости. Вы можете обойти это, создав пользовательский многозначный конвертер, который проверяет страницы на равенство.

 public class PageEqualityToBooleanConverter : IMultiValueConverter
{
   public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
   {
      return (int)values[0] == System.Convert.ToInt32(values[1]);
   }

   public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
   {
      throw new InvalidOperationException();
   }
}
  

Первое значение , передаваемое конвертеру , — это текущая страница as int , а второе значение — страница на Button as string . Немного странно, что ваш current_page имеет тип int , но PageList это набор типов string , поэтому нам нужно преобразовать второе значение.

Создайте экземпляр конвертера в своих ресурсах перед вашим стилем, чтобы вы могли ссылаться на него.

 <local:PageEqualityToBooleanConverter x:Key="PageEqualityToBooleanConverter"/>
  

Наконец, замените свой DataTrigger на приведенный ниже. Мы используем a MultiBinding , чтобы иметь возможность привязывать более одного значения. Конвертер преобразует оба значения в True , если значения страницы совпадают, в противном False случае .

 <DataTrigger Value="True">
   <DataTrigger.Binding>
      <MultiBinding Converter="{StaticResource PageEqualityToBooleanConverter}">
         <Binding Path="DataContext.pagination.current_page" RelativeSource="{RelativeSource AncestorType={x:Type ItemsControl}}"/>
         <Binding/>
      </MultiBinding>
   </DataTrigger.Binding>
   <Setter Property="Foreground" Value="Red"/>
</DataTrigger>
  

Первая привязка найдет родительский ItemsControl элемент для доступа к модели представления, которая содержит current_page . Вторая привязка будет привязана к контексту данных соответствующей кнопки, которая является строкой страницы. Если вы измените PageList тип элемента на int это, это все равно будет работать.

Рекомендации

Я хочу указать на некоторые замечания по вашему коду, которые могут помочь вам улучшить его.

  • Как я уже упоминал, кажется странным, что тип элемента вашей коллекции отличается от вашего текущего типа свойства элемента. Рассмотрите возможность создания int или создания отдельной модели представления элемента страницы, если вы планируете использовать в ней другие свойства, кроме номера страницы.
  • Вы определяете неявный стиль для Button , который применяется ко всем Button элементам управления в области видимости. Если вы используете его только в своем элементе управления нумерацией страниц, это может быть нормально, но если вы собираетесь использовать его в других местах, DataTrigger его не следует включать в него, поскольку он специфичен только для этого контекста данных. Создайте отдельный стиль на основе этого. Рассмотрим комментарий @BionicCode по этому поводу.
  • Как указал @Andy в комментариях, a ListBox может лучше подойти для разбиения на страницы, потому что у него есть понятие a SelectedItem IsSelected свойства его контейнеров элементов, которые могут быть привязаны к a DataTrigger ), что вы и пытаетесь сделать здесь вручную.

Ответ №2:

Value Свойство a DataTrigger не может быть привязано к данным.

Что вам нужно сделать, это создать тип, представляющий a Page , добавить Number IsCurrentPage к нему свойство a и и изменить тип PageList на an ObservableCollection<Page> вместо an ObservableCollection<string> .

Затем вы могли бы просто найти соответствующий Page в PageList и установить его IsCurrentPage свойство из модели представления всякий current_page раз, когда свойство установлено.

Вы также должны убедиться, что получатель PageList свойства не создает новую коллекцию при каждом ее вызове.