首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >ItemsControl上的WPF MVVM单选按钮

ItemsControl上的WPF MVVM单选按钮
EN

Stack Overflow用户
提问于 2011-05-05 10:08:43
回答 4查看 20.9K关注 0票数 22

我以前将枚举绑定到单选按钮,我大致了解它是如何工作的。我使用了这个问题中的替代实现:How to bind RadioButtons to an enum?

我想生成一个自定义类型的运行时枚举集,并将它们表示为一组单选按钮,而不是枚举。我已经获得了一个视图,该视图针对一个运行时枚举集使用ListView,绑定到ItemsSourceSelectedItem属性,因此我的ViewModel被正确地连接起来。现在,我正尝试从ListView切换到带单选按钮的ItemsControl

这就是我所得到的:

代码语言:javascript
复制
<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>

在另一个问题中,InstanceToBooleanConverterEnumToBooleanConverter具有相同的实现。这似乎是对的,因为它似乎只是调用了Equals方法:

代码语言:javascript
复制
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

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2011-05-06 09:05:47

事实证明,放弃使用ItemsControl,转而使用ListBox要简单得多。

它可能更重,但这主要是因为它正在为你做繁重的工作。在RadioButton.IsCheckedListBoxItem.IsSelected之间进行双向绑定真的很容易。通过为ListBoxItem选择适当的控件模板,您可以很容易地摆脱所有的选择可视化。

代码语言:javascript
复制
<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>
票数 42
EN

Stack Overflow用户

发布于 2011-05-05 11:54:47

据我所知,使用MultiBinding没有好的方法来实现这一点,尽管您最初认为会有这样的方法。由于您不能绑定ConverterParameter,因此您的ConvertBack实现没有它所需的信息。

我所做的是创建一个单独的EnumModel类,专门用于将枚举绑定到单选按钮。在ItemsSource属性上使用转换器,就可以绑定到EnumModelEnumModel只是一个使绑定成为可能的转发器对象。它包含枚举的一个可能值和对视图模型的引用,因此它可以将视图模型上的属性与布尔值相互转换。

这是一个未经测试的通用版本:

代码语言:javascript
复制
<ItemsControl ItemsSource="{Binding Converter={StaticResource theConverter} ConverterParameter="SomeEnumProperty"}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <RadioButton IsChecked="{Binding IsChecked}">
                <TextBlock Text="{Binding Name}" />
            </RadioButton>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

转换器:

代码语言:javascript
复制
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:

代码语言:javascript
复制
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的名称,这可能对您有用。

票数 4
EN

Stack Overflow用户

发布于 2011-05-05 10:22:45

你就差一步了。当一个转换器需要两个绑定时,您需要一个MultiBinding和一个IMultiValueConverter!语法有点冗长,但不会更难。

编辑:

这里有一小段代码可以帮助您入门。

绑定:

代码语言:javascript
复制
<RadioButton Content="{Binding Name}"
        Grid.Column="0">
    <RadioButton.IsChecked>
        <MultiBinding Converter="{StaticResource EqualsConverter}">
            <Binding Path="SelectedItem"/>
            <Binding Path="Name"/>
        </MultiBinding>
    </RadioButton.IsChecked>
</RadioButton>

和转换器:

代码语言:javascript
复制
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;

代码语言:javascript
复制
<Grid>
    <ItemsControl ItemsSource="{Binding}">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <RadioButton
                    GroupName="Value"
                    Content="{Binding Description}"
                    IsChecked="{Binding IsChecked, Mode=TwoWay}"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</Grid>

和代码隐藏来模拟视图模型:

代码语言:javascript
复制
        DataContext = new CheckBoxValueCollection(new[] { "Foo", "Bar", "Baz" });

和一些视图模型基础设施:

代码语言:javascript
复制
    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; }
        }
    }
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5891924

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档