5

I am having a bear of a time figuring out how to handle a Thread from a class outside my ViewModel.

The Thread originates from a Track class. Here is the ResponseEventHandler code in Track:

public delegate void ResponseEventHandler(AbstractResponse response);
public event ResponseEventHandler OnResponseEvent;

When a "command" method is processed from within my Track object, the following code runs the OnResponseEvent, which sends a message in a Thread back to my ViewModel:

if (OnResponseEvent != null)
{
    OnResponseEvent(GetResponseFromCurrentBuffer());
}

GetResponseFromCurrentBuffer() merely returns a message type which is a pre-defined type within the Track.

My MainWindowViewModel constructor creates an event handler for the OnResponseEvent from the Track:

public MainWindowViewModel()
{
    Track _Track = new Track();

    _Track.OnResponseEvent +=
        new Track.ResponseEventHandler(UpdateTrackResponseWindow);
}

So, the idea is that every time I have a new message coming from the OnResponseEvent Thread, I run the UpdateTrackResponseWindow() method. This method will append a new message string to an ObservableCollection<string> list property called TrackResponseMessage:

private void UpdateTrackResponseWindow(AbstractResponse message)
{
    TrackResponseMessage.Add(FormatMessageResponseToString(message));
}

The FormatMessageResponseToString() method merely compares the message with all pre-defined message types within the Track, and does some nifty string formatting.

The main problem is: The UI disappears when TrackResponseMessage.Add() is run. The executable is still running in the background, and the only way to end the task is to shut down Visual Studio 2010.

TrackResponseMessage is a public property within my ViewModel:

public ObservableCollection<String> TrackResponseMessage
{
    get { return _trackResponseMessage; }
    set
    {
        _trackResponseMessage = value;
        RaisePropertyChanged("TrackResponseMessage");
    }
}

Is there a need for me to marshal the Thread coming from the Track object to my ViewModel? Any example code would be very appreciated!

2 Answers 2

10

Is there a need for me to marshall the thread comming from the Track.cs object to my viewmodel? Any example code would be very appreciated!

Yes. Unfortunately, while INotifyPropertyChanged will handle events from other threads, INotifyCollectionChanged does not (ie: ObservableCollection<T>). As such, you need to marshal back to the VM.

If the VM is being create from the View (View-First MVVM) or is known to be created on the UI thread, there's a good option using .NET 4 tasks:

TaskScheduler uiScheduler;
public MainWindowViewModel() 
{
    uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
    Track _Track = new Track();
    _Track.OnResponseEvent += new Track.ResponseEventHandler(UpdateTrackResponseWindow);
}

Then, later, your event handler can do:

private void UpdateTrackResponseWindow(AbstractResponse message) 
{
    Task.Factory.StartNew(
       () => TrackResponseMessage.Add(FormatMessageResponseToString(message)),
       CancellationToken.None, TaskCreationOptions.None,
       uiScheduler); 
}

This has the nice advantage of not pulling WPF or Silverlight specific resources and types into your ViewModel class (ie: Dispatcher), while still providing all of the benefits. It also works, unchanged, in other routines with thread affinity (ie: WCF service work).

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

3 Comments

Yeeehaw!! It worked great! Reed, I owe you a donut! You saved me hours of reading .net threading documentation. The TaskScheduler makes it easy to read incomming threads from other class objects in my viewmodel.
@EnLaCucha: FYI - This only works for threads that have a SynchronizationContext installed. That's true for WPF's main thread, WinForms's main thread, and WCF service calls, though.
With .net 4.5 you can now edit collections from another thread using BindingOperations.EnableCollectionSynchronization msdn.microsoft.com/en-us/library/hh140164(v=vs.110).aspx
0

If the RaisePropertychanged is executed on a thread other than the UI thread AND the event handler for the event touches the UI you need to switch to the UI thread.

Comments

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.