You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF C# DataGrid单单元格内嵌列表展示员工访客及功能实现问题

WPF DataGrid 单元格列表展示开发问题

使用的技术和工具

  • Visual Studio 2019
  • WPF
  • C#

需求说明

  1. 在DataGrid的「员工」列单个单元格中展示1名或多名员工列表,需同时支持单员工和多员工两种场景的实现方式;
  2. 在「访客」列单个单元格中展示1名或多名访客列表;
  3. 最后一列仅展示1条关联对应员工和访客的描述信息,无需每行重复展示;
  4. 访客信息旁需添加编辑和删除按钮,支持修改访客详情或从列表中移除访客,操作结果同步更新到模拟数据库或正式数据库中。

遇到的问题

  1. 目前仅能绑定预约列表,尝试获取并绑定下层属性时绑定失效;
  2. 所有员工都被拆分到独立行展示,不符合单单元格内存放员工列表的需求;
  3. 访客列表也存在相同问题,需将多条访客信息存放在同一DataGrid单元格中;
  4. 展开DataGrid项后无法正常滚动,需解决该异常。

现有代码

XAML代码 [AppointmentsWindow.xaml]

<DataGrid x:Name="DataGridAppointments" ItemsSource="{Binding Appointments}"
                      AutoGenerateColumns="False"
                      SelectionMode="Single"
                      CanUserAddRows="false"
                      CanUserDeleteRows="False"
                      SelectionUnit="FullRow">

                <!-- DataGrid columns -->
                <DataGrid.Columns>
                    <DataGridTextColumn Header="Title" Binding="{Binding Title}" />
                    <DataGridTextColumn Header="Date and Time" Binding="{Binding StartDateTime}" />
                    <DataGridTextColumn Header="Location" Binding="{Binding Location}" />
                </DataGrid.Columns>

                <!-- DataGrid Expander (i.e. expanding/collapsing row items) -->
                <DataGrid.RowHeaderTemplate>
                    <DataTemplate>
                        <Expander Expanded="ExecuteExpanderToggler" Collapsed="ExecuteExpanderToggler"/>
                    </DataTemplate>
                </DataGrid.RowHeaderTemplate>

                <!--Inner DataGrid for additional information -->
                <DataGrid.RowDetailsTemplate>
                    <DataTemplate>
                        <DataGrid ItemsSource="{Binding Appointments}"
                                  AutoGenerateColumns="False"
                                  SelectionMode="Single"
                                  CanUserAddRows="false"
                                  CanUserDeleteRows="False"
                                  SelectionUnit="FullRow">
                            <DataGrid.Columns>
                                <DataGridTextColumn Header="Employee" Binding="{Binding Appointments.Organizer}"/>
                                
                                <DataGridTemplateColumn Header="Visitors">
                                    <DataGridTemplateColumn.CellTemplate>
                                        <DataTemplate>
                                            <StackPanel Orientation="Horizontal">
                                                <DataGrid ItemsSource="{Binding Appointments.Visitors}">
                                                    <DataGrid.Columns>
                                                        <DataGridTextColumn Header="Visitor name" Binding="{Binding FirstName}" />
                                                        <DataGridTextColumn Header="License plate" Binding="{Binding LicensePlate}" />
                                                        <DataGridTextColumn Header="Attendees" Binding="{Binding Appointments.Attendees}" />
                                                    </DataGrid.Columns>
                                                </DataGrid>
                                            </StackPanel>
                                        </DataTemplate>
                                    </DataGridTemplateColumn.CellTemplate>
                                </DataGridTemplateColumn>

                                <DataGridTextColumn Header="Description" Binding="{Binding Appointments.Description}"/>
                            </DataGrid.Columns>
                        </DataGrid>
                    </DataTemplate>
                </DataGrid.RowDetailsTemplate>
            </DataGrid>

C#后端代码 [AppointmentsWindow.xaml.cs]

public AppointmentViewModel AppointmentViewModel { get; set; } = new AppointmentViewModel();
    public AppointmentsWindow()
    {
        InitializeComponent();
        WindowStartupLocation = WindowStartupLocation.CenterScreen;

        //TEST
        DataContext = AppointmentViewModel;
    }

C#后端代码 [AppointmentViewModel.cs]

public class AppointmentViewModel
{
    public IList<Appointment> Appointments { get; set; }

    public AppointmentViewModel()
    {
        Appointments = new List<Appointment>();
        Organizer employee = new Organizer(1, "test1@live.nl", "0612345671", "Name1", "lastName1");

        Visitor vis1 = new Visitor(1, "visitor1@live.nl", "0612345671", "Visitor1", "LastName1", "5-ABC-1");
        Visitor vis2 = new Visitor(2, "visitor2@live.nl", "0612345672", "Visitor2", "LastName2", "5-ABC-2");
        IList<Visitor> visitors = new List<Visitor> { vis1, vis2 };

        Appointment app1 = new Appointment(1, "Title1", "location1", "description1", new DateTime(2015, 1, 10), new DateTime(), employee, visitors, 5);
        Appointment app2 = new Appointment(1, "Title2", "location2", "description2", new DateTime(2015, 2, 15), new DateTime(), employee, visitors, 10);
        Appointment app3 = new Appointment(1, "Title3", "location3", "description3", new DateTime(2015, 3, 20), new DateTime(), employee, visitors, 35);

        Appointments.Add(app1);
        Appointments.Add(app2);
        Appointments.Add(app3);
    }
}

C#后端代码 [PropertyNotifier.cs]

public class PropertyNotifier : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyRaised(string propertyname)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyname));
    }
}

C#后端代码 [Appointment.cs]

public class Appointment : PropertyNotifier
{
    private int id;
    public int Id
    {
        get { return id; }
        set { id = value; OnPropertyRaised(nameof(Id)); }
    }

    private string title;
    public string Title
    {
        get { return title; }
        set { title = value; OnPropertyRaised(nameof(Title)); }
    }

    private string location;
    public string Location
    {
        get { return location; }
        set { location = value; OnPropertyRaised(nameof(Location)); }
    }

    private string description;
    public string Description
    {
        get { return description; }
        set { description = value; OnPropertyRaised(nameof(Description)); }
    }

    private DateTime? startDateTime;
    public DateTime? StartDateTime
    {
        get { return startDateTime; }
        set { startDateTime = value; OnPropertyRaised(nameof(StartDateTime)); }
    }

    private DateTime? endDateTime;
    public DateTime? EndDateTime
    {
        get { return endDateTime; }
        set { endDateTime = value; OnPropertyRaised(nameof(EndDateTime)); }
    }

    private Organizer organizer;
    public Organizer Organizer
    {
        get { return organizer; }
        set { organizer = value; OnPropertyRaised(nameof(Organizer)); }
    }

    private IList<Visitor> visitors;
    public IList<Visitor> Visitors
    {
        get { return visitors; }
        set { visitors = value; OnPropertyRaised(nameof(Visitors)); }
    }

    private int attendees;
    public int Attendees
    {
        get { return attendees; }
        set { attendees = value; OnPropertyRaised(nameof(Attendees)); }
    }

    public Appointment(int id, string title, string location, string description, DateTime? startDateTime, DateTime? endDateTime, Organizer organizer, IList<Visitor> visitors, int attendees)
    {
        Id = id;
        Title = title;
        Location = location;
        Description = description;
        StartDateTime = startDateTime;
        EndDateTime = endDateTime;
        Organizer = organizer;
        Visitors = visitors;
        Attendees = attendees;
    }
}

C#后端代码 [Person.cs]

public abstract class Person : PropertyNotifier
{
    private int id;
    public int Id 
    {
        get { return id; }
        set { id = value; OnPropertyRaised(nameof(Id)); }
    }

    private string email;
    public string Email 
    {
        get { return email; }
        set { email = value; OnPropertyRaised(nameof(Email)); }
    }

    private string phone;
    public string Phone 
    {
        get { return phone; }
        set { phone = value; OnPropertyRaised(nameof(Phone)); }
    }

    private string firstName;
    public string FirstName 
    {
        get { return firstName; }
        set { firstName = value; OnPropertyRaised(nameof(FirstName)); }
    }

    private string lastName;
    public string LastName 
    {
        get { return lastName; }
        set { lastName = value; OnPropertyRaised(nameof(LastName)); }
    }

    protected Person()
    {

    }

    protected Person(int id, string email, string phone, string firstName, string lastName)
    {
        Id = id;
        Email = email;
        Phone = phone;
        FirstName = firstName;
        LastName = lastName;
    }
}

C#后端代码 [Organizer.cs]

public class Organizer : Person
{
    public Organizer(int id, string email, string phone, string firstName, string lastName) : base(id, email, phone, firstName, lastName)
    {

    }
}

C#后端代码 [Visitor.cs]

public class Visitor : Person
{
    private string licensePlate;
    public string LicensePlate { 
        get { return licensePlate; } 
        private set 
        { 
            licensePlate = value;
            OnPropertyRaised(nameof(LicensePlate));
        } 
    }

    public Visitor(int id, string email, string phone, string firstName, string lastName, string licensePlate) : 
        base(id, email, phone, firstName, lastName)
    {
        LicensePlate = licensePlate;
    }
}

内容的提问来源于stack exchange,提问作者user16906859

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 09:09:06