如何数据绑定RibbonComboBox的SelectedItem

时间:2013-03-21 18:34:41

标签: wpf data-binding mvvm selecteditem ribboncontrolslibrary

我的问题基本上是this one。我认为提供更多信息和代码可以帮助您更轻松地重现问题。

使用来自RibbonControlsLibrary的Microsoft.Windows.Controls.Ribbon.RibbonComboBox感觉就像走过一个充满臭虫的大沼泽,而不是你知道如何绕过它做的事情。

Anywho。我遇到的最大问题是数据绑定我的SelectedItem。

以下是我的开始(在我发现了 RibbonGallery?之后)。要在ComboBox的子元素上使用ItemsSource和SelectedItem,甚至不在同一级别上已经给了我heebie-jeebies,但这似乎是正确的。

在示例应用程序中,我在ViewModel的构造函数中设置SelectedItem。但是,在运行应用程序时,不会显示SelectedItem。甚至VS设计师也正确地展示了“第二选择”!

正在运行应用:Running App VS设计师:Visual Studio Designer

调试SelectedItem setter时,您会注意到多次传递。在第一次将它设置为ctor中的“第二个选项”(1,参见下面的调试日志)后,它将重置为null(2)(通过外部代码,我在控件本身中计算)。在UI中打开下拉列表时,它将再次设置为null(3),然后在选择值时,将此值设置为两倍(4,5)。我选择了“第二选项”,然后用“第一选项”(6-9)重复该过程。这产生了以下日志(忽略了功能区控件中的一千零一个绑定异常......):

enter image description here

显然最大的问题是(2),这是重置我的初始选择。看起来当第一次显示控件时,它会被重置。一个非常丑陋的解决方法是通过计时器设置值。在用户控件的Loaded事件中设置它在这个示例应用程序中对我有用,但在我较重的现实应用程序中,它没有。无论如何,所有这些都是错误的。有谁知道更好的解决方案?

的Xaml:

<UserControl x:Class="WpfApplication1.RibbonComboBoxDemo"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:r="http://schemas.microsoft.com/winfx/2006/xaml/presentation/ribbon" 
             xmlns:local="clr-namespace:WpfApplication1"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">

    <UserControl.DataContext>
        <local:ViewModel />
    </UserControl.DataContext>

    <Grid>
        <r:Ribbon >
            <r:RibbonTab Header="First Tab">
                <r:RibbonGroup Header="Group">
                    <r:RibbonComboBox >
                        <r:RibbonGallery SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
                            <r:RibbonGalleryCategory ItemsSource="{Binding Controls}" DisplayMemberPath="Caption" />
                        </r:RibbonGallery>
                    </r:RibbonComboBox>
                </r:RibbonGroup>
            </r:RibbonTab>
            <r:RibbonTab Header="Second Tab" />
        </r:Ribbon>
    </Grid>
</UserControl>

视图模型:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;

namespace WpfApplication1
{
    public class ViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        public ObservableCollection<ControlBaseModel> Controls { get; private set; }


        private ControlBaseModel _selectedItem;
        public ControlBaseModel SelectedItem { get { return _selectedItem; } set { LogSelectedItemChange(value); _selectedItem = value; OnPropertyChanged("SelectedItem"); } }

        public ViewModel()
        {
            this.Controls = new ObservableCollection<ControlBaseModel>();

            this.Controls.Add(new ControlBaseModel() { Caption = "first option" });
            this.Controls.Add(new ControlBaseModel() { Caption = "second option" });

            this.SelectedItem = this.Controls[1]; // set to second option
        }

        int i = 0;
        private void LogSelectedItemChange(ControlBaseModel value)
        {
            i++;
            string setObject = "null";
            if (value != null)
            {
                setObject = value.Caption;
            }
            Debug.WriteLine(string.Format("{0}: SelectedItem.set(): {1}", i, setObject));
        }

    }

    public class ControlBaseModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }

        private string _name;
        public string Name { get { return _name; } set { _name = value; OnPropertyChanged("Name"); } }

        private string _caption;
        public string Caption { get { return _caption; } set { _caption = value; OnPropertyChanged("Caption"); } }
    }
}

2 个答案:

答案 0 :(得分:5)

虽然在我的应用程序中将ComboBox SelectedItem重置为null之前发生了View / UserControl加载事件,但ComboBox加载的事件实际上被触发了两次,第二次“迟到”就足够了。所以我现在的解决方案,我会乐于为更好的解决方案而努力,是这样的:

<r:RibbonComboBox>
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <i:InvokeCommandAction Command="{Binding LoadedCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <r:RibbonGallery SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
        <r:RibbonGalleryCategory ItemsSource="{Binding Controls}" DisplayMemberPath="Caption"/>
    </r:RibbonGallery>
</r:RibbonComboBox>

视图模型:

private ControlBaseModel _lastNonNullSelectedItem;

public ObservableCollection<ControlBaseModel> Controls { get; private set; }

private ControlBaseModel _selectedItem;
public ControlBaseModel SelectedItem 
{ 
    get { return _selectedItem; } 
    set 
    { 
        if (value != null) { _lastNonNullSelectedItem = value; } 
        _selectedItem = value; 
        OnPropertyChanged("SelectedItem"); 
    } 
}
public ICommand LoadedCommand { get; private set; }


public ViewModel()
{
    this.Controls = new ObservableCollection<ControlBaseModel>();
    this.LoadedCommand = new ActionCommand(OnLoaded); // ActionCommand: simple implementation of ICommand

    this.Controls.Add(new ControlBaseModel() { Caption = "first option" });
    this.Controls.Add(new ControlBaseModel() { Caption = "second option" });

    this.SelectedItem = this.Controls[1]; // set to second option
}

private void OnLoaded()
{
    this.SelectedItem = _lastNonNullSelectedItem;
}

答案 1 :(得分:2)

我最终只使用标准的ComboBox。

<ComboBox SelectedItem="{Binding Item}" ItemsSource="{Binding Items}"/>

如果您想要与RibbonComboBox相同(非常相似)的样式,请使用

<ComboBox IsEditable="True" IsReadOnly="True" SelectedItem="{Binding Item}" ItemsSource="{Binding Items}"/>