在运行时使用 WPF 中的 MVVM 模式加载 XAML [英] Loading XAML at runtime using the MVVM pattern in WPF

查看:20
本文介绍了在运行时使用 WPF 中的 MVVM 模式加载 XAML的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个从最初在这里发布的问题延伸出来的问题:链接到通过运行时加载-xaml

This is a question that extends from the originally posted here: Link to loading-xaml through runtime

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

I'm working on a WPF MVVM application that loads XAML content dynamically from an external source, very similar as the answer in the post above.
Here is what I got so far:

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

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

My question is, how can I eliminate code-behind and automate the logic so the View can render the new xaml section dynamically right after the ViewModel is done getting the XAML content and initializing the string property?

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

Should I use some kind of Messaging Bus so the ViewModel notifies once the property has been set so the View can add the new content?

让我烦恼的是,ViewModels 确实有对 Views 的引用,不应该负责生成 UI 元素.

What troubles me is the fact that ViewModels do have a reference to Views and should not be in charge of generating UI elements.

提前致谢!

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

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

Edit: Just to clarify: in my particular case I am not trying to bind a Business Object or Collection (Model) to a UI element (e.g. Grid) which obviously could be accomplished through templates and binding. My ViewModel is retrieving a whole XAML Form from an external source and setting it as a string property available to the View.

My question is: Who should be in charge of deserializing this XAML string property into a UI element and add it programmatically to the my grid once my Xaml string property in the VM is set?
This sounds to me more of like a View responsibility, not ViewModel. But the pattern as i understand it enforces to replace any code-behind logic with V-VM bindings.

推荐答案

我现在有一个可行的解决方案,我想分享它.不幸的是,我没有完全摆脱代码隐藏,但它按我的预期工作.这是它的工作原理(简化):

I have a working solution now and I'd like to share it. Unfortunately I did not get rid of code-behind completely but it works as I expect it to. Here is how it works(simplified):

我有我的简化视图模型:

I have my simplified 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
   }
}

现在我的观点:

<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>

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

Basically I created a hidden TextBlock bound to my XAML String property in the ViewModel and I hooked its Loaded event to an event handler in the code behind of the 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);
    }

这可能不是最优雅的,但可以完成工作.就像有人说的那样,在 MVVM 中,在某些情况下需要很少的代码隐藏代码.这并没有什么坏处,而且在使用 VM 检索和填充 XamlString 属性并将其公开给视图时,该解决方案的一部分仍然使用 V-VM 绑定原则.如果我们想对 XAML 解析和加载功能进行单元测试,我们可以将其委托给一个单独的类.

This may not be the most elegant but gets the job done. Like some people say, in MVVM there are some cases like this where little code-behind code is needed. It doesn't hurt and also part of this solution still uses the V-VM Binding principles when using the VM to retrieve and populate the XamlString property and exposing it to the View. If we would like to Unit Test the XAML parsing and loading functionality we could delegate it to a separate class.

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

I hope someone finds this useful!

这篇关于在运行时使用 WPF 中的 MVVM 模式加载 XAML的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆