MVVM中的双向数据绑定问题

时间:2018-04-14 15:25:39

标签: c# winforms design-patterns mvvm

我正在尝试构建一个简单的C#/ WinFoms项目,该项目根据结构使用Model-View-ViewModel设计模式:

enter image description here

两个UserControl和关联的ViewModel之间的数据绑定不能很好地工作。

MainForm包含两个UserControls(UC):Uc_CreateUc_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));
        }
    }
}

3 个答案:

答案 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_IterationViewModel_Create都通过初始化VM_Iteration_ListOfStringsInModel使用VM_Create_ListOfStringsInModel的新对象来定义其属性(BindingListmodel.simplelistOfStrings)作为指定的源列表。因此,两个ViewModel都有ListOfStringsInModel的不同对象---它们不指向同一个对象。

当然,您应该将simplelistOfStrings属性定义为BindingList,以便在Viewcode behind中建立双向数据绑定机制。但是,我建议您不要在ViewModel_IterationViewModel_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));
            }
        }

上述方法的另一个改进是根本不在所有属性定义中使用该集合。需要通知,因为在分配新列表时,属性的参考将发生变化。因此,不要允许设置新列表,而是使用列表中ClearAdd个新项目的方法。这种方式对属性的引用将保持不变,并且双向绑定可以在不显示明确通知的情况下工作。

<强> 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.