我一直尽我所能努力保持MVVM模式推荐的分离。我还没弄清楚如何正确操作有一件事与初始化我的UserControls有关。
我最近的一个例子是我写的一个库与一些低级硬件交谈。该程序集碰巧有一个UserControl,我可以简单地放入任何使用这个硬件的GUI。它所需要的只是设置对有权访问低级方法的对象的引用。
然而,这就是我的问题所在 - 目前,UserControl通过XAML添加到GUI,我在其中定义命名空间,然后将UserControl添加到我的窗口。当然,此时我无法控制它的创建,因此默认构造函数被调用。为硬件控制设置必要参考的唯一方法是在UC中调用方法来执行此操作。 ViewModel可以在Model中调用一个方法,例如GetController()
,然后调用UserControl中的方法来相应地设置引用。当所述GUI创建ViewModel时,GUI可以将对UserControl的引用传递给ViewModel,但这会违反MVVM,因为ViewModel不应该知道有关此控件的任何信息。
我可以解决这个问题的另一种方法是不在XAML中创建UserControl,而是从代码隐藏中完成所有操作。在ViewModel初始化并检索初始化的UserControl(即具有低级别对象引用集的UserControl)之后,它可以将我的窗口内容设置为UserControl。但是,这也违反了MVVM - 有没有办法将Window,TabControl或任何其他元素的内容数据绑定到UserControl?
我想听听是否有人之前必须处理这个问题,如果他们接触到我在这里概述的第一种或第二种方式,或者他们采取了完全不同的方法。如果我在这里提出的问题不清楚,请告诉我,我会尽力使用更多信息,图表等进行更新。
更新
感谢你的回复,伙计们,但我一定不能很好地解释这个问题。我已经在UserControl的ViewModel中使用RelayCommands来处理用户在UserControl本身中单击控件时对硬件层(Model)的所有调用。我的问题与最初传递对UserControl的引用有关,因此它可以与硬件层通信。
如果我直接在XAML中创建UserControl,那么我不能通过构造函数传递它的引用,因为我只能使用默认构造函数。我现在的解决方案现在看起来不符合MVVM - 我不得不在XAML中命名UserControl,然后在代码隐藏中(即对于View),我必须调用我添加到的方法能够设置此引用。例如,我有一个GUI UserControl,其中包含我的硬件的诊断UserControl:
partial class GUI : UserControl
{
private MainViewModel ViewModel { get; set; }
public GUI( Model.MainModel model)
{
InitializeComponent();
ViewModel = new MainViewModel( model, this.Dispatcher);
ViewModel.Initialize();
this.DataContext = ViewModel;
diagnostics_toolbar.SetViewModel( ViewModel);
user_control_in_xaml.SetHardwareConnection( model.Connection);
}
}
其中外部类是主GUI UserControl,而user_control_in_xaml
是我必须在GUI的XAML中命名的UserControl。
再次看一下,我意识到使用命名方法可能是可以的,因为它全部在View本身中使用。我不确定是否将模型信息传递给user_control_in_xaml
,因为这意味着如果他要重做GUI,设计师必须知道调用此方法 - 我认为这个想法是隐藏模型细节View层,但我不确定如何做到这一点。
您还会注意到主GUI在构造函数中传递了Model,我认为这同样很糟糕。也许我需要重新审视设计,看看是否可以让ViewModel创建模型,这是我通常做的,但在这种情况下我不记得为什么我必须在GUI之外创建它。
答案 0 :(得分:0)
我自己是MVVM的新手,但这是一个可能的解决方案:
在VM中创建属性对象类型(控制硬件)并将其绑定到UserControl上的附加属性的属性。然后,您可以使用依赖项注入在VM中设置属性,因此将在创建VM时设置该属性。我看到它的方式,与硬件(硬件控制器)对话的类是一种服务。该服务可以注入您的视图模型并绑定到UserControl。我不确定这是否是最好的方法,如果它对所有MVVM原则都足够严格,但它似乎是一种可能的解决方案。
答案 1 :(得分:0)
如果您的问题是:如何在视图中显示我的viewmodel?那么我的解决方案总是使用viewmodelfirst方法和datatemplates。
所以你要做的就是通过绑定到xaml中的contentcontrol.content来连接你的viewmodel。 wpf + datatemplates将完成工作并为您的viewmodel实例化您的usercontrol。
答案 2 :(得分:0)
你是对的,ViewModel不应该知道视图中的任何内容 - 或者甚至是视频中存在的东西,因此MVVM也会因为单元测试而摇摆不定,因为如果它是虚拟机可能不在乎将自己暴露给View或测试框架。
据我所知,如果可以,你可能需要稍微重构一下。为了坚持MVVM模式,你可以暴露一个ICommand,ICommand调用一个内部VM方法,从模型中获取数据(或其他),然后这个方法更新数据对象的ObservableCollection属性,以便View绑定到。例如,在您的VM中,您可以拥有
private ICommand _getDataCommand;
public ICommand GetDataCommand
{
get
{
if (this._getDataCommand == null)
{
this._getDataCommand = new RelayCommand(param => this.GetMyData(), param => true);
}
return this._getDataCommand;
}
}
private void GetMyData{
//go and get data from Model and add to the MyControls collection
}
private ObservableCollection<MyUserControls> _uc;
public ObservableCollection<MyUserControls> MyControls
{
get
{
if (this._uc == null)
{
this._uc = new ObservableCollection<MyUserControls>();
}
return this._uc;
}
}
对于RelayCommand,请查看Josh Smiths MSDN article。
在视图中你可以在UC的静态构造函数中调用ICommand - 我猜你需要在你的类中添加一个事件 - 或者从UC上的某种点击事件调用ICommand - 也许只需在WPF窗口上有一个“加载”按钮。并将UC的数据绑定设置为VM的公开可观察集合。
如果您根本无法更改UC,那么您可以从中获取新类并覆盖某些行为。
希望至少有点帮助,就像我说的那样,看看Josh Smiths的MVVM文章,因为他很好地介绍了绑定和ICommand的内容。
答案 3 :(得分:0)
如果您将包含此DataContext
的{{1}}或Window
的{{1}}设置为主视图模型,则用户控件可以调用在UserControl
事件(或UserControl
事件处理程序)中SetHardwareConnection()
本身。{/ p>
如果那是不可能的,因为你说Loaded
是'固定的',你应该从它派生或将它包装在另一个DataContextChanged
中,它将作为MVVM'适配器'。
(为了绑定窗口:您可以使MainViewModel成为具有UserControl
UserControl
属性的单例并使用static
。这是一种快速推进工作的好方法
请注意;这是基于我的理解,MVVM因为Bindings而工作。我总是将控件绑定到ViewModel,而不是将ViewModel作为参数传递。
希望有所帮助!