从WPF中的ViewModel动态生成简单视图?

时间:2012-04-13 11:26:33

标签: c# wpf mvvm

我正在使用this article和其他资源慢慢学习WPF。

我专注于应用程序逻辑 - 定义模型+ viewModel,并创建对这些操作进行操作的命令。我还没有查看视图和.xaml格式。

当我在处理逻辑时,我希望有一个视图可以渲染我绑定到它的任何viewModel。视图应该

  • 将任何公共string属性渲染为文本框,并将文本框绑定到属性
  • 将属性的名称渲染为标签。
  • 将任何公共'Command'属性渲染为按钮,并将按钮绑定到命令(可能仅在命令不带参数的情况下?)

维护MVVM设计模式时是否可以这样?如果是这样,我将如何实现它?此外,文章建议避免使用.xaml代码隐藏 - 此视图是否可以在纯xaml中实现?

4 个答案:

答案 0 :(得分:6)

我认为只有XAML才有可能。如果要在运行时生成视图,则必须在ViewModel上使用反射并相应地生成控件。如果要在编译时生成视图,则可以在构建时使用某些模板引擎(如T4或字符串模板)或CodeDom从ViewModel生成xaml文件。或者你可以更进一步,有一些元数据格式(甚至DSL),你将从中生成模型和视图等。这取决于您的应用需求。

同样在MVVM中,代码隐藏对于视觉逻辑和对模型/视图模型的绑定是好的,这些只能在XAML中完成。

答案 1 :(得分:3)

我不确定这是否适用于“纯MVVM”方法,当然不是所有内容都只是通过绑定来实现。而且我只是抛弃了避免在这里使用代码隐藏的想法,这是一个固有的程序化任务。你应该坚持的一件事是让ViewModel不了解视图,所以当你用“真实的东西”替换它时,没有工作可做。

但肯定看起来合情合理;它几乎听起来更像是一个调试可视化工具 - 你可以利用现有的工具来实现这一点。

(如果您确实希望在大多数XAML中使用标准ItemsControl和模板执行此操作,您可以编写转换器以通过以可以绑定的某种形式的反射来公开ViewModel的属性,这是一个包装器的集合具有公开元数据的对象,但我认为确保公开的属性是正确可绑定的,这将是更多的工作而不是它的价值)

答案 2 :(得分:3)

我现在正在执行此操作的一半,我希望以下代码可以帮助其他人尝试这样做。变成一个更强大的库可能会很有趣。

AbstractView.xaml

<UserControl x:Class="MyApplication.View.AbstractView"
             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:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <StackPanel Name="container">
    </StackPanel>
</UserControl>

AbstractView.xaml.cs

public partial class AbstractView : UserControl
{
    public AbstractView()
    {
        InitializeComponent();

        DataContextChanged += Changed;
    }

    void Changed(object sender, DependencyPropertyChangedEventArgs e)
    {
        object ob = e.NewValue;
        var props = ob.GetType().GetProperties();

        List<UIElement> uies = new List<UIElement>();
        foreach (var prop in props)
        {
            if (prop.PropertyType == typeof(String))
                uies.Add(makeStringProperty(prop));
            else if (prop.PropertyType == typeof(int))
                uies.Add(makeIntProperty(prop));
            else if (prop.PropertyType == typeof(bool))
                uies.Add(makeBoolProperty(prop));
            else if (prop.PropertyType == typeof(ICommand))
                uies.Add(makeCommandProperty(prop));
            else
            {
            }
        }

        StackPanel st = new StackPanel();
        st.Orientation = Orientation.Horizontal;
        st.HorizontalAlignment = HorizontalAlignment.Center;
        st.Margin = new Thickness(0, 20, 0, 0);
        foreach (var uie in uies) {
            if (uie is Button)
                st.Children.Add(uie);
            else
                container.Children.Add(uie);
        }
        if (st.Children.Count > 0)
            container.Children.Add(st);

    }

    UIElement makeCommandProperty(PropertyInfo prop)
    {
        var btn = new Button();
        btn.Content = prop.Name;

        var bn = new Binding(prop.Name);
        btn.SetBinding(Button.CommandProperty, bn);
        return btn;
    }

    UIElement makeBoolProperty(PropertyInfo prop)
    {
        CheckBox bx = new CheckBox();
        bx.SetBinding(CheckBox.IsCheckedProperty, getBinding(prop));
        if (!prop.CanWrite)
            bx.IsEnabled = false;
        return makeUniformGrid(bx, prop);
    }

    UIElement makeStringProperty(PropertyInfo prop)
    {
        TextBox bx = new TextBox();
        bx.SetBinding(TextBox.TextProperty, getBinding(prop));
        if (!prop.CanWrite)
            bx.IsEnabled = false;

        return makeUniformGrid(bx, prop);
    }

    UIElement makeIntProperty(PropertyInfo prop)
    {
        TextBlock bl = new TextBlock();
        bl.SetBinding(TextBlock.TextProperty, getBinding(prop));

        return makeUniformGrid(bl, prop);
    }

    UIElement makeUniformGrid(UIElement ctrl, PropertyInfo prop)
    {
        Label lb = new Label();
        lb.Content = prop.Name;

        UniformGrid u = new UniformGrid();
        u.Rows = 1;
        u.Columns = 2;
        u.Children.Add(lb);
        u.Children.Add(ctrl);

        return u;
    }

    Binding getBinding(PropertyInfo prop)
    {
        var bn = new Binding(prop.Name);
        if (prop.CanRead && prop.CanWrite)
            bn.Mode = BindingMode.TwoWay;
        else if (prop.CanRead)
            bn.Mode = BindingMode.OneWay;
        else if (prop.CanWrite)
            bn.Mode = BindingMode.OneWayToSource;
        return bn;
    }

}

答案 3 :(得分:1)

指针:生成动态DataTemplate作为绑定到特定VM(目标)的字符串。通过XamlReader解析它。将其插入代码中的应用程序资源。

只是一个想法..用它运行..应该通过View或ViewModel以外的某种类型来完成。