Can't update UI from ViewModel in another clas

2019-09-10 21:17发布

问题:

I've created an application that need a lot of access to UI controls, now what I did firstly is create an interface scalable, in particular I created different controls as UserControl and one class ViewModel that manage all method of this control for update the UI. Actually all working good in the Main thread. In particular the followin scenario working perfect:

MainWindow XAML

xmlns:MyControls="clr-namespace:HeavyAPP"
...
<!-- I use the control in the following way: -->
<Grid>
     <MyControls:Scheduler x:Name="Sc"/>
</Grid>

so for example the Scheduler control contains this Data Binding:

<StackPanel Orientation="Horizontal">
     <Label x:Name="NextSync" Content="{Binding NextSynchronization, IsAsync=True}" ></Label>
</StackPanel>

ViewModel structure

public class ViewModelClass : INotifyPropertyChanged
{
    private CScheduler scheduler;

    public ViewModelClass()
    {
        scheduler = new Scheduler();
    }

    public string NextSynchronization
    {
        get
        {
            return scheduler.GetNextSync();
        }
    }
}

How you can see in the ViewModel I've an instance of the Scheduler control and a property called NextSyncrhonization as the binding, so this property return a result from the method of the control instance.

For use this in the MainWindow I did the following:

public MainWindow()
{
    ViewModelClass viewModel = new ViewModelClass();
    DataContext = viewModel;
}

this automatically fill the control property. Now the problem's that I use a BackgroundWorker for perform some task, what I need is use the DataContext of MainWindow from different classes, (not Window, but classes).

For solve this situation I though to do something like this:

 MainWindow.AppWindow.Sc.SyncLog.Dispatcher.Invoke(
            new Action(() =>
            {
                ViewModelClass viewModel = new ViewModelClass();
                var dataContext = System.Windows.Application.Current.MainWindow.DataContext;
                dataContext = viewModel;
                viewModel.SynchronizationLog = "this is a test from other thread"}));

now SynchronizationLog is another property that append the text to the Control, just for precision, is this:

private string _text;
public string SynchronizationLog
{
        get
        {
            return _text += _text;
        }
        set
        {
            _text = value;
            OnPropertyChanged();
        }
 }

this is the implementation of INotifyPropertyChanged:

`public event PropertyChangedEventHandler PropertyChanged;

 protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
 {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
 }`

this working only in the MainWindow, but in the external classes I can't update the UI, what am I doing wrong?

I reiceve no error, anyway.

回答1:

Try the following:

extend your ViewModel as follow:

public class ViewModelClass : INotifyPropertyChanged
{
    private CScheduler scheduler;
    //Add this:
    public static ViewModelClass Instance {get; set;} //NEW
    public ViewModelClass()
    {
        scheduler = new Scheduler();
    }

    public string NextSynchronization
    {
        get
        {
            return scheduler.GetNextSync();
        }
    }
}

This changes your code in the xaml.cs to:

public MainWindow()
{
   ViewModelClass.Instance = new ViewModelClass();
   DataContext = viewModel.Instance;
}

In your external code you then DONT create a new Instance of the ViewModelClass - instead you use the existing one:

[...].Dispatcher.Invoke(() =>
        {
            if(ViewModelClass.Instance != null)
            {
               //Why you need the "var datacontext" in your example here ?
               ViewModelClass.Instance.SynchronizationLog = "this is a test from other thread"
            }
         }));

Basically what you do here is setting a property in your ViewModel from outside of your viewModel. This can be done from everywhere.

What is different to your approach:

  1. We dont create a new Instance of the ViewModel (different bindings in the UI aren't resetted anymore)
  2. We created an Instance so there can always be ONLY ONE viewModel at a time


标签: c# wpf xaml mvvm