MultiDataTrigger如何使用OR而不是AND?

30

我正在尝试在我的Button上设置多个DataTriggers。我进行了一些研究,发现MultiDataTrigger允许您这样做。如果CCTVPath == string.Empty或者PermissionsFlag == false,我希望将ButtonVisibility属性设置为false。目前这是我的代码:

<Button Grid.Column="3" x:Name="cctvFeedButton" Content="Live Feed"
        Width="100" FontSize="16" HorizontalAlignment="Right" Margin="5" Click="OnCCTVButtonClick">
    <Button.Style>
        <Style TargetType="Button">
            <Style.Triggers>
                <MultiDataTrigger>
                    <MultiDataTrigger.Conditions>
                        <Condition Binding="{Binding CCTVPath}" Value=""/>
                        <Condition Binding="{Binding PermissionsFlag}" Value="False"/>
                    </MultiDataTrigger.Conditions>
                    <Setter Property="Visibility" Value="Hidden"/>
                </MultiDataTrigger>
            </Style.Triggers>
        </Style>
    </Button.Style>
</Button>

然后在我的代码后台中,我这样设置PermissionsFlag;

public bool PermissionsFlag { get; set; }

private void OnPageLoaded(object sender, RoutedEventArgs e)
{
    PermissionsFlag = false;
}

正如您所看到的,PermissionsFlag 明显为 false,而且 CCTVPath 明显为空,但是 Button 从未被隐藏。我做错了什么吗?

更新:

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    private bool _permissionsFlag;
    public bool Flag
    {
        get { return _permissionsFlag; }
        set
        {
            _permissionsFlag = value;
            OnPropertyChanged("PermissionsFlag");
        }
    }

    private void OnPageLoaded(object sender, RoutedEventArgs e)
    {
        Flag = false;
        CCTVImageCollection = GetImages();
        imageListBox.ItemsSource = CCTVImageCollection;
        DataContext = this;
    }

在我的 XAML 中:

<Button.Style>
         <Style TargetType="Button">
               <Style.Triggers>
                      <DataTrigger Binding="{Binding PermissionsFlag}" Value="False">
                            <Setter Property="Visibility" Value="Hidden"/>
                      </DataTrigger>
               </Style.Triggers>
         </Style>
 </Button.Style>
3个回答

43

将条件转换为两个独立的DataTriggers

<Style.Triggers>
    <DataTrigger Binding="{Binding CCTVPath}" Value="">
        <Setter Property="Visibility" Value="Hidden"/>
    </DataTrigger>
    <DataTrigger Binding="{Binding PermissionsFlag}" Value="False">
        <Setter Property="Visibility" Value="Hidden"/>
    </DataTrigger>
</Style.Triggers>

确保绑定路径正确(检查VS输出窗口是否有可能的异常消息)。


另外: 不要仅仅依赖于Button的隐藏状态,在代码中正确实现权限(OnCCTVButtonClick)。了解为什么在这里:如何防窥您的WPF应用程序?



自动属性PermissionsFlag(public bool PermissionsFlag { get; set; })不会通知视图有关更改的信息。

可以实现INotifyPropertyChanged接口(在我的测试窗口中是这样完成的:public partial class Window3:Window,INotifyPropertyChanged),然后在属性更改时触发事件。

这是我用于测试的完整工作示例

public partial class Window3 : Window, INotifyPropertyChanged
{
    public Window3()
    {
        InitializeComponent();
        this.DataContext = this;
        //PermissionsFlag = true;
        CCTVPath = "youtube.com";
    }

    private bool _permissionsFlag = false;
    private string _cctvPath;

    public bool PermissionsFlag
    {
        get { return _permissionsFlag; }
        set
        {
            _permissionsFlag = value;
            OnPropertyChanged("PermissionsFlag");
        }
    }

    public string CCTVPath
    {
        get { return _cctvPath; }
        set
        {
            _cctvPath = value;
            OnPropertyChanged("CCTVPath");
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}

Windows XAML:

<Window x:Class="WpfDemos.Views.Window3"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window3" Height="300" Width="300">
    <StackPanel>
        <CheckBox Name="chkPermissionsFlag" 
                  Content="PermissionsFlag" 
                  IsChecked="{Binding Path=PermissionsFlag, UpdateSourceTrigger=PropertyChanged}"/>

        <TextBox Text="{Binding Path=CCTVPath, UpdateSourceTrigger=PropertyChanged}"/>

        <Button x:Name="cctvFeedButton" Content="Live Feed"
                    Width="100" FontSize="16" HorizontalAlignment="Right" Margin="5">
            <Button.Style>
                <Style TargetType="Button">
                    <Style.Triggers>
                        <DataTrigger Binding="{Binding Path=CCTVPath}" Value="">
                            <Setter Property="Visibility" Value="Hidden"/>
                        </DataTrigger>
                        <DataTrigger Binding="{Binding Path=PermissionsFlag}" Value="False">
                            <Setter Property="Visibility" Value="Hidden"/>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </Button.Style>
        </Button>
    </StackPanel>
</Window>

感谢您的时间,ASh。当我这样做时,即使“PermissionsFlag == false”,“Button”仍然显示,而我希望无论CCTVPath的值如何,它都应该被隐藏。 - CBreeze
我预料到有些地方出了问题,如果我移除CCTVPathDataTrigger,那么它仍然不能按照我的期望工作。我是否可以添加一个方法来解决这个问题,或者需要创建一个全新的类来触发事件?你手头上有好的例子吗? - CBreeze
在页面的 OnLoaded 方法中,我使用 DataContext = this;PermissionsFlag 只是在页面内部使用 public bool PermissionsFlag { get; set; } 进行设置。 - CBreeze
OnPropertyChanged("PermissionsFlag"); 应该改为 OnPropertyChanged("_permissionsFlag"); 吗?这是否意味着我还需要修改 <DataTrigger Binding="{Binding PermissionsFlag}" Value="False"> - CBreeze
1
我已经更新了我的问题,你能看出代码有什么问题吗?(它仍然没有隐藏“Button”) - CBreeze
显示剩余4条评论

8
一种替代方案是使用一个带有MultiBinding的DataTrigger。您可以通过定义一个“特殊情况”的IMultiValueConverter来使其工作,该转换器假设对象数组中有2个项目,并且如果第一个项目为空字符串或第二个项目为false,则返回true。但是,您可能永远不会在代码的其他地方使用该转换器。因此,如果您愿意在前期多做一些工作,可以定义3个简单/可重用的转换器。
1)一个[IMultiValueConverter] 'OrConverter',它可能如下所示:
public class BooleanOrConverter : IMultiValueConverter {
   public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) {
      return values.OfType<bool>().Any(b => b);
   }

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

2) 一个[IValueConverter] 'IsNullOrEmpty'字符串转换器:

public class StringIsNullOrEmptyConverter : IValueConverter {
   public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
      return string.IsNullOrEmpty(value as string);
   }

   public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
      throw new NotImplementedException();
   }
}

3) 还有一个 [IValueConverter] 'NotConverter:'

public class BooleanNotConverter : IValueConverter {
   public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
      return !(bool)value;
   }

   public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
      throw new NotImplementedException();
   }
}

然后,在你的 XAML 中,DataTrigger 应该像这样定义:

<Button x:Name="cctvFeedButton" Content="Live Feed"
  Width="100" FontSize="16" HorizontalAlignment="Right" Margin="5">
  <Button.Style>
    <Style TargetType="Button">
      <Style.Triggers>
         <DataTrigger Value="True">
           <DataTrigger.Binding>
             <MultiBinding Converter="{StaticResource OrConverter}">
               <Binding Path="PermissionFlag" Converter="{StaticResource NotConverter}"/>
               <Binding Path="CCTVPath" Converter="{StaticResource IsNullOrEmptyConverter}"/>
             </MultiBinding>
           </DataTrigger.Binding>
           <Setter Property="Visibility" Value="Hidden"/>
        </DataTrigger>
      </Style.Triggers>
    </Style>
  </Button.Style>
</Button>

我更喜欢这个解决方案,因为它更易读,与使用两个独立的DataTrigger相比;它更好地表达了您正在定义的行为 - 它是“或”逻辑:一组2个条件,应该隐藏按钮。


1
我使用这些触发器来使添加按钮在文本框为空时禁用:
或者(如果其中一个文本框被填充,按钮将启用)
<MultiDataTrigger>
    <MultiDataTrigger.Conditions>
        <Condition Binding="{Binding Text, ElementName=Usernametxtbox}" Value=""/>
        <Condition Binding="{Binding Password, ElementName=passtxtbox}" Value=""/>
    </MultiDataTrigger.Conditions>
    <Setter Property="IsEnabled" Value="False" />
</MultiDataTrigger>

而且(如果两个文本框都填写,按钮将启用)

<DataTrigger Binding="{Binding Text, ElementName=Usernametxtbox}" Value="">
    <Setter Property="IsEnabled" Value="False" />
</DataTrigger>

<DataTrigger Binding="{Binding Password, ElementName=passtxtbox}" Value="">
    <Setter Property="IsEnabled" Value="False" />
</DataTrigger>

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