4

Imagine a Control is being created.

It has some DependencyProperties and there are some properties which depend on the DependencyProperties, but doesn't need to be seen or updated from the outside.


For example: Foo is a DependencyProperty (that can be both read and write from the outside) and Bar is a common property that will be updated when (and only when) Foo changes.

public class MyControl : Control
{
    private static DependencyProperty FooProperty = 
        DependencyProperty.Register("Foo", 
                                    typeof(int), 
                                    typeof(MyControl), 
                                    new PropertyMetadata(FooChanged));

    private static void FooChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        MyControl changedObject = d as MyControl;

        changedObject.Bar++;
    }

    // The dependency property
    public int Foo { get; set; }

    // The property that is changed (only) when the dependency property changes
    public int Bar { get; private set; }
}

However, Bar's value is displayed in the screen (a Binding is established in the control's template). But since Bar is not a DependencyProperty no notification is sent to say that it's value has changed.

I see two options to solve this issue (but I'm open to suggestions).

  1. Make my Control implement INotifyPropertyChanged

or

  1. Make Bar a readonly DependencyProperty

What are the pros and cons of each approach? How should I proceed?

Is it a bad practice to implement INotifyPropertyChanged on a Control?

@EDIT

This questions was raised when I was creating a control that filters and displays a collection of games.

The data of this collection is "constant" (the games are loaded with the app and doesn't change to the end of the execution). However, the user can apply filters to this collection (for example, only displaying games that are in English)

As you can imagine, the filters are the Dependency Properties, while the collection of games is a simple property, that I wasn't sure if I should turn into a ´DependencyProperty´ (since it would always be the same, only filtered according to the ComboBoxes)

  • 2
    See stackoverflow.com/q/12466216. INotifyPropertyChanged is typically implemented in the ViewModel, not the View. – Robert Harvey Dec 31 '17 at 21:42
  • 2
    two answers here, one says do INotifyProperty changed, the other make it a DependencyProperty :/ stackoverflow.com/questions/2480366/… – Ewan Jan 1 '18 at 10:49
  • 3
    I think the answer is WPF still isn't completely sure what its doing – Ewan Jan 1 '18 at 10:51
  • 1
    @appayipyip too little for a proper answer but I'd say...it depends on what bar and foo are. If you want to expose a calculated property which, by case, will be binded and displayed then go with a DependencyProperty (think for example about ActualWidth). The point is that the property is not intended for visualization. If, instead, it's something that belongs only to visualization then it definitely must stay in the ViewModel (and yes, it's a bad practice to put it in the control). – Adriano Repetti Jan 9 '18 at 10:22
  • 1
    Questions arise when it's intended for visualization but it's used only inside the reusable Control...where to put it? If in ViewModel you then put a strong requirement on the data context associated with the control. If that's the case then you may introduce a local private ViewModel (especially if you expect more use cases) but it's not a general answer you can get without more context. – Adriano Repetti Jan 9 '18 at 10:27
1
+50

Your edit seems to change the perspective on things. The bottom line is that if you want the UI to react to changes in your control, you have to tell it that things changed. Based on a few years of WPF work, I've developed a few guidelines:

Collections

Use ObservableCollection as your type. They notify the UI when values are added and removed.

Read-only properties can be simple properties:

public ObservableCollection Games { get; }

Otherwise use DependencyProperty and ObservableCollection together

public static DependencyProperty GamesProperty = DependencyProperty.Register(...);

public ObservableCollection Games
{
    get { return GetValue(GamesProperty) as ObservableCollection; }
    set { SetValue(GamesProperty, value); }
}

Properties on DependencyObjects (includes Controls)

Never bind to a simple property. There are old bugs in WPF that have not been fixed that cause memory leaks in this case. Make everything a DependencyProperty.

DependencyObjects have their own set of rules, so any property used in the interaction really needs to be a DependencyProperty. It's a pain in the butt, but it will save you headaches later.

Properties on Models/ViewModels

Anything that you intend to update on screen needs to implement INotifyPropertyChanged. Those properties don't have the same memory leak as a simple property.

(More Information)

  • So, in this case, I should not use a simple readonly (ObservableCollection) property (since it may cause memory leaks)? – appa yip yip Jan 9 '18 at 19:35
  • You have a DependencyObject and your property is an ObservableCollection. You'll be fine. it's when you try to bind to a plain ol' C# object that doesn't have any notification features that you get memory leaks. – Berin Loritsch Jan 9 '18 at 19:52
4

A Control is the View part of MVVM. What you are updating in a View is dumb info (even if you have a complex control).

Generally, when you are creating a control is because:

  • You need to render a lot of elements with little model info, it makes your main view to be bloated, and you cannot find the model's info you are using for that main view.
  • You want to reuse some elements that happen to be together more than once. Perhaps is a title where is user's name in a place and category name in another place.

Thereby, your control must be agnostic about when model's property changes. What you are looking for is the DependencyProperty.

Conversely, the INotifyPropertyChanged must be used in the ViewModel part of MVVM because it's the one who knows when:

  • Business info (i. e. user's name) is changed in the Model.
  • Dumb info (i.e. text) is changed in the View.
  • 1
    -1. Display logic should be handled in the View, not the VM. The incorrect assumption here is that all logic is business logic. – Josh Noe Mar 13 at 18:13
  • I agree with you on that, but I'm afraid I don't know how it contradicts what was said in the answer. Perhaps, I misunderstood the question somehow – A Bravo Dev Mar 20 at 20:58
1

What are the pros and cons of each approach? How should I proceid?

1 - Make my Control implement INotifyPropertyChanged

INotifyPropertyChanged is not a specific part of WPF. Therefore, if you want a simple publish/subscribe feature this interface is a good and standard way that .NET framework provides to do it.

2 - Make Bar a readonly DependencyProperty

In my understanding, you only want the Bar property to be "published" for the client (in your case the Control template) to update itself. Therefore, you don't need a DependencyProperty in this case, because you don't want to bind it to something else like a style, template or another runtime variable.

In this case, it would work, but it seems like a workaround (because you're implementing in the control) to accomplish your feature.

3 - Another option

I'm not sure if this is applicable because your example is too general, but I suggest a third option:

  • Make your control as dumb as possible;
  • Make Foo and Bar DependencyProperties;
  • Create a ViewModel as a DataContext to your control
  • Provide the values for Foo and Bar by making your ViewModel implement INotifyPropertyChanged interface

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.