MultiDataTrigger with OR instead of AND
tranform Conditions into two independent 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>
make sure that binding paths are correct (check VS Output window for possible exception messages)
also: don't rely only on hidden state of Button, implement permissions properly in code (
OnCCTVButtonClick
). read why here:
How to Snoop proof your wpf application?
auto-property PermissionsFlag (public bool PermissionsFlag { get; set; }
) doesn't notify view about changes.
it is possible to implement INotifyPropertyChanged
interface (in my test window it is done like this: public partial class Window3 : Window, INotifyPropertyChanged
) and then raise event when property changes.
here is a complete working example which I used for test
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));
}
}
window 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>
An alternative solution is to use a single DataTrigger with a MultiBinding. You could make it work by defining a 'special-case' IMultiValueConverter that assumes 2 items in the object array, and returns true if the first item is an empty string OR the second item is false. However, you probably won't ever use that converter anywhere else in your code. So if you are willing to do a little more work up front, you could define 3 simple/reusable converters.
1) an [IMultiValueConverter] 'OrConverter,' which would look something like this:
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) An [IValueConverter] 'IsNullOrEmpty' string converter:
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) And an [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();
}
}
Then, in your xaml, the DataTrigger would be defined like this:
<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>
I prefer this solution over the use of 2 separate DataTriggers for readability; it better expresses the behavior you are defining - it is 'or' logic: a singular set of 2 conditions that should hide the button.