使用WPF中的MVVM模式在运行时加载XAML

Ado*_*rez 3 .net c# wpf dynamic mvvm

这是一个从最初发布的问题扩展到的问题: 链接到loading-xaml到运行时

我正在开发一个WPF MVVM应用程序,它从外部源动态加载XAML内容,与上面的帖子中的答案非常相似.
这是我到目前为止所得到的:

  1. My View将ViewModel的实例声明为资源,并创建该ViewModel的实例
  2. 在我的ViewModel构造函数中,我正在加载来自外部源(文件或数据库..)的XamlString属性.
  3. 在我看来,我有一个用户在ViewModel完成加载后点击的按钮,在点击事件代码隐藏中,我将动态加载的XAML反序列化并将其添加到我的网格中.

我的问题是,如何消除代码隐藏和自动化逻辑,以便View可以在ViewModel完成获取XAML内容并初始化字符串属性后立即动态呈现新的xaml部分?

我应该使用某种消息总线,以便ViewModel在设置属性后通知,以便View可以添加新内容吗?

让我感到困扰的是,ViewModel确实有对Views的引用,不应该负责生成UI元素.

提前致谢!

编辑:只是为了澄清:在我的特定情况下,我不是试图将业务对象或集合(模型)绑定到UI元素(例如网格),这显然可以通过模板和绑定来完成.我的ViewModel正在从外部源检索整个XAML表单,并将其设置为View可用的字符串属性.

我的问题是:谁应该负责将这个XAML字符串属性反序列化为UI元素,并在设置VM中的Xaml字符串属性后以编程方式将其添加到我的网格中?
这听起来更像是一个View责任,而不是ViewModel.但我理解的模式强制要用V-VM绑定替换任何代码隐藏逻辑.

Ado*_*rez 5

我现在有一个有效的解决方案,我想分享它.不幸的是,我没有完全摆脱代码隐藏,但它的工作方式与我期望的一样.以下是它的工作原理(简化):

我有简化的ViewModel:

public class MyViewModel : ViewModelBase
{
   //This property implements INPC and triggers notification on Set
   public string XamlViewData {get;set;}

   public ViewModel()
   {
      GetXamlFormData();  
   }

   //Gets the XAML Form from an external source (e.g. Database, File System)
   public void GetXamlFormData()
   {
       //Set the Xaml String property
       XamlViewData = //Logic to get XAML string from external source
   }
}
Run Code Online (Sandbox Code Playgroud)

现在我的观点:

<UserControl.Resources>
<ViewModel:MyViewModel x:Key="Model"></ViewModel:MyViewModel>
</UserControl.Resources>
<Grid DataContext="{StaticResource Model}">
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition/>
    </Grid.RowDefinitions>
    <StackPanel>
    <!-- This is the Grid used as a Place Holder to populate the dynamic content!-->
    <Grid x:Name="content" Grid.Row="1" Margin="2"/>
    <!-- Then create a Hidden TextBlock bound to my XamlString property. Right after binding happens I will trigger an event handled in the code-behind -->
    <TextBlock Name="tb_XamlString" Text="{Binding Path=XamlViewData, Mode=TwoWay, UpdateSourceTrigger=LostFocus, NotifyOnValidationError=True, ValidatesOnDataErrors=True, ValidatesOnExceptions=True}" Visibility="Hidden" Loaded="tb_XamlString_Loaded" />
    </StackPanel>
</Grid>
Run Code Online (Sandbox Code Playgroud)

基本上我在ViewModel中创建了一个绑定到我的XAML String属性的隐藏TextBlock,并将其Loaded事件挂钩到View后面代码中的事件处理程序:

    private void tb_XamlString_Loaded(object sender, RoutedEventArgs routedEventArgs)
    {
        //First get the ViewModel from DataContext
        MyViewModel vm = content.DataContext as MyViewModel;
        FrameworkElement rootObject = XamlReader.Parse(vm.XamlViewData) as FrameworkElement;
        //Add the XAML portion to the Grid content to render the XAML form dynamically!
        content.Children.Add(rootObject);
    }
Run Code Online (Sandbox Code Playgroud)

这可能不是最优雅的,但可以完成工作.就像有人说的那样,在MVVM中有一些像这样的情况需要很少的代码隐藏代码.在使用VM检索和填充XamlString属性并将其暴露给View时,此解决方案的一部分仍然使用V-VM Binding原则.如果我们想单元测试XAML解析和加载功能,我们可以将它委托给一个单独的类.

我希望有人觉得这很有用!