WPF双向多绑定=>丢失绑定?

nsc4cvqm  于 2023-01-31  发布在  其他
关注(0)|答案(3)|浏览(208)

我有一个带有MultiBinding的复选框,其中一个绑定是双向的(到视图模型),另一个是单向的(到它自己的IsEnabled属性)。一切看起来都很好,直到我点击了MultiBinding复选框。然后我突然松开了一个绑定。
下面的示例演示了这种效果。在实际程序中,IsEnabled属性也是一个多绑定,但这似乎没有什么区别。

<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>

我使用下面的转换器:

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;
    }

当我运行这个例子时,TestCheck被禁用,因为Enabled复选框没有被选中(正确)。
要重现此问题:

  • 单击启用=〉测试检查已启用(正确)。
  • 点击勾选=〉测试检查勾选(正确)。
  • 点击勾选=〉测试检查未勾选(正确)。
  • 点击勾选=〉测试检查勾选(正确)。
  • 点击启用=〉测试检查被禁用且未勾选(正确)。
  • 单击启用=〉启用并勾选测试检查(因为勾选检查仍处于勾选状态)。(correct)
  • 单击测试=〉未选中测试检查(正确)。

但是现在,启用检查不再起作用了。我松开了绑定吗?
短路线:

  • 开始
  • 单击启用=〉测试检查已启用(正确)。
  • 单击测试=〉未选中测试检查(正确)。

再一次,启用不再起作用。
我想做的是:

  • 将check值绑定到它的视图模型(双向)。
  • 如果未选中"启用",则应无法以任何方式选中"测试"检查。
ztyzrc3y

ztyzrc3y1#

是的,你实际上失去了约束力。
如果将dependencyproperty设置为一个值,则该值将覆盖任何绑定,除非绑定是双向的。
单击时,将在“测试”上设置ischecked值。
避免这种情况的一个方法是将逻辑移到视图模型中,然后就可以在各种属性的setter和getter中精确地控制你想要发生的事情。
您可能需要考虑中间属性,这样您就有了表示视图和输入的属性、其他属性或表示内部状态的字段。
也许您正尝试将“测试”复选框用于两个不同的目的,其中两个不同的控件可能更合适。也许它应该是只读的。

11dmarpk

11dmarpk2#

我认为您需要更换反向转换器:

public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        object[] objs = Enumerable.Repeat(Binding.DoNothing, targetTypes.Length).ToArray();
        if (value is bool bValue)
        {
            objs[1] = bValue;
        }
        else if(value is bool? bnValue)
        {
            objs[1] = (bool)(bnValue ?? false);
        }
        return objs;
    }
xzv2uavs

xzv2uavs3#

Andy回答说除非是双向绑定,否则绑定实际上会丢失,我决定将其设置为双向绑定,并让第二个转换器在我点击Test Check时避免复选框被禁用。

<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;
        }
    }

LogicalAndConverter类没有改变。不确定这个解决方案在美学上是否正确,但至少它能工作。

相关问题