我无法解决这个问题。所以我会尽力描述我的问题。
我正在使用MVVM模式构建应用程序。我有用户控件AAAView
,其中viewmodel AAAViewModel
用于填充数据类CDataClass
。我还有主窗口MainView
及其viewmodel MainViewModel
。接下来,我有DialogView
窗口DialogViewModel
。
所以现在MainViewModel
(具有自己的用户控件)创建DialogViewModel
(使用另一个用户控件实例)。如何在CDataClass
这两个用户控件之间传输数据?我试图在AAAViewModel
中创建属性,该属性包含MainViewModel
或DialogViewModel
的实例,因此我可以将数据传递给它,但是我因为无法将其作为依赖属性而陷入困境
我的目标是让用户控制可以在不同的视图中使用,这些视图可以在CDataClass
下加载不同的数据。
只是为了澄清......我正在使用用户控件<views:GeneralInfoView Grid.Row="0" />
,并且不知道如何在不同视图中的同一用户控件的两个不同实例之间共享数据。对某种模式或方法的任何指点都会非常感激。
感谢您的帮助。
答案 0 :(得分:1)
我认为您的应用程序架构图表是视图之间的关系,这并不理想;我认为更好的思考方式是视图模型之间的一组关系,视图根据需要悬挂在树上。当你以这种方式思考时,&#34;数据如何通过&#34;变得更简单了。视图只是视图模型和用户之间的管道。你不是把房子设计成一套窗户和电话,然后试着从中找出平面图。你从房子的作用以及人们将如何生活开始。
所以这很简单:
某些视图模型具有AAViewModel
属性。这些视图模型可能有各种简单或复杂的视图;如果视图要让用户编辑视图模型的AAViewModel
内容,则其中包含AAView
适当绑定到视图模型的AAViewModel
。您的MainViewModel
和DialogViewModel
都是大型复杂的互动视图,希望让某人编辑他们的虚拟用户AAViewModel
内容。
如果MainViewModel
是DialogViewModel
的父级,或者创建DialogViewModel
的临时实例只是为了放入模式对话框,则MainViewModel
会显示对话框,看看dialogVM.AAVM.CData.IsDirty
来决定如何处理它。或者它可能在显示对话框之前给dialogVM.AAVM
一个新的CDataClass
实例(可能是它自己的实例的一个克隆),如果ShowModel()
返回true
,那么它会做一些事情dialogVM.AAVM.CData
。
关键是,一旦你的视图模型驱动一切,它们就会相互简单地沟通。亲子很容易:父母给孩子提供东西并看孩子带回来的东西。 viewmodel可以订阅另一个viewmodel的PropertyChanged
事件;父视图模型可以监视其子项;当孩子发生某事时,父母可以决定是否更新兄弟姐妹。一般来说,孩子们对父母一无所知;这使得在不同的上下文中重用子视图模型变得更加容易。由父母决定如何处理这些信息。
所有AAViewModel都知道有人递给他CDataClass
的副本;他相应地更新了他的公共财产。然后其他人(可能AAView
,但他不知道)通过设置他的属性给他一些改变;他相应地更新了他的CDataClass
实例。过了一段时间,他不知道,一个视图模型或另一个视图模型来看CDataClass
。
视图和视图模型之间的通信通过绑定发生。
事实证明,您在视图中创建了视图模型,因此您不知道父级如何访问它们。现在你知道为什么以这种方式创建子视图视图模型不是一个好主意。
以下是我在上面描述的以视图模型为中心的设计中执行子视图/视图模型的方法:
首先,摆脱您在视图中创建子视图模型所做的任何事情。
其次,为子视图模型类型创建DataTemplate
。这应该放在一个资源字典中,该资源字典被合并到App.xaml中的资源中,但它非常简单,如果你变懒,它就不会杀了你,只需将它粘贴到Resources
中即可。它使用的两种不同观点。
我不知道您的命名空间声明是什么样的。我将假设视图位于名为xmlns:view="..."
的视图中,视图模型位于名为xmlns:vm="..."
的视图中。
<DataTemplate DataType="{x:Type vm:AAAViewModel}">
<view:AAAView />
</DataTemplate>
现在,您可以将AAAViewModel
分配给从ContentProperty
继承的任何控件的ContentControl
(以及其中大部分内容),并且模板将被实例化。这意味着XAML将为您创建AAAView
,并将AAAViewModel
的实例分配给它刚创建的DataContext
的{{1}}属性。
接下来让我们创建一个孩子AAAView
,然后我们会在界面中显示它。
AAAViewModel
现在我们可以在public class DialogViewModel
{
// You can create this in DialogViewModel's constructor if you need to
// give it parameters that won't be known until then.
private AAAViewModel _aaavm = new AAAViewModel();
public AAAViewModel AAAVM
{
get { return _aaavm; }
protected set {
_aaavm = value;
OnPropertyChanged(nameof(AAAVM));
}
}
中显示AAAVM
:
DialogView
现在<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<ContentControl
Content="{Binding AAAVM}"
Grid.Row="0"
/>
<StackPanel Orientation="Vertical" Grid.Row="1">
<!-- Other stuff -->
</StackPanel>
</Grid>
如何与MainViewModel
取得联系?在对话框的情况下,由于它们具有有限的寿命,因此让它们创建自己的视图模型实际上并不是什么大不了的事。你可以这样做。我通常倾向于让它创建自己的,如下面的第二个例子。
不太一样,但很接近。首先,再一次,摆脱对话创建自己的视图模型时所做的任何事情。
MainViewModel.cs
DialogViewModel
请注意,下一个是视图代码隐藏,而不是viewmodel。
DialogView.xaml.cs
public CDataClass CDC { /* you know the drill */ }
public void ShowDialog()
{
var dvm = new DialogViewModel();
// Maybe this isn't what you want; I don't know what CDataClass does.
// But I'm assuming it has a copy constructor.
dvm.AAAVM.CDC = new CDataClass(this.CDC);
if (DialogView.ShowDialog(dvm).GetValueOrDefault())
{
CDC = dvm.CDC;
}
}
现在,您可以让对话框继续创建自己的viewmodel;在这种情况下,你会给它一个像这样的公共财产:
public static bool? ShowDialog(DialogViewModel dvm)
{
var vw = new DialogView() { DataContext = dvm };
return vw.ShowDialog();
}
这样的ShowDialog方法:
DialogView.xaml.cs
public DialogViewModel ViewModel => (DialogViewModel)DataContext;
然后父母可以像这样与之互动:
MainViewModel.cs
public static bool? ShowDialog(CDataClass cdc)
{
var dlg = new DialogView();
dlg.ViewModel.AAAVVM.CDC = cdc;
return dlg.ShowDialog();
}
干净整洁。
如果该对话框不是模态的,则使对话框viewmodel成为public void ShowDialog()
{
var cdcClone = new CDataClass(this.CDC);
if (DialogView.ShowDialog(cdcClone).GetValueOrDefault())
{
CDC = cdcClone;
}
}
的私有成员,并让MainViewModel
订阅对话框视图模型上的事件,以及时了解对话框正在执行的操作。每当用户更新对话框的MainViewModel
副本时,对话框都会引发CDataClass
,而DataClassUpdated
会有一个针对MainViewModel
嗅探的事件的处理程序,并决定如何处理它。如有必要,我们可以为此编写示例代码。
现在,您可以通过根据父/子视图模型构建所有内容,并在适当的时候将它们填充到视图中来了解我的意思。