我正在尝试构建一个简单的C#/ WinFoms项目,该项目根据结构使用Model-View-ViewModel设计模式:
两个UserControl和关联的ViewModel之间的数据绑定不能很好地工作。
MainForm
包含两个UserControls(UC):Uc_Create
,Uc_Iteration
。
每个UC都包含一个组合框,它连接到ViewModel_xxx中的相关属性,即
Uc_Create 有:
this.comboBox1ComplexCreate.DataSource = oVM_Create.VM_Create_ListOfStringsInModel;
Uc_Iteration 有:
this.comboBox1ComplexIteration.DataSource = oVM_Iteration.VM_Iteration_ListOfStringsInModel;
问题:
当我将元素添加到VM_Iteration_ListOfStringsInModel
相应的UC(comboBox1ComplexCreate
)和中的组合框时,模型中的列表已正确更改但 comboBox1ComplexIteration
中的其他组合框(Uc_Iteration
)不是!
为什么????
如果我将模型中的List
更改为BindingList
,一切正常。我做错了什么?
提前致谢!
型号:
namespace Small_MVVM
{
public class Model
{
private static readonly object m_oLock = new object();
private static Model instance;
public List<string> simplelistOfStrings;
private Model()
{
simplelistOfStrings = new List<string>();
}
public static Model GetInstance()
{
if (instance == null)
{
lock (m_oLock)
{
if (instance == null)
{
instance = new Model();
}
}
}
return instance;
}
}
}
ModelView_Create :
namespace Small_MVVM
{
class ViewModel_Create : NotifyPropertyChangedBase
{
private static Model oModel = Model.GetInstance();
private BindingList<string> _VM_Create_ListOfStringsInModel = new BindingList<string>(oModel.simplelistOfStrings);
public BindingList<string> VM_Create_ListOfStringsInModel
{
get
{
return _VM_Create_ListOfStringsInModel;
}
set
{
_VM_Create_ListOfStringsInModel = value;
this.FirePropertyChanged(nameof(VM_Create_ListOfStringsInModel));
}
}
}
}
ModelView_Iteration :
namespace Small_MVVM
{
class ViewModel_Iteration : NotifyPropertyChangedBase
{
private static Model oModel = Model.GetInstance();
public ViewModel_Iteration()
{
}
BindingList<string> _VM_Iteration_ListOfStringsInModel = new BindingList<string>(oModel.simplelistOfStrings);
public BindingList<string> VM_Iteration_ListOfStringsInModel
{
get
{
return _VM_Iteration_ListOfStringsInModel;
}
set
{
_VM_Iteration_ListOfStringsInModel = value;
this.FirePropertyChanged(nameof(VM_Iteration_ListOfStringsInModel));
}
}
}
}
这是实现NotifyPropertyChangedBase
接口的抽象类INotifyPropertyChange
:
public abstract class NotifyPropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool CheckPropertyChanged<T>(string propertyName, ref T oldValue, ref T newValue)
{
if (oldValue == null && newValue == null)
{
return false;
}
if ((oldValue == null && newValue != null) || !oldValue.Equals((T)newValue))
{
oldValue = newValue;
return true;
}
return false;
}
private delegate void PropertyChangedCallback(string propertyName);
protected void FirePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
答案 0 :(得分:6)
如果您正在关注MVVM模式,那么您应该使用ObservableCollection
来收集如下集合
private ObservableCollection<int> _intList;
public ObservableCollection<int> IntList
{
get
{
return _intList;
}
set
{
_intList= value;
_intList.CollectionChanged +=
new System.Collections.Specialized.NotifyCollectionChangedEventHandler
(MyProperty_CollectionChanged);
}
}
void MyProperty_CollectionChanged(object sender,
System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
NotifyPropertyChanged("IntList");
}
ObservableCollection - 表示动态数据集合,在添加,删除项目或刷新整个列表时提供通知。
你也可以查看:MVVM (Model-View-ViewModel) Pattern For Windows Form Applications, using C#
答案 1 :(得分:4)
我认为您的问题是两个BindingList之间没有链接。
将项目添加到VM_Iteration_ListOfStringsInModel时,更改将传播到模型中绑定的组合框和基础列表。但是模型中的列表无法将这些更改进一步传播到VM_Create_ListOfStringsInModel,因为它不支持此功能。 VM_Create_ListOfStringsInModel将包含您添加到VM_Iteration_ListOfStringsInModel的项目,但它不会引发ListChanged事件,因为模型中的List会中断事件链。 List无法引发ListChanged事件。这就是BindingList的用途。
正如您已经尝试过的,当您通过BindingList替换模型中的列表时,它可以正常工作。
所以你已经提到过一个解决方案,另一个解决方案就是只为这两个组合框使用一个共享的BindingList,如果这是你的选择。
private void ListDemo()
{
var l = new List<string>();
l.Add("A");
BindingList<string> blist1 = new BindingList<string>(l);
BindingList<string> blist2 =new BindingList<string>(l);
blist1.ListChanged += Blist1_ListChanged;
blist2.ListChanged += Blist2_ListChanged;
blist1.Add("B");
// at this point blist1 and blist2 items count is 2 but only blist1 raised ListChanged
}
private void Blist2_ListChanged(object sender, ListChangedEventArgs e)
{
//No event fired here when adding B
}
private void Blist1_ListChanged(object sender, ListChangedEventArgs e)
{
// event fired when adding B
}
答案 2 :(得分:3)
ViewModel_Iteration
和ViewModel_Create
都通过初始化VM_Iteration_ListOfStringsInModel
使用VM_Create_ListOfStringsInModel
的新对象来定义其属性(BindingList
和model.simplelistOfStrings
)作为指定的源列表。因此,两个ViewModel都有ListOfStringsInModel
的不同对象---它们不指向同一个对象。
当然,您应该将simplelistOfStrings
属性定义为BindingList
,以便在View
和code behind
中建立双向数据绑定机制。但是,我建议您不要在ViewModel_Iteration
和ViewModel_Create
ViewModel中定义新的成员变量,而应按如下方式更改属性定义:
public BindingList<string> VM_Iteration_ListOfStringsInModel
{
get
{
return oModel.simplelistOfStrings;
}
set
{
oModel.simplelistOfStrings = value;
this.FirePropertyChanged(nameof(VM_Iteration_ListOfStringsInModel));
}
}
并且
public BindingList<string> VM_Create_ListOfStringsInModel
{
get
{
return oModel.simplelistOfStrings;
}
set
{
oModel.simplelistOfStrings = value;
this.FirePropertyChanged(nameof(VM_Create_ListOfStringsInModel));
}
}
上述方法的另一个改进是根本不在所有属性定义中使用该集合。需要通知,因为在分配新列表时,属性的参考将发生变化。因此,不要允许设置新列表,而是使用列表中Clear
和Add
个新项目的方法。这种方式对属性的引用将保持不变,并且双向绑定可以在不显示明确通知的情况下工作。
<强> ViewModel_Iteration 强>
public BindingList<string> VM_Iteration_ListOfStringsInModel
{
get
{
return oModel.simplelistOfStrings;
}
}
<强> ViewModel_Create 强>
public BindingList<string> VM_Create_ListOfStringsInModel
{
get
{
return oModel.simplelistOfStrings;
}
}
<强>用法强>
VM_Create_ListOfStringsInModel.Clear();
VM_Create_ListOfStringsInModel.Add(item); // Or use AddRange to add Range.