我以前将枚举绑定到单选按钮,我大致了解它是如何工作的。我使用了这个问题中的替代实现:How to bind RadioButtons to an enum?
我想生成一个自定义类型的运行时枚举集,并将它们表示为一组单选按钮,而不是枚举。我已经获得了一个视图,该视图针对一个运行时枚举集使用ListView
,绑定到ItemsSource
和SelectedItem
属性,因此我的ViewModel
被正确地连接起来。现在,我正尝试从ListView
切换到带单选按钮的ItemsControl
。
这就是我所得到的:
<Window.Resources>
<vm:InstanceToBooleanConverter x:Key="InstanceToBooleanConverter" />
</Window.Resources>
<!-- ... -->
<ItemsControl ItemsSource="{Binding ItemSelections}">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type vm:ISomeType}">
<RadioButton Content="{Binding Name}"
IsChecked="{Binding Path=SelectedItem, Converter={StaticResource InstanceToBooleanConverter}, ConverterParameter={Binding}}"
Grid.Column="0" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
在另一个问题中,InstanceToBooleanConverter
与EnumToBooleanConverter
具有相同的实现。这似乎是对的,因为它似乎只是调用了Equals
方法:
public class InstanceToBooleanConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.Equals(parameter);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return value.Equals(true) ? parameter : Binding.DoNothing;
}
}
我现在遇到的问题是,我不知道如何将运行时值作为ConverterParameter
发送。当我尝试(使用上面的代码)时,我得到这个错误:
Binding A‘
’不能在类型为'Binding‘的'ConverterParameter’属性上设置。只能在DependencyObject的DependencyProperty上设置“Binding”。
是否有一种方法可以绑定到item实例,并将其传递给IValueConverter
发布于 2011-05-06 09:05:47
事实证明,放弃使用ItemsControl
,转而使用ListBox
要简单得多。
它可能更重,但这主要是因为它正在为你做繁重的工作。在RadioButton.IsChecked
和ListBoxItem.IsSelected
之间进行双向绑定真的很容易。通过为ListBoxItem
选择适当的控件模板,您可以很容易地摆脱所有的选择可视化。
<ListBox ItemsSource="{Binding Properties}" SelectedItem="{Binding SelectedItem}">
<ListBox.ItemContainerStyle>
<!-- Style to get rid of the selection visual -->
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type local:SomeClass}">
<RadioButton Content="{Binding Name}" GroupName="Properties">
<!-- Binding IsChecked to IsSelected requires no support code -->
<RadioButton.IsChecked>
<Binding Path="IsSelected"
RelativeSource="{RelativeSource AncestorType=ListBoxItem}"
Mode="TwoWay" />
</RadioButton.IsChecked>
</RadioButton>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
发布于 2011-05-05 11:54:47
据我所知,使用MultiBinding
没有好的方法来实现这一点,尽管您最初认为会有这样的方法。由于您不能绑定ConverterParameter
,因此您的ConvertBack
实现没有它所需的信息。
我所做的是创建一个单独的EnumModel
类,专门用于将枚举绑定到单选按钮。在ItemsSource
属性上使用转换器,就可以绑定到EnumModel
。EnumModel
只是一个使绑定成为可能的转发器对象。它包含枚举的一个可能值和对视图模型的引用,因此它可以将视图模型上的属性与布尔值相互转换。
这是一个未经测试的通用版本:
<ItemsControl ItemsSource="{Binding Converter={StaticResource theConverter} ConverterParameter="SomeEnumProperty"}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton IsChecked="{Binding IsChecked}">
<TextBlock Text="{Binding Name}" />
</RadioButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
转换器:
public class ToEnumModelsConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var viewmodel = value;
var prop = viewmodel.GetType().GetProperty(parameter as string);
List<EnumModel> enumModels = new List<EnumModel>();
foreach(var enumValue in Enum.GetValues(prop.PropertyType))
{
var enumModel = new EnumModel(enumValue, viewmodel, prop);
enumModels.Add(enumModel);
}
return enumModels;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
EnumModel:
public class EnumModel : INPC
{
object enumValue;
INotifyPropertyChanged viewmodel;
PropertyInfo property;
public EnumModel(object enumValue, object viewmodel, PropertyInfo property)
{
this.enumValue = enumValue;
this.viewmodel = viewmodel as INotifyPropertyChanged;
this.property = property;
this.viewmodel.PropertyChanged += new PropertyChangedEventHandler(viewmodel_PropertyChanged);
}
void viewmodel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == property.Name)
{
OnPropertyChanged("IsChecked");
}
}
public bool IsChecked
{
get
{
return property.GetValue(viewmodel, null).Equals(enumValue);
}
set
{
if (value)
{
property.SetValue(viewmodel, enumValue, null);
}
}
}
}
对于我知道可以工作的代码示例(但它仍然很不完善- WIP!),您可以查看http://code.google.com/p/pdx/source/browse/trunk/PDX/PDX/Toolkit/EnumControl.xaml.cs。这只适用于我的库的上下文,但它演示了如何根据DescriptionAttribute
设置EnumModel的名称,这可能对您有用。
发布于 2011-05-05 10:22:45
你就差一步了。当一个转换器需要两个绑定时,您需要一个MultiBinding
和一个IMultiValueConverter
!语法有点冗长,但不会更难。
编辑:
这里有一小段代码可以帮助您入门。
绑定:
<RadioButton Content="{Binding Name}"
Grid.Column="0">
<RadioButton.IsChecked>
<MultiBinding Converter="{StaticResource EqualsConverter}">
<Binding Path="SelectedItem"/>
<Binding Path="Name"/>
</MultiBinding>
</RadioButton.IsChecked>
</RadioButton>
和转换器:
public class EqualsConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values[0].Equals(values[1]);
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
第二次编辑:
上面的方法对于使用问题中链接的技术实现双向绑定没有用处,因为在转换回时没有必要的信息。
我认为正确的解决方案是直截了当的MVVM:编写view-model以匹配视图的需求。代码量非常小,并且不需要任何转换器或有趣的绑定或技巧。
下面是XAML;
<Grid>
<ItemsControl ItemsSource="{Binding}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton
GroupName="Value"
Content="{Binding Description}"
IsChecked="{Binding IsChecked, Mode=TwoWay}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
和代码隐藏来模拟视图模型:
DataContext = new CheckBoxValueCollection(new[] { "Foo", "Bar", "Baz" });
和一些视图模型基础设施:
public class CheckBoxValue : INotifyPropertyChanged
{
private string description;
private bool isChecked;
public string Description
{
get { return description; }
set { description = value; OnPropertyChanged("Description"); }
}
public bool IsChecked
{
get { return isChecked; }
set { isChecked = value; OnPropertyChanged("IsChecked"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class CheckBoxValueCollection : ObservableCollection<CheckBoxValue>
{
public CheckBoxValueCollection(IEnumerable<string> values)
{
foreach (var value in values)
this.Add(new CheckBoxValue { Description = value });
this[0].IsChecked = true;
}
public string SelectedItem
{
get { return this.First(item => item.IsChecked).Description; }
}
}
https://stackoverflow.com/questions/5891924
复制相似问题