WPF DataGrid,其中DataGrid位于RowDetailsTemplate中

WPF DataGrid,其中DataGrid位于RowDetailsTemplate中,wpf,mvvm,datagrid,Wpf,Mvvm,Datagrid,我上一篇关于检测虚拟机中的属性更改的文章不够深入,所以我发布了这篇文章 我有很多工作。每个工作可以有一个或多个员工 DataGrid的RowDetailsTemplate包含另一个显示员工的网格。所以到父网格绑定到一个作业列表。内部网格绑定到工作模型中的员工列表 工作模式: public class Job : _Base { private string _JobName = string.Empty; public string JobName { g

我上一篇关于检测虚拟机中的属性更改的文章不够深入,所以我发布了这篇文章

我有很多工作。每个工作可以有一个或多个员工

DataGrid的RowDetailsTemplate包含另一个显示员工的网格。所以到父网格绑定到一个作业列表。内部网格绑定到工作模型中的员工列表

工作模式:

public class Job : _Base
{
    private string _JobName = string.Empty;
    public string JobName
    {
        get { return _JobName; }
        set 
        {
            if (_JobName != value)
            {
                _JobName = value;
                RaisePropertyChanged("JobName");
            }
        }
    }

    private string _JobNumber = string.Empty;
    public string JobNumber
    {
        get { return _JobNumber; }
        set
        {
            if (_JobNumber != value)
            {
                _JobNumber = value;
                RaisePropertyChanged("JobNumber");
            }
        }
    }

    private ObservableCollection<Employee> _Employees;
    public ObservableCollection<Employee> Employees
    {
        get { return _Employees; }
        set
        {
            if (_Employees != value)
            {
                if (_Employees != value)
                {
                    _Employees = value;
                    RaisePropertyChanged("Employees");
                }
            }
        }
    }

    private Employee _SelectedEmployee;
    public Employee SelectedEmployee
    {
        get { return _SelectedEmployee; }
        set
        {
            if (_SelectedEmployee != value)
            {
                if (_SelectedEmployee != value)
                {
                    _SelectedEmployee = value;
                    RaisePropertyChanged("SelectedEmployee");
                }
            }
        }
    }

    public Job()
    {
        Employees = new ObservableCollection<Employee>();
    }
}
XAML

<DataGrid ItemsSource="{Binding Jobs}"
            SelectedItem="{Binding SelectedJob}"
            AutoGenerateColumns="False">

    <DataGrid.Columns>
        <DataGridTextColumn Header="Job Name" Binding="{Binding JobName}" />
        <DataGridTextColumn Header="Job Number" Binding="{Binding JobNumber}" />
    </DataGrid.Columns>

    <DataGrid.RowDetailsTemplate>
        <DataTemplate>

            <StackPanel Orientation="Vertical">

                <DataGrid ItemsSource="{Binding Employees}"
                            SelectedItem="{Binding SelectedEmployee}"
                            AutoGenerateColumns="False">

                    <DataGrid.Columns>
                        <DataGridCheckBoxColumn Binding="{Binding IsChecked}"/>
                        <DataGridTextColumn Binding="{Binding EmployeeName}"/>
                    </DataGrid.Columns>

                </DataGrid>

                <Button Margin="5"
                        Height="23"
                        Width="75"
                        HorizontalAlignment="Left"
                        Content="Remove"/>

            </StackPanel>


        </DataTemplate>
    </DataGrid.RowDetailsTemplate>

</DataGrid>

主窗口视图模型

public class MainWindowViewModel : _Base
{
    private ObservableCollection<Job> _Jobs;
    public ObservableCollection<Job> Jobs
    {
        get { return _Jobs; }
        set 
        {
            if (_Jobs != value)
            {
                if (_Jobs != value)
                {
                    _Jobs = value;
                    RaisePropertyChanged("Jobs");
                }
            }
        }
    }

    private Job _SelectedJob;
    public Job SelectedJob
    {
        get { return _SelectedJob; }
        set
        {
            if (_SelectedJob != value)
            {
                if (_SelectedJob != value)
                {
                    _SelectedJob = value;
                    RaisePropertyChanged("SelectedJob");
                }
            }
        }
    }

    public MainWindowViewModel()
    {
        this.PropertyChanged += new PropertyChangedEventHandler(MainWindowViewModel_PropertyChanged);
    }

    void MainWindowViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        if (e.PropertyName.Trim().ToLower() == "ischecked")
        {
            int x = 1;
        }
    }
}
public类MainWindowViewModel:\u Base
{
私人可观察收集工作;
公开收集工作
{
获取{return_Jobs;}
设置
{
如果(_Jobs!=值)
{
如果(_Jobs!=值)
{
_工作=价值;
RaiseProperty变更(“工作”);
}
}
}
}
私人工作(u selected Job);;
公共职务选定职务
{
获取{return\u SelectedJob;}
设置
{
如果(_SelectedJob!=值)
{
如果(_SelectedJob!=值)
{
_SelectedJob=值;
RaisePropertyChanged(“SelectedJob”);
}
}
}
}
公共主窗口视图模型()
{
this.PropertyChanged+=新的PropertyChangedEventHandler(MainWindowViewModel\u PropertyChanged);
}
void MainWindowViewModel\u属性已更改(对象发送方,System.ComponentModel.PropertyChangedEventArgs e)
{
如果(e.PropertyName.Trim().ToLower()=“ischecked”)
{
int x=1;
}
}
}
我有几个问题:

1) 在内部网格中单击员工时,不会激发职务模型上的SelectedEmployee属性

2) 选中员工时,不会激发MainWindowViewModel_属性Changed

3) 请注意内部网格下面的按钮。如何将其命令绑定到MainWindowVM

  • 由于DataGrid的行中有DataGrid
    ,因此上面的DataGrid在某种程度上消耗了内部DataGrid的selectionchange
    。要解决这个问题,您需要强制更新子DataGrid的绑定源。为此,请捕获内部DataGrid的SelectionChanged事件,并在处理程序中执行以下操作

     private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
     {
          DataGrid grid = e.OriginalSource as DataGrid;
          var expression = grid.GetBindingExpression(DataGrid.SelectedItemProperty);
          expression.UpdateSource();
    
      }
    
  • MainwindowVM没有任何ischecked属性,这就是为什么该属性的propertychanged未启动的原因

  • 要解决这个问题,你需要做两件事。问题是DataGrid单元格和行不继承DataContext,因为它们不在其可视树下。因此,要解决这个问题,您必须使用
    BindingProxy
    将windows DataContext带到DataGrid的rowdetails中的按钮

  • 定义绑定代理类,如下所示:

        public class MyBindingProxy : Freezable
        {
            public static readonly DependencyProperty BindingDataProperty =
                DependencyProperty.Register("BindingData", typeof(object),
                typeof(MyBindingProxy), new UIPropertyMetadata(null));
    
            protected override Freezable CreateInstanceCore()
            {
                return new MyBindingProxy();
            }
    
            public object BindingData
            {
                get { return (object)GetValue(BindingDataProperty); }
                set { SetValue(BindingDataProperty, value); }
            }
    
        }
    
    现在,在窗口的资源(DataGrid所在的位置)中创建代理实例,并将BindingData设置为窗口的DataContext,即MainWindowViewModel,如下所示:

    <Window.Resources>
        <local:MyBindingProxy x:Key="myproxy" BindingData="{Binding}" />
    </Window.Resources>
    
    
    
    现在只需在按钮上设置如下命令:

    <Button Margin="5"
       Height="23"
       Width="75"
       HorizontalAlignment="Left"
       Content="Remove"
       Command="{Binding BindingData.MyCommand, Source={StaticResource myproxy}}"/>
    
    
    
  • 由于DataGrid的行中有DataGrid
    ,因此上面的DataGrid在某种程度上消耗了内部DataGrid的selectionchange
    。要解决这个问题,您需要强制更新子DataGrid的绑定源。为此,请捕获内部DataGrid的SelectionChanged事件,并在处理程序中执行以下操作

     private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
     {
          DataGrid grid = e.OriginalSource as DataGrid;
          var expression = grid.GetBindingExpression(DataGrid.SelectedItemProperty);
          expression.UpdateSource();
    
      }
    
  • MainwindowVM没有任何ischecked属性,这就是为什么该属性的propertychanged未启动的原因

  • 要解决这个问题,你需要做两件事。问题是DataGrid单元格和行不继承DataContext,因为它们不在其可视树下。因此,要解决这个问题,您必须使用
    BindingProxy
    将windows DataContext带到DataGrid的rowdetails中的按钮

  • 定义绑定代理类,如下所示:

        public class MyBindingProxy : Freezable
        {
            public static readonly DependencyProperty BindingDataProperty =
                DependencyProperty.Register("BindingData", typeof(object),
                typeof(MyBindingProxy), new UIPropertyMetadata(null));
    
            protected override Freezable CreateInstanceCore()
            {
                return new MyBindingProxy();
            }
    
            public object BindingData
            {
                get { return (object)GetValue(BindingDataProperty); }
                set { SetValue(BindingDataProperty, value); }
            }
    
        }
    
    现在,在窗口的资源(DataGrid所在的位置)中创建代理实例,并将BindingData设置为窗口的DataContext,即MainWindowViewModel,如下所示:

    <Window.Resources>
        <local:MyBindingProxy x:Key="myproxy" BindingData="{Binding}" />
    </Window.Resources>
    
    
    
    现在只需在按钮上设置如下命令:

    <Button Margin="5"
       Height="23"
       Width="75"
       HorizontalAlignment="Left"
       Content="Remove"
       Command="{Binding BindingData.MyCommand, Source={StaticResource myproxy}}"/>
    
    
    
    选择这些
    对象的目的是什么?我会认真地重新考虑将名为
    isChecked
    的属性放在
    Employee
    内,或
    SelectedEmployee
    放在
    Job
    内。这些表示视图的状态,这将视图逻辑与模型域相耦合。用户可以选中/取消选中任意数量的员工,然后单击“删除”。这就是目的这些
    选定对象的目的是什么?我会认真地重新考虑将名为
    isChecked
    的属性放在
    Employee
    内,或
    SelectedEmployee
    放在
    Job
    内。这些表示视图的状态,这将视图逻辑与模型域相耦合。用户可以选中/取消选中任意数量的员工,然后单击“删除”。这就是为什么自从你发布后我一直在看这个,我很抱歉,但这一切看起来像是一个黑客。我不敢相信一个简单的一对多网格设置需要所有这些。另外,你说的是与事件挂钩。这不是真正的MVVM。在没有事件的情况下绑定应该是可能的。自从你发布后,我一直在看这个,我很抱歉,但这一切看起来像是一个黑客。我不敢相信一个简单的一对多网格设置需要所有这些。另外,你说的是与事件挂钩。这不是真正的MVVM。绑定应该可以在没有事件的情况下进行。