c# - MVVM 动态添加字段到 View 中

标签 c# wpf mvvm caliburn.micro

我正在使用 caliburn.micro MVVM 框架开发 WPF 应用程序。
为了开发搜索屏幕,我需要根据模型属性将字段动态加载到 View 中。

考虑下面的 View 和 View 模型:

  • 搜索查看型号
  • 搜索查看

  • 让我们假设 T 是下面示例中的一种产品。
    public class SearchViewModel<T>
    {
       public T Item{get;set;}
    }
    
    public class Product
    {
       public int Id{get;set;}
       public string Name{get;set;}
       public string Description{get;set;}
    }
    

    我有一个名为 SearchView.xaml 的用户控件,上面没有任何内容。
    每当加载 View 时,应将新字段添加到 View 中,并且应将字段绑定(bind)到属性。

    根据上面的代码示例,Product 类中有 3 个公共(public)属性,因此应该在 View 中动态添加 3 个 TextBox。当用户在文本字段中输入数据时,应更新相应的属性。

    这可能吗?
    任何专家都可以通过提供一些示例来帮助我实现这一目标吗?

    最佳答案

    我建议以不同的方式解决这个问题。我不会考虑将属性动态添加到 View /模型中,而是考虑将有关这些属性的信息添加到 View 模型上的列表中。然后该列表将绑定(bind)到 ItemsControl使用看起来像 TextBox 的模板.

    因此,您的 View 模型将具有您要检查的“事物”的属性。在该属性的 setter 中,使用反射枚举您感兴趣的属性,并添加某种 FieldInfo 的实例类(您创建的)到具有绑定(bind)的属性列表。

    这样做的好处是使所有东西都与 MVVM 兼容,并且无需使用您自己的代码动态创建控件。

    下面的示例使用我自己的 MVVM 库(作为 nuget 包)而不是 caliburn.micro,但它应该足够相似以遵循基本思想。示例的完整源代码可以从 this BitBucket repo 下载。 .

    正如您在包含的屏幕截图中看到的,搜索字段是在 View 上动态创建的, View 中没有任何代码。一切都在 View 模型上完成。这也使您可以轻松访问用户输入的数据。

    View 模型:

    namespace DynamicViewExample
    {
        class MainWindowVm : ViewModel
        {
            public MainWindowVm()
            {
                Fields = new ObservableCollection<SearchFieldInfo>();
                SearchableTypes = new ObservableCollection<Type>()
                                  {
                                      typeof(Models.User),
                                      typeof(Models.Widget)
                                  };
    
                SearchType = SearchableTypes.First();
            }
    
            public ObservableCollection<Type> SearchableTypes { get; }
            public ObservableCollection<SearchFieldInfo> Fields { get; }
    
    
            private Type _searchType;
    
            public Type SearchType
            {
                get { return _searchType; }
                set
                {
                    _searchType = value;
                    Fields.Clear();
                    foreach (PropertyInfo prop in _searchType.GetProperties())
                    {
                        var searchField = new SearchFieldInfo(prop.Name);
                        Fields.Add(searchField);
                    }
                }
            }
    
            private ICommand _searchCommand;
    
            public ICommand SearchCommand
            {
                get { return _searchCommand ?? (_searchCommand = new SimpleCommand((obj) =>
                {
                    WindowManager.ShowMessage(String.Join(", ", Fields.Select(f => $"{f.Name}: {f.Value}")));
                })); }
            }
        }
    }
    
    SearchFieldInfo类(class):
    namespace DynamicViewExample
    {
        public class SearchFieldInfo
        {
            public SearchFieldInfo(string name)
            {
                Name = name;
            }
    
            public string Name { get; }
    
            public string Value { get; set; } = "";
        }
    }
    

    风景:
    <Window
        x:Class="DynamicViewExample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:local="clr-namespace:DynamicViewExample"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        Title="MainWindow"
        Width="525"
        Height="350"
        d:DataContext="{d:DesignInstance local:MainWindowVm}"
        mc:Ignorable="d">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition Height="*" />
                <RowDefinition Height="Auto" />
            </Grid.RowDefinitions>
            <ComboBox
                Grid.Row="0"
                ItemsSource="{Binding Path=SearchableTypes}"
                SelectedItem="{Binding Path=SearchType}" />
            <ItemsControl Grid.Row="1" ItemsSource="{Binding Path=Fields}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <StackPanel Orientation="Horizontal">
                            <TextBlock Text="{Binding Path=Name}" />
                            <TextBox Width="300" Text="{Binding Path=Value}" />
                        </StackPanel>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
            <Button Grid.Row="2" Command="{Binding Path=SearchCommand}">Search</Button>
        </Grid>
    </Window>
    

    模型类:
    class User
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string PhoneNumber { get; set; }
        public string Id { get; set; }
    }
    
    class Widget
    {
        public string ModelNumber { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
    }
    

    searching a Widget model
    searching User model

    关于c# - MVVM 动态添加字段到 View 中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44158190/

    相关文章:

    asp.net - 在 Web 项目中包含 PCL 时出现错误 "Could not load file or assembly ' sqlite 3' or one of its dependencies"

    WPF:DataGrid如何输入多行文本

    wpf - 一起使用 F# 和 Caliburn.Micro

    c# - 限制嵌套并行循环生成的并行线程总数

    c# - 从其他窗口刷新组合框列表,MVVM

    wpf - 如何做到这一点。DataContext = this : in XAML. ...例如<Window.DataContext> <local :MainWindow/></Window. DataContext>-->

    c# - WPF caliburn屏幕和选项卡控件

    c# - 无法在WP 8.1中将void分配给隐式类型的局部变量

    c# - 在读取页面源代码时,如何知道将使用 POST 发送哪些参数?

    c# - EntityFramework 测试初始化​​错误 : CREATE DATABASE statement not allowed within multi-statement transaction