【问题标题】:How to re-instantiate ViewModel when it was originally instantiated in XAML in C#最初在 C# 中的 XAML 中实例化 ViewModel 时如何重新实例化它
【发布时间】:2021-09-13 19:35:50
【问题描述】:

我正在开发一个应用程序并尝试尽可能多地遵循 MVVM,因此有许多具有相应 ViewModes 的视图。我正在反序列化使用 XAML 在视图中实例化的 ViewModel。例如,如果 View 称为“ExampleView”,而 ViewModel 称为“ExampleViewModel”。 ViewModel 通过这样做在 ExampleView 中实例化...

<UserControl.Resources>
        <local:ExampleViewModel x:Key="ViewModel" />
</UserControl.Resources>

使用后面的代码从 View 中获取/设置 ViewModel(通常这只是一个获取,但我尝试在反序列化后设置 ViewModel)。

    public ExampleViewModel ViewModel
        {
            get { return (ExampleViewModel)this.Resources["ViewModel"];  }
            set
            {
                if (this.Resources["ViewModel"]!=value)
                {
                    this.Resources["ViewModel"] = value;
                }
            }
        }

这不起作用,但我认为原因是 PropertyChanged 没有被解雇。因此,在 ExampleViewModel 中,我输入了一种方法来刷新每个属性。比如……

public void RefreshAllProperties()
        {
            NotifyPropertyChanged("Property1");
            NotifyPropertyChanged("Property2");
            ...
        }

NotifyPropertyChanged 在哪里...

   private void NotifyPropertyChanged([CallerMemberName] string PropertyName = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
        }

虽然这没有通过代码气味测试,但我试图在寻找更优雅的方法的过程中理解。但是,我惊讶地发现它不起作用。

我更愿意在 XAML 中实例化 ViewModel。是否有在反序列化后重新实例化 ViewModel 的最佳做法?

修改了后续问题

关于将创建的 ViewModel 移动到 View 类的构造函数中的任何 cmets?这是更好的设计模式吗?

ExampleViewModel exampleViewModel;

        public ExampleView()
        {
            InitializeComponent();
            ExampleViewModel = new ExampleViewModel();
            this.DataContext = ExampleViewModel;
        }

        public ExampleViewModel ViewModel
        {
            get { return exampleViewModel;  }
            set
            {
                if (exampleViewModel!=value)
                {
                    exampleViewModel = value;
                    NotifyPropertyChanged();
                    
                }
            }
        }

【问题讨论】:

  • 这一切有什么用? UserControl 通常会在由其 DataContext 属性的属性值继承提供的视图模型实例上进行操作。
  • 如果您在运行时更改资源的值,您需要通过{DynamicResource} 标记扩展来引用它。不是{StaticResource}。静态资源仅在加载时读取一次。 INotifyPropertyChanged 在任何一种情况下都无济于事。因为资源不是属性。
  • 如何绑定到资源中声明的虚拟机实例?

标签: c# wpf xaml


【解决方案1】:

我以前没有见过在 ResourceDictionary 中定义的 ViewModel。我倾向于在代码隐藏中初始化我的 ViewModel(我知道你提到你想将它保留在 XAML 中),因为我可以更直接地控制页面 DataContext 以及它最终更新的时间(比如在你的情况下反序列化之后)。在运行时修改 ResourceDictionary 似乎是一种危险的方法,而 ResourceDictionary in WPF does not implement INotifyPropertyChanged 或任何其他更改接口(如 INotifyCollectionChanged),这意味着它不会通知任何其一个键值对已以某种方式更改的任何内容。

总而言之:我的答案是不要在 ResourceDictionary 中定义您的 VM,而是在您的页面代码隐藏中对其进行管理,您可以确保在 VM 或其状态发生变化时适当地更新 DataContext。

【讨论】:

  • 谢谢。我是 XAML 的新手,我的想法基于这篇文章。 codemag.com/Article/1905031。感谢您的回答,但我仍然希望有人有一个优雅的解决方案。
  • 我刚刚用建议的解决方案修改了这个问题。对这种方法的适用性有什么意见吗?
  • 我认为您提出的解决方案很好。当引用从不更改并且您的视图数据绑定到它加载的同一个实例时,在 XAML 中声明您的 VM 很好,但是当您需要开始将 VM 引用更改为新对象时,最好在代码隐藏。
猜你喜欢
  • 2019-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-08
  • 2010-12-04
相关资源
最近更新 更多