C# 事件、使用这些事件的方法和继承

C# 事件、使用这些事件的方法和继承,c#,wpf,mvvm,C#,Wpf,Mvvm,我正在创建一个在WPF中开发的软件的程序端架构,我设计的架构符合MVVM模式 出于许多原因(设计、一致性、可重用性、可维护性、可伸缩性等),我创建了类BaseViewModel,实现接口INotifyPropertyChanged和一些其他方法: public class BaseViewModel: INotifyPropertyChanged { private PropertyChangedEventHandler property_changed; public even

我正在创建一个在WPF中开发的软件的程序端架构,我设计的架构符合MVVM模式

出于许多原因(设计、一致性、可重用性、可维护性、可伸缩性等),我创建了类
BaseViewModel
,实现接口
INotifyPropertyChanged
和一些其他方法:

public class BaseViewModel: INotifyPropertyChanged
{
    private PropertyChangedEventHandler property_changed;
    public event PropertyChangedEventHandler PropertyChanged
    {
        add { property_changed += value; }
        remove { property_changed -= value; }
    }

    //Here several methods using PropertyChanged and easing the usage of ViewModels

    public BaseViewModel() { }
}
上述定义的类
BaseViewModel
用作应用程序的所有其他
ViewModel
的基类(或者至少是这样),例如:

public class SampleViewModel : BaseViewModel
{
    //private PropertyChangedEventHandler property_changed;
    //public event PropertyChangedEventHandler PropertyChanged
    //{
    //    add { property_changed += value; }
    //    remove { property_changed -= value; }
    //}

    public String Name
    {
        get { return name; }
        set
        {
            if(value != name)
            {
                 name = value;
                 var handler = PropertyChanged;
                 if(handler != null)
                 {
                    handler(this, new PropertyChangedEventArgs("Name"));
                 }
            }
        }
    }
    private String name = "";

    public SampleViewModel ()
        : base() { }
}
我使用类
SampleViewModel
作为
SampleUserControl
DataContext
,它显示了一个
dependencProperty

public partial class SampleUserControl : UserControl
{
    #region ViewModel
    public SampleViewModel ViewModel
    {
        get { return view_model; }
    }
    private SampleViewModel view_model = new SampleViewModel();
    #endregion

    #region DependencyProperty
    public String Text
    {
        get { return (String)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }
    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("Text", typeof(String), typeof(SampleUserControl),
                new FrameworkPropertyMetadata(String.Empty, FrameworkPropertyMetadataOptions.AffectsRender,
                        new PropertyChangedCallback(TextPropertyChangedCallback)));
    private static void TextPropertyChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        SampleUserControl sender = d as SampleUserControl;
        if (sender != null)
        {
            sender.ViewModel.Name = (String)e.NewValue;
        }
    }
    #endregion

    public SampleUserControl()
    {
        InitializeComponent();
        LayoutRoot.DataContext = ViewModel;
        ViewModel.PropertyChanged += new PropertyChangedEventHandler(ViewModel_PropertyChanged);
    }

    void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        SampleViewModel viewmodel = sender as SampleViewModel;
        if (viewmodel != null)
        {
            switch (e.PropertyName)
            {
                case "Name":
                    SetValue(TextProperty, viewmodel.Name);
                    break;
                default:
                    break;
            }
        }
    }
}
总之,与
SampleUserControl
相关的数据包含在三个位置:
SampleViewModel
的实例、
TextProperty
内以及
SampleUserControl
的xaml部分的
TextBox
的属性内(此属性
文本
通过
绑定
视图模型
名称
字段双向绑定)

为了同步这三个值,我添加了方法
TextPropertyChangedCallback
ViewModel\u PropertyChanged
,用于更新需要更新的字段

上面的代码可以工作,上面提到的三个位置保持最新,事件触发等等,当数据绑定使用
SampleUsercontrol
时,一切正常

但是
SampleViewModel
触发事件
BaseViewModel.PropertyChanged
,并且由于
BaseViewModel
被广泛使用,我希望每个
ViewModel
都有自己的事件
PropertyChanged
,至少为了避免事件重叠

因此,我取消了对
SampleViewModel
的代码的注释,从而重新定义了事件
PropertyChanged
,但它破坏了
SampleViewModel
实例的
Name
字段与
SampleUserControl
TextProperty
属性之间的同步

我在构思方面是否犯了一些错误? 你对我有什么指导吗? 对于继承自
BaseViewModel
的每个ViewModel,定义不同事件
PropertyChanged
的最佳经济方式是什么,同时仍然使用该基类中定义的通用方法(此类方法使用
PropertyChanged
)? (我希望避免复制粘贴大量代码。)

我知道这更多的是关于优化,但是这样的优化可以在慢软件和快软件之间产生区别。我正处于代码分解阶段,所以我喜欢形状优美、优雅且经过分解的代码

最后,我可能会错过一些显而易见的解决方案

提前感谢您提供的任何线索,
Julien

TL;DR:基本上,我会再次检查您是否在该用户控件上正确执行DC/DP,并抛弃任何关于
属性更改的多个定义的概念

详细内容:

  • 您在基类中定义了
    PropertyChanged
    ,这很好。没有理由在任何其他地方重新定义它。。实际上,这样做只是自找麻烦
  • 与此相关的是,您实际上应该只创建一个方法来执行事件调用,而不是在setter.Insta中执行整个
    处理程序
    位来减少复制粘贴
  • 您必须使用
    TextPropertyChanged
    这一事实在这里是一个巨大的危险信号。这与实际问题有关,即您可能正在滥用依赖项属性。DPs用于允许父控件绑定到用户控件的属性。您通常不会将它们与用户控件内部的数据上下文结合使用e控制,因为正如您所看到的,保持它们的同步是一场噩梦
  • 一般来说,如果用户控件被设置为独立于任何其他控件(即子视图),那么用户控件应该只有自己的数据上下文。如果用户控件只是一个花哨的控件,那么给它们一个视图模型很难得到任何东西

  • 1.不同意。我不希望对实际上是横切关注点的东西进行继承,尝试对
    INPC
    使用AOP(请参见PropertyChanged.Fody)@Aron那很好,如果你不想要一个基础视图模型,我不会和你争论。也就是说,你不应该在基础类和派生类中都定义它,因为基础类会被隐藏,并可能会给你带来各种奇怪的问题。你说的“TL”、“DR”和“DC/DP”是什么意思?第2点是什么意思?(可能我已经在上面提到的方法中实现了它,但没有显示。)关于第3点和第4点,我有我的理由,噩梦对我来说已经结束了,因为事情与上面的代码是同步的。关于第1点,我已经告诉过每个ViewModel有一个事件可以帮助我管理事件,而接受这个挑战是本文的目的。我不知道如何构思事件管理器否则,@JulienFertéTL;DR“=”太长,没有阅读”.DC=DataContext,DP=DependencyProperty。在第二个方面,我是说你的属性设置器中有一些样板文件,关于你对第1点、第3点和第4点的响应,可以放在一个方法中。事实上,你做错了,让它工作了,并且发现了这种方法的一些问题,这应该告诉你改变你做这件事的方式。All你现在的做法是积累技术债务。我做错了什么?做我现在做的事情的更好方法是什么?