【问题标题】:Can't update UI from ViewModel in another classes无法从其他类中的 ViewModel 更新 UI
【发布时间】:2016-05-19 10:22:30
【问题描述】:

我创建了一个需要大量访问 UI 控件的应用程序,现在我首先要做的是创建一个可扩展的界面,特别是我创建了不同的控件,如 UserControl 和一个类 ViewModel 来管理所有方法此控件用于更新 UI。实际上在Main 线程中一切正常。特别是以下场景完美运行:

主窗口 XAML

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

例如,Scheduler 控件包含此Data Binding:

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

ViewModel 结构

public class ViewModelClass : INotifyPropertyChanged
{
    private CScheduler scheduler;

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

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

你如何在ViewModel 中看到我有一个Scheduler 控件的实例和一个名为NextSyncrhonization 的property 作为binding,所以这个属性从控件的方法返回一个结果实例。

为了在MainWindow 中使用它,我做了以下操作:

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

这会自动填充控件属性。现在的问题是我使用BackgroundWorker 来执行某些任务,我需要的是使用来自不同类的MainWindow 的DataContext(不是Window,而是类)。

为了解决这种情况,我想这样做:

 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"}));

现在SynchronizationLog 是另一个将文本附加到控件的属性,只是为了精确,是这样的:

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

这是INotifyPropertyChanged的实现:

`public event PropertyChangedEventHandler PropertyChanged;

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

这仅适用于MainWindow,但在外部类中我无法更新 UI,我做错了什么?

无论如何,我没有收到任何错误。

【问题讨论】:

    标签: c# wpf xaml mvvm


    【解决方案1】:

    尝试以下方法:

    按如下方式扩展您的 ViewModel:

    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();
            }
        }
    }
    

    这会将您在 xaml.cs 中的代码更改为:

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

    然后,在您的外部代码中,您不要创建 ViewModelClass 的新实例 - 而是使用现有的:

    [...].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"
                }
             }));
    

    基本上,您在这里所做的就是在您的 ViewModel 中从您的 viewModel 之外设置一个属性。这可以在任何地方完成。

    与您的方法有何不同:

    1. 我们不创建 ViewModel 的新实例(UI 中的不同绑定不再重置)
    2. 我们创建了一个Instance,因此一次只能有一个视图模型

    【讨论】:

    • 听起来不错,但这里有一个问题:ViewModelClass.Instance = new ViewModelClass(); 你使用了viewModel 变量但没有声明
    • 使用 ClassName 代替,因为这是一个静态属性,它应该可以工作 => ViewModelClass.Instance
    • 类名?什么意思?
    • 我不知道这是否有效,我坚持这个:DataContext = viewModel.Instance; 我应该从哪里得到这个变量?
    • 不要使用变量 使用Class 本身与使用ViewModelClass variable = new ViewModelClass(); 时一样使用ViewModelClass.Insance 在这种情况下
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多