简体   繁体   English

如何在 WPF 中有条件地更改按钮前景色?

[英]How to Change Button Foreground Color Conditionally in WPF?

I have a button inside ItemsControl (pagination kind control) I have a property named current_page in my view model.我在ItemsControl (分页类型控件)中有一个按钮,我的视图模型中有一个名为current_page的属性。

I want to compare current_page value to Content of Button that is (1 / 2 / 3) and want to change the foreground color of a button.我想将current_page值与 (1 / 2 / 3) 的Button Content进行比较,并想更改按钮的前景色。

Please have a look at the code.请看一下代码。

My ViewModel我的视图模型

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

My Pagination Model我的分页模型

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 && 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");
    }
  }
}

My Layout我的布局

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

Button Style Template按钮样式模板

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

A Value Converter Solution价值转换器解决方案

You cannot bind the Value of a DataTrigger to a property, because it is not a dependency property.您不能将DataTriggerValue绑定到属性,因为它不是依赖属性。 You can work around this by creating a custom multi-value converter that checks pages for equality.您可以通过创建一个用于检查页面是否相等的自定义多值转换器来解决此问题。

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

The first value passed to the converter is the current page as int and the second value is the page on the Button as string .传递给转换器的第一个值是作为int的当前页面,第二个值是作为stringButton上的页面。 It is a bit odd that your current_page is of type int , but the PageList is a collection of type string , that is why we need to convert the second value.您的current_pageint类型有点奇怪,但PageListstring类型的集合,这就是我们需要转换第二个值的原因。

Create an instance of the converter in your resources before your style, so you can reference it.样式之前在资源中创建转换器的实例,以便您可以引用它。

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

Finally, replace your DataTrigger with the one below.最后,用下面的替换你的DataTrigger We are using a MultiBinding to be able to bind more than one value.我们正在使用MultiBinding来绑定多个值。 The converter will convert both values to True , if the page values match, otherwise False .如果页面值匹配,转换器会将两个值都转换为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>

The first binding will find the parent ItemsControl to access the view model that contains the current_page .第一个绑定将找到父项ItemsControl以访问包含current_page的视图模型。 The second binding will bind to the data context of the associated button, which is the page string.第二个绑定将绑定到关联按钮的数据上下文,即页面字符串。 If you change the PageList item type to int this will still work.如果您将PageList项目类型更改为int这仍然有效。

Recommendations建议

I want to point out some observations on your code that might help you to improve it.我想指出一些对您的代码的观察,可能会帮助您改进它。

  • As I already mentioned, it seems odd that your collection item type differs from your current item property type.正如我已经提到的,您的集合项类型与您当前的项属性类型不同似乎很奇怪。 Consider making it int or creating a separate page item view model if you plan on having other properties than the page number in it.如果您计划在其中包含除页码以外的其他属性,请考虑将其int或创建单独的页面项视图模型。
  • You are defining an implicit style for Button , which gets applied to all Button controls in scope.您正在为Button定义隐式样式,该样式应用于范围内的所有Button控件。 If you use it in your pagination control only this might be ok, but if you intend to use it in other places, the DataTrigger should not be included in it, as it is specific to this data context only.如果您仅在分页控件中使用它,这可能没问题,但如果您打算在其他地方使用它, DataTrigger应将DataTrigger包含在其中,因为它仅特定于此数据上下文。 Create separate style based on this one.在此基础上创建单独的样式。 Consider @BionicCode's comment about this.考虑@BionicCode 对此的评论。
  • As @Andy pointed out in the comments, a ListBox could be a better fit for a paginator, because it has the notion of a SelectedItem (and IsSelected properties on its item containers that can be bound in a DataTrigger ), which is what you are trying to do here manually.正如@Andy 在评论中指出的那样, ListBox可能更适合分页器,因为它具有SelectedItem的概念(以及可以绑定在DataTrigger项目容器上的IsSelected属性),这就是您的想法试图在这里手动完成。

The Value property of a DataTrigger cannot be data-bound. DataTriggerValue属性不能是数据绑定的。

What you should do is to create a type that represents a Page , add a Number and IsCurrentPage property to it and change the type of PageList to be an ObservableCollection<Page> instead of an ObservableCollection<string> .你应该做的是创建一个代表一个类型Page ,一个加NumberIsCurrentPage属性并更改类型PageList是一个ObservableCollection<Page>代替ObservableCollection<string>

You could then simply look up the corresponding Page in the PageList and set its IsCurrentPage property from the view model whenever the current_page property is set.然后,您可以简单地查找相应的PagePageList并设置其IsCurrentPage每当从视图模型属性current_page属性设置。

You should also make sure that the getter of the PageList property doesn't create a new collection each time it's invoked.您还应该确保PageList属性的 getter 不会在每次调用时创建新集合。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM