【问题标题】:Inverted TwoWay-MultiBinding反向双向多重绑定
【发布时间】:2012-09-14 13:34:49
【问题描述】:

我试图将视图模型中的枚举属性表示为视图中的一组单选按钮。到目前为止,一切都很好;我可以用双向MultiBinding 来表达:

(rb1.IsChecked, rb2.IsChecked, rb3.IsChecked) vm.Value

这里使用的多重绑定将具有一个多重转换器,可在(bool, bool, bool) <-> MyValue 之间转换;显然,MyValue 类型的(三个)允许值之一是根据booltrue 来选择的,反之亦然。

不过,这已经有点不方便了:我无法在视图的 Xaml 中定义该绑定,因为必须从单个值的一侧定义多重绑定。因此,我必须在代码隐藏中定义多重绑定并使用SetBinding 将其设置在我的视图模型的Value 属性上。

现在,我遇到的问题是我不只是将一组单选按钮绑定到该值,而是两个。因此,我的绑定必须如下所示:

(rbA1.IsChecked, rbA2.IsChecked, rbA3.IsChecked) vm.Value (rbB1.IsChecked, rbB2.IsChecked, rbB3.IsChecked)

问题是我不能同时使用SetBinding 将多个绑定连接到vm.Value

到目前为止我尝试过的解决方案是:

  • 使用一个 big 多重绑定,一次绑定到所有单选按钮。这意味着(rbA1.IsChecked, rbA2.IsChecked, rbA3.IsChecked, rbB1.IsChecked, rbB2.IsChecked, rbB3.IsChecked) <-> vm.Value 形式的绑定。此解决方案的问题在于,如果选中了其中一​​个单选按钮(例如,rbB2),我无法判断rbA2(未选中)或rbB2(选中)是否具有“新的,正确的”价值。
  • 首先通过定义一个单选按钮组 控件来抽象单选按钮组,该控件只公开一个SelectedIndex 属性。然后,这个属性可以方便地从我的 radio group 控件的所有实例中绑定到我的vm.Value 属性。虽然可行,但它需要编写一个新的控件类,我想知道这是否是 WPF 中的唯一方法。
  • 将一组单选按钮绑定到另一组:通过双向绑定rbB1rbA1rbB2rbA2 等等,并在我的vm.Value 之间使用多重绑定并且只有第一组单选按钮,我会达到预期的效果,但我不喜欢拥有“主单选组”的概念。这将滥用 GUI 元素进行数据传输。
  • 在代码隐藏中执行所有操作并手动更新单选按钮和视图模型值。当然这是一个可行的后备解决方案,但是这感觉在 Xaml/with 绑定中应该是可行的。

【问题讨论】:

  • “使用一个大的多重绑定”,我不太明白。您有两个 radioButton 组。您可以从 group1 中选择一项,也可以从 group2 中选择一项。我不明白“具有新的正确值”。
  • @ChrisDD:“大多重绑定”将同时绑定vm.Value 与所有六个单选按钮。六个单选按钮可以有一个初始状态,例如(1, 0, 0, 1, 0, 0)。现在,假设有人点击了第二组中的第二个单选按钮,所以我的多转换器被(1, 0, 0, 0, 1, 0) 调用。如何判断第一组还是第二组正确,即返回第一个枚举值还是第二个枚举值?据我所知,这并不方便。
  • 为什么不对每个 RadioButton 使用单个绑定?也使用转换器和命令参数。如:IsChecked={Binding VMEnum, Converter={StaticResource toEnum}, ConverterParameter={YourEnum:FirstButtonChecked}}。您需要实现双向转换器。无论哪种方式 IsChecked = VMEnum == (CommandParameter as Enum) [一种方式]
  • 用于从 ViewMODEl 控制 RadioBoxes。对于相反的方式,逻辑将是:IsChecked==true 然后返回 CommandParameter。绑定必须是双向的
  • @ChrisDD:很好,只要在IsChecked 更改为true 时调用转换器。但是,当这种情况发生时,所有其他单选按钮的 IsChecked 属性将更改为 false;他们的转换器应该返回什么?

标签: wpf binding multibinding two-way-binding


【解决方案1】:

将 VMEnum 单独绑定到每个 RadioButton(使用单个双向绑定)。每个绑定都应该有 CommandParameter 它是枚举。喜欢:

<RadioButton IsChecked="{Binding VMEnum, Converter={StaticResource EnumConverter}, ConverterParameter={Enums:VMEnums.FirstRadioButtonGroupA}}" />

在转换器中,

  • Convert 应该返回正确的值(真/假),具体取决于 VMEnum 和 COMmandParameter。本质上逻辑是 VMEnum == (YourEnum)CommandParameter。
  • ConvertBack 应该根据 IsChecked 返回正确的枚举。如果 IsChecked 为真,则返回正确的枚举。否则返回 Binding.DoNothing,这将中止该特定情况的绑定。

【讨论】:

  • Binding.DoNothing 是我错过的作品!
【解决方案2】:

将复杂的多重绑定与转换器和代码隐藏一起使用不仅会使您的代码更难调试,而且更难测试。在我看来,最好将每组单选按钮(标志)表示为视图模型。在选中/取消选中任何单选按钮时评估您的值。

RadioButtonGroup

public class RadioButtonGroup : ViewModel {

    public RadioButtonGroup(string groupName, int count, Action<bool[]> whenAnyChanaged = null) {

        RadioButtons = Enumerable.Range(0, count).Select(_ => {
            var button = new RadioButton { GroupName = groupName };
            button.PropertyChanged += (s, e) => {
                if (e.PropertyName == "IsChecked")
                    whenAnyChanaged(Flags);
            };
            return button;
        }).ToList();           
    }

    public List<RadioButton> RadioButtons { get; private set; }

    public bool[] Flags { get { return RadioButtons.Select(rb => rb.IsChecked).ToArray(); } }
}

单选按钮

 public class RadioButton : ViewModel {

    private bool isChecked;

    public bool IsChecked {
        get { return isChecked; }
        set { SetProperty(ref this.isChecked, value); }
    }

    public string GroupName { get; set; }
}

MainViewModel

public class MainViewModel : ViewModel {
    public MainViewModel() {
        GroupA = new RadioButtonGroup("A", 10, flags => GroupToggle(flags, GroupB.Flags));
        GroupB = new RadioButtonGroup("B", 10, flags => GroupToggle(GroupA.Flags, flags));
    }

    public RadioButtonGroup GroupA { get; private set; }

    public RadioButtonGroup GroupB { get; private set; }

    void GroupToggle(bool[] groupA, bool[] groupB) {
        MyValue = Evaluate(groupA, groupB);
    }
}

查看

<Window x:Class="WpfLab.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="{Binding Title}" Height="350" Width="525">
<Window.Resources>
    <DataTemplate x:Key="RadioButton">
        <RadioButton IsChecked="{Binding IsChecked, Mode=OneWayToSource}" GroupName="{Binding GroupName}"/>
    </DataTemplate>
</Window.Resources>
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="30"/>
        <RowDefinition Height="30"/>
    </Grid.RowDefinitions>

    <ListBox Grid.Row="0" ItemsSource="{Binding GroupA.RadioButtons}" ItemTemplate="{StaticResource ResourceKey=RadioButton}">
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
    </ListBox>

    <ListBox Grid.Row="1" ItemsSource="{Binding GroupB.RadioButtons}" ItemTemplate="{StaticResource ResourceKey=RadioButton}">
        <ListBox.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
    </ListBox>
</Grid>

【讨论】:

  • 您在 MainViewModel 类中调用的 Evaluate 方法是什么?特别是,当RadioButtonGroups 的状态为例如(1, 0, 0); (0, 1, 0),它如何知道(1, 0, 0)(0, 1, 0) 是预期的新状态?
  • 现在可能不会,但是您可以添加 Enum,例如“哪个组已更改”并将其添加到 GroupToggle 并修改代表(MainiewModel()) + 将其传递给 Evaluate。
  • @ChrisDD:我认为问题在于,在调用Evaluate 时,已经不知道/发现哪个组被更改了。
猜你喜欢
  • 1970-01-01
  • 2011-01-19
  • 1970-01-01
  • 2015-04-26
  • 2016-04-19
  • 2012-05-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多