【问题标题】:How to disable a ComboBoxItem that is provided via ObjectProvider Enum WPF C# in MVVM?如何在 MVVM 中禁用通过 ObjectProvider Enum WPF C# 提供的 ComboBoxItem?
【发布时间】:2014-03-13 12:50:53
【问题描述】:

我有一个填充组合框的“发票操作”枚举,但我想根据 ViewModel 中的“CanDisplayDetails”属性禁用枚举成员之一(“视图”)。

我不太清楚如何将 IsEnabled 属性绑定到 ComboBoxItem ItemContainerStyle 中的“CanDisplayDetails”,因为上下文似乎是 Enum 而不是我的 ViewModel。如何更正绑定以便它可以提供枚举值,但 IsEnabled 绑定到我的 ViewModel 属性?而且它也只需要影响“视图”ComboBoxItem。谢谢!

到目前为止的风格想法:

<ComboBox.ItemContainerStyle>
    <Style TargetType="{x:Type ComboBoxItem}">
        <Setter Property="IsEnabled" Value="{Binding CanDisplayDetails}"/>
    </Style>
</ComboBox.ItemContainerStyle>

在 XAML 用户控件的资源中:

<ObjectDataProvider x:Key="ActionsEnum" MethodName="GetValues" ObjectType="{x:Type sys:Enum}">
        <ObjectDataProvider.MethodParameters>
            <x:TypeExtension TypeName="Constants:InvoiceActionsLong"/>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>

同一个UserControl中的ComboBox:

<ComboBox Grid.Column="1" ItemsSource="{Binding Source={StaticResource ActionsEnum}}" IsEnabled="{Binding SelectedItem, Converter={StaticResource SelectionConverter}}"
                      SelectedItem="{Binding SelectedAction}">
    <ComboBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding Converter={StaticResource EnumDescriptionConverter}}" />
        </DataTemplate>
    </ComboBox.ItemTemplate>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged">
            <i:InvokeCommandAction Command="{Binding ActionCommand}"/>
        </i:EventTrigger>
    </i:Interaction.Triggers>
</ComboBox>

EnumDescriptionConverter:

public class EnumDescriptionConverter : IValueConverter
{
    /// <summary>
    /// Get enum description
    /// </summary>
    /// <param name="enumObj"></param>
    /// <returns></returns>
    private string GetEnumDescription(Enum enumObj)
    {
        FieldInfo fieldInfo = enumObj.GetType().GetField(enumObj.ToString());

        object[] attribArray = fieldInfo.GetCustomAttributes(false);

        if (attribArray.Length == 0)
        {
            return enumObj.ToString();
        }
        else
        {
            DescriptionAttribute attrib = attribArray[0] as DescriptionAttribute;
            return attrib.Description;
        }
    }

    /// <summary>
    /// Returns an enum member's description
    /// </summary>
    /// <param name="value"></param>
    /// <param name="targetType"></param>
    /// <param name="parameter"></param>
    /// <param name="culture"></param>
    /// <returns></returns>
    object IValueConverter.Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        Enum myEnum = (Enum)value;
        string description = GetEnumDescription(myEnum);
        return description;
    }

    object IValueConverter.ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return string.Empty;
    }
}

枚举:

/// <summary>
/// Enum lists all available Actions for Action Combo Box at the Invoice level for DETAIL types ie. active, plan
/// </summary>
public enum InvoiceActionsLong
{
    [Description("Contacts")]
    Contacts,
    [Description("Delivery")]
    Delivery,
    [Description("Documents")]
    Documents,
    [Description("Note")]
    Note,
    [Description("Payments")]
    Payments,
    [Description("Print")]
    Print,
    [Description("Process")]
    Process,
    [Description("Reload")]
    Reload,
    [Description("Send")]
    Send,
    [Description("View")]
    View
}

我的 ViewModel 中的属性:

/// <summary>
/// Bool denotes whether can display details
/// </summary>
private bool CanDisplayDetails
{
    get
    {
        bool b = true;

        if (SelectedItem != null)
        {
            if (SelectedItem.AdjustmentTypeID == 1)
            {
                b = false;
            }
        }

        return b;
    }
}

【问题讨论】:

    标签: c# wpf xaml mvvm enums


    【解决方案1】:

    Style 绑定使用的数据上下文是 ComboboxItem(这将是枚举元素本身)的数据上下文,而不是您希望的视图模型。

    尝试在您的绑定上使用相对源来遍历逻辑树并获取 ComboBox 的数据上下文,而不是 ComboBoxItem

    <Setter Property="IsEnabled" Value="{Binding DataContext.CanDisplayDetails, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ComboBox}}}"/>
    

    您可以通过检查 Visual Studio 输出窗口自行诊断。您会看到一个绑定错误,告诉您类似的信息;

    Data Error : CanDisplayDetails property cannot be found on a _InvoiceActionsLong_ instance

    编辑

    为了让这仅适用于您的 View 项目,请考虑;

    • 自定义StyleSelector 将只应用此样式是ComboBoxItem 的内容是View。将 ComboBox.ItemContainerStyleSelector 属性设置为选择器的一个实例。
    • 一个IMultiValueConverter 实现,用于绑定到CanDisplayDetails 和枚举值。如果值不是 View,此转换器将始终返回 true。

    为什么会这样?

    生成的ItemSelector 项目(如ComboBoxItem)的数据上下文自动设置为项目所代表的数据。

    【讨论】:

    • @Gusdor-这完全是我所缺少的,除了:我如何才能让它只对“视图”组合框项目而不是所有这些项目进行操作?
    • 对于任一路线,我如何告诉 styleSelector 或转换器该项目是“视图”?我开始制作转换器,但传递给它的对象是布尔值 CanDisplayDetails。
    • @FrederickVonStubenstein 对于转换器,在 xaml 绑定中使用 ConverterParameter,在 IValueConverter.Convert 实现中使用 parameter 参数。 StyleSelector 要求您在 ComboBox.ItemContainerStyleSelector 属性上设置选择器,您将收到 Select(object) 实现中的每个项目。
    • @Gusdor- 但是我在 xaml 绑定中为 ConverterParameter 放了什么?我试过 ConverterParameter={x:Static Constants:InvoiceActionsLong.View},但显然这总是导致参数为“View”。还尝试了 ConverterParameter={RelativeSource FindAncestor, AncestorType={x:Type ComboBoxItem}} 但不是这样。感谢您的帮助,我快到了!
    • @FrederickVonStubenstein 我给了你错误的信息。不能在转换器参数上设置绑定。请参阅此解决方案stackoverflow.com/questions/15309008/binding-converterparameter
    【解决方案2】:

    在@Gusdor 的帮助下,我终于解决了这个问题。我需要使用 MultiBinding 和 MultiConverter,如下所示。基本上,我将我的 ViewModel 属性“CanDisplayDetails”布尔值和 ComboBoxItem 本身都传递给 MultiConverter。如果传递的布尔值为 false,它会检查 ComboBoxItem 的内容是否是我的“视图”枚举值。如果这两个都返回 false,则禁用 ComboBoxItem。否则,它只返回 true,保持所有其他项目处于启用状态。感谢@Gusdor 的持续帮助,并为我指出正确的 SO 讨论!希望这会在未来对其他人有所帮助。

    编辑的组合框ItemContainerStyle:

    <ComboBox.ItemContainerStyle>
                    <Style TargetType="{x:Type ComboBoxItem}">
                        <Setter Property="IsEnabled">
                            <Setter.Value>
                                <MultiBinding Converter="{StaticResource IsViewItemConverter}">
                                    <Binding Path="DataContext.CanDisplayDetails" RelativeSource="{RelativeSource FindAncestor, AncestorType={x:Type ComboBox}}" />
                                    <Binding RelativeSource="{RelativeSource Self}"/>
                                </MultiBinding>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </ComboBox.ItemContainerStyle>
    

    新的 MultiConverter 而不是 IValueConverter:

    public class IsViewItemConverter : IMultiValueConverter
    {
        /// <summary>
        /// COnverter checks CanDisplayDetails and if ComboBoxItem is View Details, will disable if false and is View Details
        /// </summary>
        /// <param name="values">object array</param>
        /// <param name="targetType"></param>
        /// <param name="parameter"></param>
        /// <param name="culture"></param>
        /// <returns>true if should enabled item</returns>
        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
        {
            bool? b = values[0] as bool?;
            ComboBoxItem c = values[1] as ComboBoxItem;
            InvoiceActionsLong? item = c.Content as InvoiceActionsLong?;
    
            if (b == false && (InvoiceActionsLong)item == InvoiceActionsLong.View)
            {
                return false;
            }
            else
            {
                return true;
            }
        }
    
        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
        {
            throw new NotSupportedException();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2018-01-05
      • 1970-01-01
      • 2010-11-09
      • 1970-01-01
      • 1970-01-01
      • 2011-11-02
      • 2011-03-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多