WPF - 如何结合使用DataTrigger和Trigger?

58

注意:我已经提出了相关问题:如何结合DataTrigger和EventTrigger?

我有一个包含多个项目的列表框。每个项目的类都实现了INotifyPropertyChanged接口,并且具有一个IsAvailable属性。我使用该属性来表示列表中不可用的选项,使用不同的颜色。

但是,如果所选项目不可用,则前景颜色应为红色。

<ListBox>
  <ListBox.Resources>
    <DataTemplate DataType="{x:Type local:InstitutionViewModel}">
      <TextBlock Name="Name" Text="{Binding Name}"/>
      <DataTemplate.Triggers>
        <DataTrigger Binding="{Binding IsAvailable}" Value="False">
          <Setter TargetName="Name" Property="Foreground" Value="#888"/>
        </DataTrigger>
      </DataTemplate.Triggers>
    </DataTemplate>
  </ListBox.Resources>
</ListBox>

我使用上述数据触发器来将不可用的项目变灰。

我面临的问题是,所选项目与模板绑定的基础数据没有任何关系。我真正想要的是一种支持常规依赖属性Trigger ( ListBoxItem.IsSelected )和绑定数据项的DataTrigger的多触发器。

在不引入选择概念到我的视图模型的情况下,这可以完成吗?

对于任何想知道为什么我不禁用不可用项目的人,请理解这是应用程序的要求,因为可以选择不可用选项。实际上有几个列表框,其中一个的选择影响其他列表框中的可用内容。如果基于先前的选择禁用项目,则用户将无法更改主意或探索不同的组合。

3个回答

81

对于其他遇到此问题的人,我找到了适合我的解决方案。当然,我仍然感兴趣看到其他有趣的答案。

这是我的解决方案:

<MultiDataTrigger>
  <MultiDataTrigger.Conditions>
    <Condition Binding="{Binding
      RelativeSource={
        RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}},
        Path=IsSelected}" Value="True"/>
    <Condition Binding="{Binding IsAvailable}" Value="False"/>
  </MultiDataTrigger.Conditions>
  <Setter TargetName="Name" Property="Foreground" Value="#F00"/>
</MultiDataTrigger>

虽然这是一个多触发器,但这并没有什么特别之处。如果您只想在数据模板中以不同的样式呈现所选项目,则可以使用:

<DataTrigger Binding="{Binding 
  RelativeSource={
    RelativeSource Mode=FindAncestor, AncestorType={x:Type ListBoxItem}},
    Path=IsSelected}" Value="True">
  <Setter TargetName="Name" Property="Foreground" Value="#888"/>
</DataTrigger>

2
这正是我要推荐的。据我所知,这是最佳解决方案。 - Bryan Anderson
刚遇到了同样的问题。非常好的观点,伙计,我忘记了相对源绑定。 - EightyOne Unite

20

要将其与 DataGridRow 一起使用,请将绑定模式更改为 Self

Binding="{Binding RelativeSource={RelativeSource Mode=Self}, Path=... 

此外,如果您想使用 Path=Selector.IsSelectionActive,请改用 Path=(Selector.IsSelectionActive),因为它是一个附加属性。 - Andrew

0
对于DevExpress GridControl,有一个示例可以将字段值格式条件和行IsFocused属性组合起来。
XAML:
<dxg:GridControl.View>
    <dxg:TableView CustomRowAppearance="CustomRowAppearance" NavigationStyle="Row"> 
        <dxg:TableView.FormatConditions> 
            <dxg:DataBarFormatCondition FieldName="Profit" PredefinedFormatName="GreenGradientDataBar" /> 
            <dxg:FormatCondition FieldName="Profit" Expression="[Profit]&lt;0" PredefinedFormatName="RedText"/> 
            <dxg:FormatCondition FieldName="Profit" Expression="[Profit]&gt;=0" PredefinedFormatName="GreenText"/> 
            <dxg:FormatCondition FieldName="Profit" Expression="[Profit]&lt;=0" PredefinedFormatName="LightRedFillWithDarkRedText" ApplyToRow="True"/> 
        </dxg:TableView.FormatConditions> 
    </dxg:TableView>
</dxg:GridControl.View>

C#:

void CustomRowAppearance(object sender, CustomRowAppearanceEventArgs e) {
    if (e.RowSelectionState != SelectionState.None) {
        object result = e.ConditionalValue;
        if (e.Property == TextBlock.ForegroundProperty || e.Property == TextBlock.BackgroundProperty) {
            SolidColorBrush original = e.OriginalValue as SolidColorBrush;
            SolidColorBrush conditional = e.ConditionalValue as SolidColorBrush;
            if (conditional != null && (original == null || original.Color != conditional.Color))
                result = ShadeBrush(conditional);
        }
        e.Result = result;
        e.Handled = true;
    }
}

SolidColorBrush ShadeBrush(SolidColorBrush brush) {
    Color originalColor = brush.Color;
    float coefficient = 0.75f;
    byte a = originalColor.A;
    if (!grid.IsKeyboardFocusWithin) // I commented this line in WPF
        a = (byte)(originalColor.A / 2);
    byte r = (byte)(originalColor.R * coefficient);
    byte g = (byte)(originalColor.G * coefficient);
    byte b = (byte)(originalColor.B * coefficient);
    return new SolidColorBrush(Color.FromArgb(a, r, g, b));
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接