我被迫在 WPF 应用程序中使用 View First MVVM,我正在努力了解如何让它优雅地工作。
问题的根源在于嵌套UserControls
。在 MVVM 架构中,每个都UserControl
需要将其视图模型分配给它DataContext
的DataTemplate
.
但是,如果子项UserControl
具有父项需要绑定到其自己的视图模型的依赖属性,那么子项UserControl
将其DataContext
设置为自己的视图模型这一事实意味着父 XAML 文件中的“隐式路径”绑定将改为解析为子项的视图模型父母的。
为了解决这个问题,应用程序中的每个父级都UserControl
需要默认使用显式命名绑定(这很冗长、丑陋且容易出错),或者它必须知道特定控件是否已将其DataContext
设置为自己的视图模型或不使用并使用适当的绑定语法(这同样容易出错,并且严重违反了基本封装)。
经过几天的研究,我还没有找到一个半体面的解决方案来解决这个问题。我遇到的最接近解决方案的是将视图模型设置为(最上面的或其他)UserControl's
的内部元素,这仍然会让您在尝试将自身的属性绑定到自己的视图模型时遇到问题!(在这种情况下绑定将不起作用,因为绑定将在命名元素之前声明,并将视图模型分配给它)。UserControl
Grid
UserControl
ElementName
DataContext
我怀疑没有多少其他人遇到此问题的原因是他们要么使用没有此问题的 viewmodel first MVVM,要么将 view first MVVM 与改善此问题的依赖注入实现结合使用。
请问有人有干净的解决方案吗?
更新:
根据要求提供示例代码。
<!-- MainWindow.xaml -->
<Window x:Class="UiInteraction.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:UiInteraction"
Title="MainWindow" Height="350" Width="525"
x:Name="_this">
<Window.DataContext>
<local:MainWindowVm/>
</Window.DataContext>
<StackPanel>
<local:UserControl6 Text="{Binding MainWindowVmString1}"/>
</StackPanel>
</Window>
namespace UiInteraction
{
// MainWindow viewmodel.
class MainWindowVm
{
public string MainWindowVmString1
{
get { return "MainWindowVm.String1"; }
}
}
}
<!-- UserControl6.xaml -->
<UserControl x:Class="UiInteraction.UserControl6" x:Name="_this"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:UiInteraction">
<UserControl.DataContext>
<local:UserControl6Vm/>
</UserControl.DataContext>
<StackPanel>
<!-- Is bound to this UserControl's own viewmodel. -->
<TextBlock Text="{Binding UserControlVmString1}"/>
<!-- Has its value set by the UserControl's parent via dependency property. -->
<TextBlock Text="{Binding Text, ElementName=_this}"/>
</StackPanel>
</UserControl>
namespace UiInteraction
{
using System.Windows;
using System.Windows.Controls;
// UserControl code behind declares DependencyProperty for parent to bind to.
public partial class UserControl6 : UserControl
{
public UserControl6()
{
InitializeComponent();
}
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text", typeof(string), typeof(UserControl6));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
}
}
namespace UiInteraction
{
// UserControl's viewmodel.
class UserControl6Vm
{
public string UserControlVmString1
{
get { return "UserControl6Vm.String1"; }
}
}
}
这导致:
System.Windows.Data 错误:40:BindingExpression 路径错误:在“对象”“UserControl6Vm”(HashCode=44204140)上找不到“MainWindowVmString1”属性。绑定表达式:路径=MainWindowVmString1;DataItem='UserControl6Vm' (HashCode=44204140); 目标元素是'UserControl6'(名称='_this');目标属性是“文本”(类型“字符串”)
因为在MainWindow.xaml
声明中<local:UserControl6 Text="{Binding MainWindowVmString1}"/>
试图解决MainWindowVmString1
.UserControl6Vm
在UserControl6.xaml
注释掉 theDataContext
和 first的声明时TextBlock
,代码将起作用,但UserControl
需要DataContext
. MainWIndow1
使用 an而ElementName
不是隐式路径绑定也可以,但是为了使用ElementName
绑定语法,您要么必须知道UserControl
将其视图模型分配给它DataContext
(封装失败),要么采用在任何地方ElementName
使用绑定的策略。两者都没有吸引力。