I have a checkbox with a MultiBinding where one binding is twoway (to a viewmodel) and the other is oneway (to it's own IsEnabled property). Everything seems to work fine until I touch the multibound checkbox. Then I suddenly loose a binding.
The following sample demonstrates this effect. In the real program, the IsEnabled property is also a multibinding, but that doesn't seem to make a difference.
<Window x:Class="TwowayMultiBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TwowayMultiBinding"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel>
<StackPanel.Resources>
<local:LogicalAndConverter x:Key="LogicalAndConverter"/>
</StackPanel.Resources>
<CheckBox Content="Enabled" Name="EnableCheck"/>
<CheckBox Content="Ticked" Name="TickCheck"/>
<CheckBox Content="Test" Name="TestCheck" IsEnabled="{Binding ElementName=EnableCheck, Path=IsChecked}">
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource LogicalAndConverter}">
<Binding ElementName="TestCheck" Path="IsEnabled" Mode="OneWay"/>
<Binding ElementName="TickCheck" Path="IsChecked" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"/>
</MultiBinding>
</CheckBox.IsChecked>
</CheckBox>
</StackPanel>
</Window>
And I'm using the following converter:
public class LogicalAndConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (values == null || values.Length < 2)
{
return DependencyProperty.UnsetValue;
}
for (int i = 0; i < values.Length; i++)
{
bool result;
if (values[i] is bool bValue)
{
result = bValue;
}
else if (values[i] is bool?)
{
result = ((bool?)values[i]) ?? false;
}
else
{
return DependencyProperty.UnsetValue;
}
if (!result)
{
return false; // early exit.
}
}
return true;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
if (value is bool bValue)
{
return Enumerable.Repeat(value, targetTypes.Length).ToArray();
}
else if (value is bool?)
{
bool result = ((bool?)value) ?? false;
return Enumerable.Repeat((object)result, targetTypes.Length).ToArray();
}
return null;
}
When I run this example, the Test Check is disabled, because the Enabled checkbox is not checked (correct).
To reproduce the problem:
But now, the Enabled check does not work anymore. Did I loose the binding?
Short route:
Once again, Enabled doesn't work anymore.
What I'm trying to do is:
After Andy's answer that a binding is effectively lost unless it's a twoway binding, I decided to make it a twoway binding and let a 2nd converter avoid the checkbox actually being disabled when I click the Test Check. And this works:
<Window x:Class="TwowayMultiBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:TwowayMultiBinding"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<StackPanel>
<StackPanel.Resources>
<local:LogicalAndConverter x:Key="LogicalAndConverter"/>
<local:OneWayBooleanConverter x:Key="OneWayBooleanConverter"/>
</StackPanel.Resources>
<CheckBox Content="Enabled" Name="EnableCheck"/>
<CheckBox Content="Ticked" Name="TickCheck"/>
<CheckBox Content="Test" Name="TestCheck" IsEnabled="{Binding ElementName=EnableCheck, Path=IsChecked, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource LogicalAndConverter}">
<Binding ElementName="TickCheck" Path="IsChecked" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged"/>
<Binding ElementName="TestCheck" Path="IsEnabled" Mode="TwoWay" UpdateSourceTrigger="PropertyChanged" Converter="{StaticResource OneWayBooleanConverter}"/>
</MultiBinding>
</CheckBox.IsChecked>
</CheckBox>
</StackPanel>
</Window>
public class OneWayBooleanConverter : IValueConverter
{
private bool lastValue = false;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool bValue)
{
lastValue = bValue;
return bValue;
}
return DependencyProperty.UnsetValue;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool bValue)
{
return lastValue;
}
return DependencyProperty.UnsetValue;
}
}
The LogicalAndConverter class has been unchanged. Not sure if this solution is aesthetically correct, but atleast it works.
Unfortunately, this doesn't work for the real-world program, because the checkbox being disabled is part of a item control. It's impossible to know which checkbox (and with that which cached value) is used from within the IValueConverter.ConvertBack function.
Thankfully, the requirement for which I needed this has been dropped, so I don't have to worry about it any longer.