lahsrah
lahsrah

Reputation: 9173

How to avoid a databinding / events hell on a complex screen?

This is more of an architecture / design question.

I have run into a few projects in the past written in WPF/Windows Forms, etc. that have complex screens with a lot of fields and these fields are connected to each other (their values depend on each other with some logic involved).

These projects I have taken on after they were implemented, and I found a lot of events / data bind hell - what I mean by this is that because all these fields are depending on others they have implemented INotifyPropertyChanged and other fields are being modified as a result. This causes the same fields being updated 5-6 times when the screen loads and the order in which fields are populated causes horrible bugs. (For example, Date was set before Job Type, instead of after Job Type, so I end up with a different Job Fee.)

To make matters worse, some hacks are implemented on UI events (for example, DropDown changed to update field X) while others are in the domain model that the UI binds to.

Basically, it's a huge mess, and I just want to know what the best way to implement something like this is if I was to start from scratch. Or is it a good idea to avoid such a complex screen in the first place?

Upvotes: 13

Views: 663

Answers (2)

hbarck
hbarck

Reputation: 2944

I would try to keep the business logic out of the property setters as much as possible.

First of all, if several properties are needed for one calculation, I'd write one method that does the calculation, and call that method when appropriate. E.g. if all different combinations of property values make sense, one could just call the method in the setters of each property, making sure that the same code runs any time one of the properties is changed. If you only can evaluate special combinations of property values, you could either implement a command and let the user decide when to calculate the resulting changes, or you could provide feedback through validation, and only evaluate the property changes if the combination is valid. If there are several interdependent properties, I often use a "ChangeInitiator" variable to indicate what property has changed, so that it is clear in the calculation method which property is responsible for the change and which others should change as a result. Basically, this is the same as doing one part of the calculation in each property setter, but I find that it helps me to keep an overview of things if the different parts of the relationship are all in one method.

In a program I wrote once, I had some calculations running on a background thread periodically, so I would just set a flag whenever a piece of data changed that required a new calculation, and do all the updates based on a timer every second or so... that could also help you get the logic more straight, and it avoids to have the calculation run several times for one set of related changes.

With regard to change notification, I'd really try to only use it for UI data binding.

Upvotes: 2

Fede
Fede

Reputation: 44038

We have fairly complex UIs (including several related fields of different types in, say for example a Row in a DataGrid) and the MVVM pattern has worked pretty well for us. All the properties coming from the Model and exposed to the View that have complex logic related are "wrapped" by an equivalent property in the ViewModel, which has no Backing Field, but rather points directly to the Model:

public class SomeComplexViewModel
{

    public SomeModel Model {get;set;}

    public string SomeCrazyProperty
    {
       get
       {
          return Model.SomeCrazyProperty;
       }
       {
          Model.SomeCrazyProperty = value;
          //... Some crazy logic here, potentially modifying some other properties as well.
       }
    }
}

<TextBox Text="{Binding SomeCrazyProperty}"/>

This removes the "initial value" problem, as the initial value read by the Binding is actually the real value coming from the Model, and thus the logic placed in the Setter is executed only when needed.

Then, for dummy properties (which have no logic behind), we bind directly from the View to the Model:

<TextBox Text="{Binding Model.SomeRegularProperty}"/>

This reduces the bloat in the ViewModel.

With regard to events in the code behind, I totally avoid that. My code behind files are almost always one InitializeComponent() and nothing else.

Only View-Specific logic is placed in the code behind (such as animations stuff, etc), when it cannot be directly done in XAML, or is easier to do in code (which is not the case most of the time).

Edit:

It's important to mention that the winforms binding capabilities are a joke compared to the XAML-based ones. could that be the cause you're seeing those horrible messes in those projects?

Upvotes: 0

Related Questions