Creating view on receiving a message

2019-07-26 14:12发布

问题:

I have a MVVM view and viewmodel. In the constructor of my viewmodel I pass a list of IObservable messages and subscribe to them through a simple class sitting outide of my viewmodel and view

Outside class

{
     viewModel =
            new ViewModelClass(
                responseHandler.AsObservable());

     viewModel.PropertyChanged += ViewModel_PropertyChanged;
 }

 private void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
 {

     if (e.PropertyName == nameof(ViewModelClass.MyProperty))
     {
         // Error here
         view = new MyViewClass() { DataContext = viewModel };

     }
 }

In the view model constructor

        subscription = receiveMessages.Subscribe(MessageReceived);

        private void MessageReceived(GvsMessage message)
        {
            MyProperty = true;

        }

On receiving a message I want to create my view not before that. Although the viewmodel is created before to handle property change etc

The problem is that I get "the calling thread must be sta because many ui components require this". Could someone please help

回答1:

As we spoke in the comments you need to use a Dispatcher which can be acquired from different parts of the app.
To initialize the dispatcher you can use this snippet:

protected static Dispatcher _d;
if (Application.Current != null)
{
   _d = Application.Current.Dispatcher;
}
else
{
   _d = Dispatcher.CurrentDispatcher;
}  

Explanation:
The first dispatcher is done when the application has UnitTests when you run the tests this dispatcher will not be null,
The second one is the current Dispatcher used by your application.
After you have this in your code when initializing your VM now you can send messages Actions, Events to the UI Thread.
I have a little method just for that:

public static void UIThread(Action action)
    {
        if (_d == null)
        {
            if (Application.Current != null)
            {
                _d = Application.Current.Dispatcher;
            }
            else
            {
                _d = Dispatcher.CurrentDispatcher;
            }
        }
        _d.Invoke(action);
    }  

This function will accept lambda, like so:

UIThread(() =>
        {
            Processing = true;
            Message = "Working ...";
            //in your case you would raise the Loaded event here
        });  

This way you EventHandler in your view will have no problem showing that view.
If you need any more info let us know.
HTH



回答2:

I resolved this by creating the view and the viewmodel in the constructor. In the propertychanged event I just set a property IsVisible to 'true' binds the window visibility

<Window.Visibility>
    <Binding Path="IsVisible" Converter="{StaticResource BoolToVisibilityConverter}" Mode="TwoWay"/>
</Window.Visibility>


标签: c# wpf mvvm