如何在WPF中正确绑定ComboBox?

时间:2014-06-19 15:50:18

标签: c# wpf xaml mvvm combobox

我是WPF和MVVM的新手,我正在按照MVVM设计模式开发测试WPF应用程序。我的数据库有2个实体,卡片和部门。任何一张卡只能有一个部门,所以它是一对多的关系。

我已创建以下ViewModel以绑定到视图:

public class CardViewModel : INotifyPropertyChanged
{
    public CardViewModel(Card card)
    {
        this.Card = card;

        SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
        builder.DataSource = ".\\SQLExpress";
        builder.InitialCatalog = "TESTDB";
        builder.IntegratedSecurity = true;

        SybaseDatabaseContext myDB = new SybaseDatabaseContext(builder.ConnectionString);

        var query = from d in myDB.Departments
                    select d;

        this.Departments = new ObservableCollection<Department>(query);
    }
    private Card _Card;
    private ObservableCollection<Department> _Departments;

    public Card Card
    {
        get { return _Card; }
        set
        {
            if (value != this._Card)
            {
                this._Card = value;
                SendPropertyChanged("Card");
            }
        }
    }

    public ObservableCollection<Department> Departments
    {
        get { return _Departments; }
        set
        {
            this._Departments = value;
            SendPropertyChanged("Departments");
        }
    }

    #region INPC
    // Logic for INotify interfaces that nootify WPF when change happens
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void SendPropertyChanged(String propertyName)
    {
        if ((this.PropertyChanged != null))
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    #endregion
}

CardForms&#39; datacontext当前正被设置为CardForm实例化的代码中的CardViewModel实例,但我将创建一个IoC容器或依赖注入。

除了ComboBox应该包含所有部门并且选择了Card实例中的当前部门(card.Department)之外,所有内容都正确绑定。这是ComboBox的XAML:

<ComboBox Height="23" HorizontalAlignment="Left" Margin="350,64,0,0" 
          Name="comboBoxDepartment" VerticalAlignment="Top" Width="120"
          IsSynchronizedWithCurrentItem="True"
          ItemsSource="{Binding Path=Departments}" 
          DisplayMemberPath="DepartmentName"
          SelectedItem="{Binding Path=Card.Department, Mode=TwoWay}" />

部门显示在组合框中,但卡的当前部门不是,如果我尝试更改它,我得到并且错误说&#34;无法添加具有已经存在的密钥的实体使用&#34;

所以,我的问题是,如何将这个组合框正确地绑定到我的ViewModel?

P.S。我知道填充ViewModel中的ObservableCollection<Department>可能不是正确的方法,但我当时想不出更好的方法。如果您对此有任何建议,请告诉我。

此外,这是卡片型号:

[Table(Name = "Card")]
public class Card : INotifyPropertyChanged, INotifyPropertyChanging
{
    private string _CardID;
    private string _Holder;
    private Int16? _DepartmentNo;

    [Column(UpdateCheck = UpdateCheck.WhenChanged)]
    public string CardID
    {
        get
        {
            return this._CardID;
        }
        set
        {
            if (value != this._CardID)
            {
                SendPropertyChanging();
                this._CardID = value;
                SendPropertyChanged("CardID");
            }
        }
    }

    [Column(UpdateCheck = UpdateCheck.WhenChanged)]
    public string Holder
    {
        get
        {
            return this._Holder;
        }
        set
        {
            if (value != this._Holder)
            {
                SendPropertyChanging();
                this._Holder = value;
                SendPropertyChanged("Holder");
            }
        }
    }

    [Column(CanBeNull = true, UpdateCheck = UpdateCheck.WhenChanged)]
    public Int16? DepartmentNo
    {
        get
        {
            return this._DepartmentNo;
        }
        set
        {
            if (value != this._DepartmentNo)
            {
                SendPropertyChanging();
                this._DepartmentNo = value;
                SendPropertyChanged("DepartmentNo");
            }
        }
    }

    private EntityRef<Department> department;
    [Association(Storage = "department", ThisKey = "DepartmentNo", OtherKey = "DepartmentNo", IsForeignKey = true)]
    public Department Department
    {
        get
        {
            return this.department.Entity;
        }
        set
        {
            Department previousValue = this.department.Entity;
            if (((previousValue != value)
                        || (this.department.HasLoadedOrAssignedValue == false)))
            {
                this.SendPropertyChanging();
                if ((previousValue != null))
                {
                    this.department.Entity = null;
                    previousValue.Cards.Remove(this);
                }
                this.department.Entity = value;
                if ((value != null))
                {
                    value.Cards.Add(this);
                    this._DepartmentNo = value.DepartmentNo;
                }
                else
                {
                    this._DepartmentNo = default(Nullable<short>);
                }
                this.SendPropertyChanged("Department");
            }
        }
    }

2 个答案:

答案 0 :(得分:0)

我在CardViewModel中编辑了构造函数,将DataContext作为参数进行了操作。这是新的CardViewModel构造函数:

public CardViewModel(Card card, SybaseDatabaseContext myDB)
{
    this.Card = card;

    var query = from d in myDB.Departments
                select d;

    this.Departments = new ObservableCollection<Department>(query);
}

答案 1 :(得分:0)

我必须自己做一些研究。以为我会用一个自我回答的问题做出贡献,但发现这个当前开放的问题......

ComboBox旨在成为一种文本框,将其可能的值限制在给定列表的内容中。该列表由ItemsSource属性提供。 ComboBox的当前值是SelectedValue属性。通常,这些属性绑定到相应ViewModel的相关属性。

以下示例显示了有线ComboBox以及用于通过共享视图模型属性冗余查看TextBox的当前值的ComboBox控件。 (值得注意的是,当TextBox将共享属性更改为ComboBox的值列表范围之外的值时,ComboBox不会显示任何内容。)

注意:以下WPF / C#示例确实使用了代码隐藏,因此将ViewModel仅显示为视图的datacontext而不是partial class,这是当使用带F#的WPF时的当前实现约束。

WPF XAML

<Window 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:m="clr-namespace:WpfApplication1"
  Title="MainWindow" Height="350" Width="525">
  <Window.DataContext>
    <m:MainWindowVM />
  </Window.DataContext>
  <StackPanel>
    <TextBox Text="{Binding SelectedString}" />
    <ComboBox ItemsSource="{Binding MyList}" SelectedValue="{Binding SelectedString}" />
  </StackPanel>
</Window>

C#ViewModel

using System.Collections.Generic;
using System.ComponentModel;
namespace WpfApplication1
{
  public class MainWindowVM : INotifyPropertyChanged
  {
    string selectedString;
    void NotifyPropertyChanged(string propertyName)
    {
      if (PropertyChanged == null) return;
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
    public string SelectedString
    {
      get { return selectedString; }
      set
      {
        selectedString = value;
        NotifyPropertyChanged("SelectedString");
      }
    }
    public List<string> MyList
    {
      get { return new List<string> { "The", "Quick", "Brown", "Fox" }; }
    }
    public event PropertyChangedEventHandler PropertyChanged;
  }
}

默认情况下,ToString()用于解释列表中的对象。但是,ComboBox提供DisplayMemberPathSelectedValuePath属性,用于指定相应显示和存储值的特定对象属性的路径。这些路径是相对于列表对象元素的,因此路径为&#34; Name&#34;引用列表对象项上的名称。

&#34;备注&#34; this MSDN link部分解释了IsEditableIsReadOnly ComboBox属性的解释。