What I have is an object that has an IsReadOnly
property. If this property is true, I would like to set the IsEnabled
property on a Button, ( for example ), to false.
I would like to believe that I can do it as easily as IsEnabled="{Binding Path=!IsReadOnly}"
but that doesn't fly with WPF.
Am I relegated to having to go through all of the style settings? Just seems too wordy for something as simple as setting one bool to the inverse of another bool.
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=IsReadOnly}" Value="True">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsReadOnly}" Value="False">
<Setter Property="IsEnabled" Value="True" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
With standart binding you need to use converters that looks little windy. So, I recommend you to look at my project CalcBinding, which was developed specially to resolve this problem and some others. With advanced binding you can write expressions with many source properties directly in xaml. Say, you can write something like:
<Button IsEnabled="{c:Binding Path=!IsReadOnly}" />
or
<Button Content="{c:Binding ElementName=grid, Path=ActualWidth+Height}"/>
or
<Label Content="{c:Binding A+B+C }" />
or
<Button Visibility="{c:Binding IsChecked, FalseToVisibility=Hidden}" />
where A, B, C, IsChecked - properties of viewModel and it will work properly
.Net Core Solution
Handles null situation and does not throw an exception, but returns true
if no value is presented; otherwise takes the inputted Boolean and reverses it.
public class BooleanToReverseConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
=> !(bool?) value ?? true;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> !(value as bool?);
}
Xaml
IsEnabled="{Binding IsSuccess Converter={StaticResource BooleanToReverseConverter}}"
App.Xaml I like to put all my converter statics in the app.xaml file so I don't have to redeclare them throughout the windows/pages/controls of the project.
<Application.Resources>
<converters:BooleanToReverseConverter x:Key="BooleanToReverseConverter"/>
<local:FauxVM x:Key="VM" />
</Application.Resources>
To be clear converters:
is the namespace to the actual class implementation (xmlns:converters="clr-namespace:ProvingGround.Converters"
).
I would recommend using https://quickconverter.codeplex.com/
Inverting a boolean is then as simple as:
<Button IsEnabled="{qc:Binding '!$P', P={Binding IsReadOnly}}" />
That speeds the time normally needed to write converters.
This one also works for nullable bools.
[ValueConversion(typeof(bool?), typeof(bool))]
public class InverseBooleanConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (targetType != typeof(bool?))
{
throw new InvalidOperationException("The target must be a nullable boolean");
}
bool? b = (bool?)value;
return b.HasValue && !b.Value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return !(value as bool?);
}
#endregion
}
Add one more property in your view model, which will return reverse value. And bind that to button. Like;
in view model:
public bool IsNotReadOnly{get{return !IsReadOnly;}}
in xaml:
IsEnabled="{Binding IsNotReadOnly"}
I had an inversion problem, but a neat solution.
Motivation was that the XAML designer would show an empty control e.g. when there was no datacontext / no MyValues
(itemssource).
Initial code: hide control when MyValues
is empty.
Improved code: show control when MyValues
is NOT null or empty.
Ofcourse the problem is how to express '1 or more items', which is the opposite of 0 items.
<ListBox ItemsSource={Binding MyValues}">
<ListBox.Style x:Uid="F404D7B2-B7D3-11E7-A5A7-97680265A416">
<Style TargetType="{x:Type ListBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding MyValues.Count}">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.Style>
</ListBox>
I solved it by adding:
<DataTrigger Binding="{Binding MyValues.Count, FallbackValue=0, TargetNullValue=0}">
Ergo setting the default for the binding. Ofcourse this doesn't work for all kinds of inverse problems, but helped me out with clean code.
I did something very similar. I created my property behind the scenes that enabled the selection of a combobox ONLY if it had finished searching for data. When my window first appears, it launches an async loaded command but I do not want the user to click on the combobox while it is still loading data (would be empty, then would be populated). So by default the property is false so I return the inverse in the getter. Then when I'm searching I set the property to true and back to false when complete.
private bool _isSearching;
public bool IsSearching
{
get { return !_isSearching; }
set
{
if(_isSearching != value)
{
_isSearching = value;
OnPropertyChanged("IsSearching");
}
}
}
public CityViewModel()
{
LoadedCommand = new DelegateCommandAsync(LoadCity, LoadCanExecute);
}
private async Task LoadCity(object pArg)
{
IsSearching = true;
//**Do your searching task here**
IsSearching = false;
}
private bool LoadCanExecute(object pArg)
{
return IsSearching;
}
Then for the combobox I can bind it directly to the IsSearching:
<ComboBox ItemsSource="{Binding Cities}" IsEnabled="{Binding IsSearching}" DisplayMemberPath="City" />
I use a similar approach like @Ofaim
private bool jobSaved = true;
private bool JobSaved
{
get => jobSaved;
set
{
if (value == jobSaved) return;
jobSaved = value;
OnPropertyChanged();
OnPropertyChanged("EnableSaveButton");
}
}
public bool EnableSaveButton => !jobSaved;
Following @Paul's answer, I wrote the following in the ViewModel:
public bool ShowAtView { get; set; }
public bool InvShowAtView { get { return !ShowAtView; } }
I hope having a snippet here will help someone, probably newbie as I am.
And if there's a mistake, please let me know!
BTW, I also agree with @heltonbiker comment - it's definitely the correct approach only if you don't have to use it more than 3 times...
I wanted my XAML to remain as elegant as possible so I created a class to wrap the bool which resides in one of my shared libraries, the implicit operators allow the class to be used as a bool in code-behind seamlessly
public class InvertableBool
{
private bool value = false;
public bool Value { get { return value; } }
public bool Invert { get { return !value; } }
public InvertableBool(bool b)
{
value = b;
}
public static implicit operator InvertableBool(bool b)
{
return new InvertableBool(b);
}
public static implicit operator bool(InvertableBool b)
{
return b.value;
}
}
The only changes needed to your project are to make the property you want to invert return this instead of bool
public InvertableBool IsActive
{
get
{
return true;
}
}
And in the XAML postfix the binding with either Value or Invert
IsEnabled="{Binding IsActive.Value}"
IsEnabled="{Binding IsActive.Invert}"
Don't know if this is relevant to XAML, but in my simple Windows app I created the binding manually and added a Format event handler.
public FormMain() {
InitializeComponent();
Binding argBinding = new Binding("Enabled", uxCheckBoxArgsNull, "Checked", false, DataSourceUpdateMode.OnPropertyChanged);
argBinding.Format += new ConvertEventHandler(Binding_Format_BooleanInverse);
uxTextBoxArgs.DataBindings.Add(argBinding);
}
void Binding_Format_BooleanInverse(object sender, ConvertEventArgs e) {
bool boolValue = (bool)e.Value;
e.Value = !boolValue;
}
Have you considered an IsNotReadOnly
property? If the object being bound is a ViewModel in a MVVM domain, then the additional property makes perfect sense. If it's a direct Entity model, you might consider composition and presenting a specialized ViewModel of your entity to the form.
Source: Stackoverflow.com