2

I'm a web and backend programmer by nature. Normally I try to avaoid making windows programs. Now I have to make a WPF client.

I have a background task that raises an event every often time. (It is working like a poller and when the criteria are met an event is raised). Noob as I am I wrote this code that was attached to the event to update the UI.

    private void IsDisconnectedEvent()
    {
            UserWindow.Visibility = Visibility.Hidden;
            DisconnectWindow.Visibility = Visibility.Visible;
    }

This gives an exception because I am not on the same thread. After some googling I found that I should change the code with:

    private void IsDisconnectedEvent()
    {
        Dispatcher.Invoke(() =>
                          {
                              UserWindow.Visibility = Visibility.Hidden;
                              DisconnectWindow.Visibility = Visibility.Visible;
                          });
    }

This works, but this is not the only event and thus makes my code horrible ugly. Are there better ways to do this?

Patrick
  • 2,730
  • 4
  • 33
  • 55
  • You could use a `BackgroundWorker`. Other than that, `Dispatcher.Invoke()` is the way to go. Also, you could wrap the Dispatcher call in a method, e.g. `PropagateChangesToUI(UIState newState) { Dispatcher.Invoke([...]); }` – Janis F Feb 19 '14 at 14:44
  • Sure, there are lots of mechanisms in place to make marshaling code to the UI thread easier. They are specialized for various tasks, so which you use will depend on what you're doing. If you're updating with progress, use `IProgress`, if you're invoking code after an interval of time, use a `DispatcherTimer`, if you're doing CPU bound work in a background thread you can use `BackgroundWorker`. So on so forth. – Servy Feb 19 '14 at 14:44

1 Answers1

15

Regarding this:

This works, but this is not the only event and thus makes my code horrible ugly

Yes, your WPF-based code will definitely be extremely horrible unless you understand and embrace The WPF Mentality.

Basically, all interactions between your custom logic (AKA Business logic or Application Logic) and the WPF UI should manifest in the form of Declarative DataBinding as opposed to the traditional imperative approach.

This means that there should be nothing like this:

UserWindow.Visibility = Visibility.Hidden;

anywhere in your code, simply because introducing things like that makes your code dependent on the UI and thus only executable on the UI thread.

Instead, the WPF approach to that would be to declaratively DataBind the Visibility propety of the UI element (IN XAML) to a relevant bool property that you can operate from the outside, like this:

<UserWindow Visibility="{Binding ShowUserWindow, Converter={my:BoolToVisibilityConverter}}">
   <!-- ... -->
</UserWindow>

Then, you would need to create a relevant class that contains the properties the UI is expecting to bind to. This is called a ViewModel.

Notice that in order to properly support Two-Way WPF DataBinding, your ViewModels must Implement the INotifyPropertyChanged interface.

When doing so, it is also convenient to have the PropertyChanged event from that interface marshalled to the UI thread, so that you no longer have to worry about setting the ViewModel's properties by using the Dispatcher.

Therefore our first step is to have all our ViewModels inherit from a class like this:

(taken from this answer):

public class PropertyChangedBase:INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        //Raise the PropertyChanged event on the UI Thread, with the relevant propertyName parameter:
        Application.Current.Dispatcher.BeginInvoke((Action) (() =>
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
        }));
    }
}

Once we have our Property Change Notification Dispatch to the UI Thread in place, we can proceed to create a relevant ViewModel that suits, in this case, the UserWindow and it's DataBinding expectations:

public class UserViewModel: PropertyChangedBase
{
    private bool _showUserWindow;
    public bool ShowUserWindow
    {
        get {return _showUserWindow; }
        set
        {
            _showUserWindow = value;
            OnPropertyChanged("ShowUserWindow"); //This is important!!!
        }
    }
}

Finally, you would need to set the Window's DataContext to an instance of it's corresponding ViewModel. One simple way to do that is in the Window's constructor:

public UserWindow() //Window's Constructor
{
    InitializeComponent();  //this is required.

    DataContext = new UserViewModel(); //here we set the DataContext
}

As you can see in this example, there is literally no need to manipulate the UI element's properties in procedural code. This is good not only because it resolves the Thread Affinity issues (because now you can set the ShowUserWindow property from any thread), but also because it makes your ViewModels and logic completely decoupled from the UI and thus testable and more scalable.

This same concept applies to EVERYTHING in WPF.

One detail that I need to mention is that I'm making use of a technique of Combining MarkupExtension and IValueConverter in order to reduce the the XAML boilerplate involved in using Converters.

You can read more about that in the link and also the MSDN DataBinding page linked above.

Let me know if you need further details.

Community
  • 1
  • 1
Federico Berasategui
  • 43,562
  • 11
  • 100
  • 154
  • 1
    -1 from me, its a nice answer but i do not agree that wpf is all about mvvm pattern. mvvm is a pattern, thats it, just a pattern. wpf may as well be used with mvp pattern, thats also just a pattern. of course wpf and mvvm like each other more than wpf and mvp but still i do not agree with you forcing mvvm pattern so much. its like saying all the people who develop or developed ui applications in winforms are fools and horses :) hehe – dev hedgehog Feb 19 '14 at 19:39
  • you dont know what mvp is? dude... no comment :) – dev hedgehog Feb 19 '14 at 19:58
  • @devhedgehog I was being sarcastic dude... I know what MVP is, but I ignore that completely because it's completely irrelevant to me.. do you understand better now? – Federico Berasategui Feb 19 '14 at 20:01
  • I thought so lol. seriously I support mvvm too but sometimes you should let the op use mvp/mvc too. static, small apps are all fine with mvp but big apps which have structured data and swap data context often like in views who edit emplyees/persons or whatever, those need mvvm for sure :) I ll change to + 1 but seriously I see you always pushing mvvm sometimes without a reason :) – dev hedgehog Feb 19 '14 at 20:07
  • 3
    The way you describe it sounds like what I'm looking for. With Web app's I build MVC applications with clear separation of concerns. I'll go read about MVVM and I think it will get me a lot further... – Patrick Feb 19 '14 at 20:55
  • Great answer that explains a lot about MVVM which I know barely about and the how the Dispatcher is implemented makes code so much cleaner with threads. +1 – Measurity Feb 20 '14 at 17:59
  • 1
    WPF automatically marshals all PropertyChanged events to UI thread, there is no point in Dispatcher.BeginInvoke. That is not true however for the second main WPF event - CollectionChanged. – dm.dymov Apr 30 '20 at 17:45
  • You went off-topic. Just being 100% MVVM won't make you avoid Dispatcher.Invoke, which is what the OP asked. – Norbert Forgacs Jul 13 '21 at 13:30