【问题标题】:Two way data binding issue in a MVVMMVVM 中的两种方式数据绑定问题
【发布时间】:2018-04-14 15:25:38
【问题描述】:

我正在尝试根据结构构建一个使用模型-视图-视图模型设计模式的简单 C#/WinFoms 项目:

两个 UserControl 和关联的 ViewModel 之间的数据绑定不能正常工作。

MainForm 包含两个用户控件 (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));
            }
        }
    }
}

这是实现INotifyPropertyChange接口的抽象类NotifyPropertyChangedBase

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));
        }
    }
}

【问题讨论】:

  • 尝试将数据源绑定到viewmodel属性this.comboBox1ComplexCreate.Bindings.Add("DataSource", oVM_Create, "VM_Create_ListOfStringsInModel", true)
  • 感谢法比奥!不幸的是它仍然不起作用:(
  • 你试过 observablecollection 吗?

标签: c# winforms design-patterns mvvm


【解决方案1】:

如果你遵循 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#

【讨论】:

    【解决方案2】:

    我认为您的问题是您的两个 BindingLists 之间没有链接。

    当您向 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
        }
    

    【讨论】:

    • 将一个视图模型与两个视图共享是错误的做法。即使我们可以概念化或想象这一点。
    【解决方案3】:

    ViewModel_IterationViewModel_Create 都通过初始化 BindingList 的新对象来定义它们的属性(VM_Iteration_ListOfStringsInModelVM_Create_ListOfStringsInModel),该对象使用 model.simplelistOfStrings 作为指定的源列表。所以两个 ViewModel 都有不同的 ListOfStringsInModel 对象——它们不指向同一个对象。

    绝对应该将simplelistOfStrings 属性定义为BindingList,以便可以在Viewcode behind 中建立双向数据绑定机制。但是,与其在 ViewModel_IterationViewModel_Create ViewModels 中定义一个新的成员变量,我建议您更改属性定义如下:

            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.
    

    【讨论】:

      猜你喜欢
      • 2021-03-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-27
      • 2019-02-27
      • 1970-01-01
      • 1970-01-01
      • 2020-05-08
      • 1970-01-01
      相关资源
      最近更新 更多