反向双向多重绑定



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

(rb1.IsChecked、rb2.IsChecked和rb3.IsChecked)<->vm。价值

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

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

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

(rbA1.IsChecked、rbA2.IsChecked和rbA3.IsChecked)<->vm。值<->(rbB1.I已检查、rbB2.I已检查和rbB3.已检查)

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

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

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

将VMEnum分别绑定到每个RadioButton(使用单个双向绑定)。每个绑定都应该有其枚举的CommandParameter。类似:

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

在转换器中,

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

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

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>

相关内容

  • 没有找到相关文章

最新更新