我正试图将视图模型中的枚举属性表示为视图中的一组单选按钮。到目前为止,一切都很好;我可以用双向MultiBinding
:来表达
(rb1.IsChecked、rb2.IsChecked和rb3.IsChecked)<->vm。价值
这里使用的多绑定将具有在(bool, bool, bool) <-> MyValue
之间转换的多转换器;显然,基于bool
是true
来选择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中的唯一方法 - 将一组单选按钮绑定到另一组:通过双向绑定
rbB1
到rbA1
,rbB2
到rbA2
等等,并且仅在我的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>