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?

2
  • 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([...]); } Commented Feb 19, 2014 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. Commented Feb 19, 2014 at 14:44

1 Answer 1

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.

Sign up to request clarification or add additional context in comments.

8 Comments

-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
you dont know what mvp is? dude... no comment :)
@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?
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...
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.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.