[英]How to bind ComboBox properly in WPF?
I'm new to WPF and MVVM and I'm developing a test WPF application following the MVVM design pattern. 我是WPF和MVVM的新手,并且正在按照MVVM设计模式开发测试WPF应用程序。 My database has 2 entities, Cards and Departments. 我的数据库有2个实体,卡和部门。 Any card can have only 1 department, so it's a one-to-many relationship. 任何一张卡只能有1个部门,因此是一对多关系。
I've created the following ViewModel in order to bind to the view: 我创建了以下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
}
The CardForms' datacontext is currently being set to an instance of the CardViewModel in the code where the CardForm is being instantiated, but I'm going to create a IoC container or dependency injections down the line. CardForms的datacontext当前正在实例化CardForm的代码中设置为CardViewModel的实例,但是我将在此行下创建一个IoC容器或依赖项注入。
Everything binds correctly except for the ComboBox that should contain all departments and that has the current department in the Card instance selected (card.Department). 除了应该包含所有部门并且在Card实例中选择了当前部门(card.Department)的ComboBox之外,其他所有东西都正确绑定。 Here's the XAML for the ComboBox: 这是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}" />
The departments are displayed in the combobox, but the current department of the card isn't and if I try to change it I get and error saying "Cannot add an entity with a key that is already in use". 部门显示在组合框中,但是卡的当前部门不是,如果尝试更改它,则会出现错误,提示“无法使用已使用的密钥添加实体”。
So, my question is, how do I bind this combobox correctly to my ViewModel? 因此,我的问题是,如何将该组合框正确绑定到ViewModel?
PS I know populating the ObservableCollection<Department>
in the ViewModel is probably not the right way to do it, but I could not think of a better way at the time. PS我知道在ViewModel中填充ObservableCollection<Department>
可能不是正确的方法,但是我当时想不到更好的方法。 If you have any suggestions for this also, please let me know. 如果您对此有任何建议,请告诉我。
Additionally, this is the Card model: 此外,这是Card模式:
[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");
}
}
}
I edited the constructor in the CardViewModel
to take the DataContext
as a parameter and that did it. 我在CardViewModel
编辑了构造函数,以将DataContext
作为参数,然后做到了。 This is the new CardViewModel
constructor: 这是新的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);
}
Had to do a bit of research on this myself. 不得不对此做一些研究。 Thought I would contribute with a self answered question, but found this open current question... 我以为我会回答一个自我回答的问题,但是发现了这个开放的当前问题...
The ComboBox
is designed to be a kind of textbox that restricts it's possible values to the contents of a given list. ComboBox
设计为一种文本框,将其可能的值限制为给定列表的内容。 The list is provided by the ItemsSource
attribute. 该列表由ItemsSource
属性提供。 The current value of the ComboBox
is the SelectedValue
property. ComboBox
的当前值是SelectedValue
属性。 Typically these attributes are bound to relevant properties of a corresponding ViewModel. 通常,这些属性绑定到相应ViewModel的相关属性。
The following example shows wired ComboBox together with a TextBox
control used to redundantly view the current value of the ComboBox
by sharing a view model property. 下面的示例显示了有线的ComboBox和TextBox
控件,该TextBox
用于通过共享视图模型属性来冗余地查看ComboBox
的当前值。 (It is interesting to note that when TextBox changes the shared property to a value outside the scope of the ComboBox
's list of values, the ComboBox
displays nothing.) (有趣的是,当TextBox将shared属性更改为ComboBox
值列表范围之外的值时, ComboBox
不会显示任何内容。)
Note: the following WPF/C# example does does use code-behind and so presents the ViewModel as merely the datacontext of the view and not a partial class
of it, a current implementation constraint when using WPF with F#. 注意:以下WPF / C#示例确实使用了代码隐藏功能,因此将ViewModel仅仅表示为视图的数据上下文,而不是视图的partial class
,这是将WPF与F#一起使用时的当前实现约束。
WPF XAML 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 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;
}
}
By default, ToString() is used to interpret the objects in the list. 默认情况下,ToString()用于解释列表中的对象。 However, ComboBox
offers DisplayMemberPath
and SelectedValuePath
attributes for specifying paths to specific object properties for corresponding displayed and stored values. 但是, ComboBox
提供了DisplayMemberPath
和SelectedValuePath
属性,用于为相应的显示和存储的值指定特定对象属性的路径。 These paths are relative to the list object element so a path of "Name" refers to Name on a list object item. 这些路径是相对于列表对象元素的,因此“名称”路径是指列表对象项上的名称。
The "Remarks" section of this MSDN link explains the interpretations of the IsEditable
and IsReadOnly
ComboBox
properties. 此MSDN链接的“备注”部分说明了IsEditable
和IsReadOnly
ComboBox
属性的解释。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.