将模型类的列表/集合/ IEnumerable绑定到数据网格并选择列

时间:2019-05-21 00:15:52

标签: c# wpf mvvm data-binding wpfdatagrid

我似乎无法通过搜索找到此答案,也无法连接到其他示例到我的场景中的点。

使用MVVM模式:

  1. 如何通过视图中的ViewModel将模型类绑定到列表框/数据网格?
  2. 如何选择要显示的列?
  3. 问题2后续行动:我可以通过Visual Studio UI属性进行选择,如果可以,如何选择?
  4. 如果使用XAML,我的模型/视图模型类是否可以自动完成?
  5. 使用Observable Collection,什么是正确的实现方式?
  6. 什么是“首选” XAML控件(datagrid,listbox等)

我已将示例简化为: 型号类别:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;


namespace TestListBinding.Models
{
    public class ProjectModel
    {
        public string Id { get; set; }
        public string ProjectTitle { get; set; }
        public string ProjectDetails { get; set; }
    }
}

ViewModel类:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Threading.Tasks;
using TestListBinding.Models;

namespace TestListBinding.ViewModels
{
    public class ProjectViewModel
    {
        private ProjectModel _project;
        private ObservableCollection<ProjectModel> _projects;

        public ProjectViewModel()
        {
            _projects = new ObservableCollection<ProjectModel>();
            var proj = new ProjectModel();
            for (int i = 1; i <= 3; i++)
            {
                proj.Id = "ID" + i.ToString();
                proj.ProjectTitle = "Project " + i.ToString();
                proj.ProjectDetails = "Details about this: " + i.ToString();
                _projects.Add(proj);
                proj = new ProjectModel();
            }
        }

        public IEnumerable<ProjectModel> Projects
        {
            get { return _projects; }
        }
    }
}

视图部分:

我知道该视图必须有一个DataContext。许多示例显示了它是在后台代码中设置的(请参见下文),还有一些示例引用了列表框的“ StaticResource”。我没有找到一种通过UI设置代码的方式来设置绑定的方法。

MainWindow.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using TestListBinding.ViewModels;

namespace TestListBinding
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DataContext = new ProjectViewModel();//is this correct, or is there an alternative method via XAML/control properties?
        }
    }
}

XAML(通过代码或UI)能否帮助我完成数据网格列/绑定: 根据@EdPlunkett更新

    <Window x:Class="TestListBinding.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:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:TestListBinding"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <DataGrid HorizontalAlignment="Left" Height="239" Margin="42,32,0,0" VerticalAlignment="Top" Width="435" ItemsSource="{Binding Projects}">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Project Title" Binding="{Binding ProjectTitle}"/>
                <DataGridTextColumn Header="Project Details" Binding="{Binding ProjectDetails}"/>
            </DataGrid.Columns>

        </DataGrid>

    </Grid>
</Window>

这会产生带有额外列的奇怪布局: Extra Columns

1 个答案:

答案 0 :(得分:1)

这在您的窗口构造函数中是正确的:

DataContext = new ProjectViewModel();

这应该向您显示网格中的一些内容:

<DataGrid.Columns>
    <DataGridTextColumn Binding="{Binding Id}" Header="ID"  />
    <DataGridTextColumn Binding="{Binding ProjectTitle}" Header="Project Title" />
    <DataGridTextColumn Binding="{Binding ProjectDetails}" Header="Project Details" />
</DataGrid.Columns>

WPF人员通常不使用“设计器/属性”窗格中的内容。这不是很好,并且十年来没有改善。在这种情况下,您也无法使用自动完成功能。

首选控制?取决于您要的内容:如果您希望每行多列,请使用DataGrid或ListView。如果要每行显示一个值,或者每行显示某种漂亮的模板化UI,请使用ListBox。

快速列表框:

<ListBox
    ItemsSource=“{Binding Projects}”
    DisplayMemberPath=“ProjectTitle”
    />

DisplayMemberPath未绑定。这是一个字符串,用于指定您要在ListBox中显示的事物的属性名称。


我个人会做下一个更改,尽管它是完全可选的。您所拥有的将可以完美地工作:DataGrid.ItemsSource并不介意绑定到声明为IEnumerable<T>的属性。 DataGrid.ItemsSource的声明类型是非通用System.Collections.IEnumerable。但是公开这样的可观察集合是更常规的,并且通常很方便。

public ObservableCollection<ProjectModel> Projects
{
    get { return _projects; }
}

当您说“模型类”时,我可能会弄错,但我想知道那里是否存在术语:您的ProjectModel是模型类,因为它基本上是POCO(“计划旧CLR对象”),没有INotifyPropertyChanged。但是,由于它已绑定在UI中,因此如果用户可以更改其上的任何属性,则需要使用INPC的正确视图模型。如果没有,请在该DataGrid上设置IsReadOnly="True"。如果项目只是计划UI中的无效只读数据,而没有任何行为,则像您拥有的POCO很好。只要确保UI确实是只读的即可。

MVVM并不意味着拥有模型的ViewModel。这意味着视图模型拥有的视图模型拥有其他视图模型,这些视图模型可能深达十几层,并且该视图模型“树”反映了应用程序的基本设计。然后,您将获得模型,这些模型是一个独立的并行层次结构,反映了数据层的必要组织。如果要通过Web服务从数据库加载项目,则可能会有一个ProjectModel和一个ProjectViewModel。但是某些模型没有对应的视图模型,许多视图模型没有对应的模型。

一个小项目可能根本不需要“模型”层。学习时,我会毫不犹豫地暂时编写视图模型和视图。掌握那些和模型是无关紧要的。

模型只是数据。一个ProjectViewModel可以包装一个ProjectModel,以暴露相同的属性,但是具有INPC以及其他特定于UI的属性:IsDirty,例如,如果它是可编辑的,或者查找列表以将UI绑定到ComboBoxes(如果它具有属性)是枚举或查找类型。它还将具有命令和方法。除了编写包装器外,还有其他一些也许更好的方法,但是已经很晚了,可悲的是,我现在正在空白。