简体   繁体   中英

Events, methods using these events and inheritance

I am creating the program-side architecture of a software developped in WPF, I designed the architecture as being compliant with the MVVM pattern.

For many sakes (design, coherence, reusability, maintainability, scalability, etc) I created the class BaseViewModel implementing the interface INotifyPropertyChanged and some other methods:

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() { }
}

The above-defined class BaseViewModel is used as a base class for all the other ViewModel s of the application (or, at least, is meant to be so), for example:

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() { }
}

I use the class SampleViewModel as the DataContext of SampleUserControl which bares a DependencyProperty :

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

To sum up, the data relative to SampleUserControl are contained at three locations : the instance of SampleViewModel , within TextProperty and within the property Text of a TextBox in the xaml part of SampleUserControl (this property Text is twoway-bound through Binding with the field Name of ViewModel ).

To synchronize the three values, I added the methods TextPropertyChangedCallback and ViewModel_PropertyChanged which update the fields which need to be updated.

The above code works and the three above-mentionned locations are kept up-to-date, events fire and so on, things are fine when SampleUsercontrol is consumed with data-binding.

But SampleViewModel fires the event BaseViewModel.PropertyChanged , and since BaseViewModel is meant to be extensively used, I would like each ViewModel to have its own event PropertyChanged , at least in order to avoid overlapping events.

So I uncomment the code of SampleViewModel thus redefining the event PropertyChanged but it breaks down the synchronization between the field Name of the instance of SampleViewModel and the property TextProperty of SampleUserControl .

Am I making some mistakes on the conception side? Do you have any guidance for me? What is the best economic way of defining a different event PropertyChanged for each ViewModel inheriting from BaseViewModel while still using the general-purpose methods defined within that base class (such methods use PropertyChanged )? (I would like to avoid having heavy pieces of code to copy-paste.)

I know that it is more about optimization, but such optimizations can make a difference between a slow software and a fast one. I am at the stage of code-factoring, so I fancy nicely-shaped, elegant and factorized code.

End of the day happening, I may miss some obvious solutions.

Thanks in advance for any clue, Julien

TL;DR: Basically, I would double-check that you are doing your DC/DP on that user control correctly, and toss out any concept of multiple definitions of PropertyChanged

In detail:

  1. You defined PropertyChanged in the base class, which is great. There is no reason to ever redefine it anywhere else . Really, you are just asking for trouble by doing this.
  2. Related to that, you should really just make a method to do the event invocation rather than doing the whole handler bit in the settter. Insta-reduction of copy paste.
  3. The fact that you are having to use TextPropertyChanged is a huge red flag here. Which relates to the real problem, that you are probably abusing your dependency property. DPs are used to allow parent controls to bind to a property of your user control. You typically won't use them in conjunction with a data context internal to the control because, as you have seen, keeping them in sync is a nightmare.
  4. In general, user controls should only have their own data context if they are set up to stand apart from any other control (ie, a sub-view). If they are just a fancy control, then giving them a view model rarely gets you anything.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM