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

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

问题描述

这是一个从最初发布的问题:
通过运行时链接到load-xaml

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

我正在开发一种从外部源动态加载XAML内容的WPF MVVM应用程序,与在上面的帖子中回复。

这是我到目前为止所得到的:

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. 我的视图声明了一个ViewModel作为资源并创建该ViewModel的实例

  2. 在我的ViewModel构造函数中,我正在加载来自外部源(文件或数据库..)的XamlString属性

  3. 在我看来,我有一个按钮,用户在ViewModel完成加载后点击,并在点击事件代码中反序列化动态加载的XAML并将其添加到我的网格。

我的问题是,如何消除代码隐藏和自动化逻辑,以便View可以渲染ne在ViewModel完成XAML内容并初始化字符串属性后,动态处理wamam部分?

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元素(例如Grid),这显然可以通过模板和绑定来实现。我的ViewModel从外部来源检索整个XAML表单,并将其设置为View可用的字符串属性。

我的问题是:谁应该负责将此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):

我有简化的ViewModel:

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>

基本上我创建了一个隐藏的TextBlock绑定到我的XAML String属性在ViewModel中,我挂钩了它的加载事件在视图后面的代码中的事件处理程序:

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属性并将其暴露给View时,此解决方案的一部分仍然使用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天全站免登陆