反向TwoWay-MultiBinding

时间:2012-09-22 08:37:48

标签: wpf binding multibinding two-way-binding

我试图在我的视图模型中将枚举属性表示为我视图中的一组单选按钮。到现在为止还挺好;我可以用双向MultiBinding来表达这一点:

  

(rb1.IsChecked,rb2.IsChecked,rb3.IsChecked)< - > vm.Value

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

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

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

  

(rbA1.IsChecked,rbA2.IsChecked,rbA3.IsChecked)&lt; - &gt; vm.Value&lt; - &gt; (rbB1.IsChecked,rbB2.IsChecked,rbB3.IsChecked)

问题是,我无法一次使用SetBinding将多个绑定连接到vm.Value

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

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

2 个答案:

答案 0 :(得分:0)

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

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

答案 1 :(得分:0)

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

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

在转换器中,

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