【问题标题】:SelectedItem binding on ComboBox not showing selected valueComboBox 上的 SelectedItem 绑定未显示所选值
【发布时间】:2016-05-11 19:59:38
【问题描述】:

我正在尝试构建一个设置页面,以允许用户选择在项目滑动时执行的操作,例如 Outlook 应用程序。

为此,我创建了一个包含可用操作的enum,并将其绑定到ComboBox

一切正常,用户可以选择动作并且他的选择被正确保存。问题是当我导航到页面时ComboBox 不显示所选项目,它仅在选择后显示。

这意味着如果用户更改选择,则 ComboBox 会更新,但在导航时所选项目显示为空白。

这是我的代码:

(XAML)

<ComboBox x:Uid="LeftActionComboBox"
          Grid.Row="0"                          
          HorizontalAlignment="Stretch"                          
          SelectedItem="{Binding LeftSwipeActionType, Mode=TwoWay, Converter={StaticResource StringToSwipeActionTypesConverter}}"
          ItemsSource="{Binding LeftSwipeActionType, Converter={StaticResource EnumToStringListConverter}}"/>

(虚拟机属性)

public SwipeActionTypes LeftSwipeActionType
{
    get { return _settings.LeftSwipeActionTypeProperty; }
    set
    {
        _settings.LeftSwipeActionTypeProperty = value;
        // RaisePropertyChanged causes a StackOverflow, but not using it is not the problem since the ComboBox is empty only before set
    }
}

(转换器StringToSwipeActionTypesConverter,本地化就绪)

// Returns localized string value for the Enum
public object Convert(object value, Type targetType, object parameter, string language)
{
    var enumValue = (SwipeActionTypes) value;
    switch (enumValue)
    {
        case SwipeActionTypes.Copy:
            return App.ResourceLoader.GetString("CopySwipeActionName");
        case SwipeActionTypes.Delete:
            return App.ResourceLoader.GetString("DeleteSwipeActionName");
        default:
            throw new ArgumentOutOfRangeException();
    }
}

// Parses the localized string into the enum value
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
    var stringValue = (string) value;
    if (stringValue.Equals(App.ResourceLoader.GetString("CopySwipeActionName")))
    {
        return SwipeActionTypes.Copy;
    }
    if (stringValue.Equals(App.ResourceLoader.GetString("DeleteSwipeActionName")))
    {
        return SwipeActionTypes.Delete;
    }
    return null;
}

(转换器EnumToStringListConverter

public object Convert(object value, Type targetType, object parameter, string language)
{
    var valueType = value.GetType();
    return Enum.GetNames(valueType).ToList();
}

public object ConvertBack(object value, Type targetType, object parameter, string language)
{
    return value;
}

知道为什么会失败吗?

【问题讨论】:

    标签: c# xaml combobox win-universal-app


    【解决方案1】:

    您收到 StackOverflow 异常的原因是因为每次更改 LeftSwipeActionType 属性时,您都在更改 ComboBox 的 ItemsSource,这会更改 SelectedItem 触发 INotifyPropertyChanged 会更改 ItemsSource 等等。

    一旦您停止对 ItemsSource 和 SelectedItem 使用相同的属性,就会设置正确的初始选择。

    与其使用转换器来创建 ItemsSource,不如在 ViewModel 中创建

    public MyViewModel(type enumType)
    {
        SourceForItems = Enum.GetValues(enumType);
    }
    
    public IEnumerable SourceForItems { get; private set; }
    

    【讨论】:

    • 标记为解决方案,因为它有效,即使它破坏了本地化。
    【解决方案2】:

    首先,您的方法有什么问题: 您将ItemsSource 绑定到与SelectedItem 相同的属性,即使您使用转换器也很困难,这可能会导致无限更新循环 - 您不希望这样。

    一遍又一遍地生成相同的静态元素列表似乎有点浪费。与其传递类型的实例,不如将类型本身传递给转换器:

    EnumToMembersConverter.cs

    public class EnumToMembersConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, string language)
        {
            return Enum.GetValues((Type)value).ToList();
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
            return DependencyProperty.UnsetValue;
        }
    }
    

    XAML

    ItemsSource="{Binding Source={x:Type whateverNamespace:SwipeActionTypes}, Converter={StaticResource EnumToMembersConverter}}"
    

    这将为您提供SwipeActionTypes 的所有,因此您可以直接绑定它,而无需再次转换回来。

    SelectedItem="{Binding LeftSwipeActionType, Mode=TwoWay}"
    

    将 ComboBox 用于字符串以外的类型并没有什么问题,所以让我们将其作为进一步步骤的基础:

    <ComboBox x:Uid="LeftActionComboBox"
          Grid.Row="0"                          
          HorizontalAlignment="Stretch"                          
          SelectedItem="{Binding LeftSwipeActionType, Mode=TwoWay}"
          ItemsSource="{Binding Source={x:Type whateverNamespace:SwipeActionTypes}, Converter={StaticResource EnumToMembersConverter}}"/>
    

    您编写所有这些转换的原因可能是因为ComboBox 显示了奇怪的值而不是可读的字符串。不用担心,我们已经有了您的转换器,您只需反转它(将SwipeActionTypes 转换为String)并将其应用于文本框:

    <ComboBox x:Uid="LeftActionComboBox"
          Grid.Row="0"                          
          HorizontalAlignment="Stretch"                          
          SelectedItem="{Binding LeftSwipeActionType, Mode=TwoWay}"
          ItemsSource="{Binding Source={x:Type whateverNamespace:SwipeActionTypes}, Converter={StaticResource EnumToMembersConverter}}">
        <ComboBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Path=., Converter = {StaticResource SwipeActionTypesStringConverter}}"/>
            </DataTemplate>
        </ComboBox.ItemTemplate>
    </ComboBox>
    

    请注意,我没有运行此代码,因此您可能需要相应地调整使用的命名空间

    【讨论】:

    • 现在无法测试,但您确定 Windows Universal 上有 x:Type 吗?因为这就是我必须绑定到同一个元素的原因。
    • 啊,不,没看到。但是,您可以在 ViewModel 中提供静态枚举值列表,或者忽略转换器中的 value 并始终使用 typeof(SwipeActionTypesStringConverter) 代替(不过,ViewModel 会更干净)
    猜你喜欢
    • 2020-01-24
    • 2013-09-19
    • 2015-06-28
    • 1970-01-01
    • 1970-01-01
    • 2019-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多