将数据网格传递给 ContentControl over RowDetail 无法按预期工作

本文关键字:RowDetail 工作 over ContentControl 数据网 数据 网格 | 更新日期: 2023-09-27 17:56:52

我需要的是有一个能够列出类似于DataGrid的项目的UserControl,这意味着它将具有行列表,并且每一行都有一个RowDetail,但还有一些附加功能。

我已经尝试了两种不同的场景。

方案 1 - 无法重复使用,但按预期工作:

MainWindow.xaml

   <DataGrid ItemsSource="{Binding Employees}" >            
        <DataGrid.RowDetailsTemplate>
            <DataTemplate>
                <DataGrid ItemsSource="{Binding Details}"/>
            </DataTemplate>
        </DataGrid.RowDetailsTemplate>
    </DataGrid>

MainWindow.xaml.cs

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        var detail1 = new EmployeeDetails() {ManagerID=11, ManagerName="11 Name" , ManagerMobile = "123456" };
        var detail2 = new EmployeeDetails() { ManagerID = 12, ManagerName = "12 Name", ManagerMobile = "123456" };
        var detail3 = new EmployeeDetails() { ManagerID = 13, ManagerName = "13 Name", ManagerMobile = "123456" };
        var detail4 = new EmployeeDetails() { ManagerID = 11, ManagerName = "11 Name", ManagerMobile = "123456" };
        var detail5 = new EmployeeDetails() { ManagerID = 12, ManagerName = "12 Name", ManagerMobile = "123456" };
        var detail6 = new EmployeeDetails() { ManagerID = 13, ManagerName = "13 Name", ManagerMobile = "123456" };
        var detail7 = new EmployeeDetails() { ManagerID = 11, ManagerName = "11 Name", ManagerMobile = "123456" };
        var detail8 = new EmployeeDetails() { ManagerID = 12, ManagerName = "12 Name", ManagerMobile = "123456" };
        var detail9 = new EmployeeDetails() { ManagerID = 13, ManagerName = "13 Name", ManagerMobile = "123456" };
        var details1 = new List<EmployeeDetails>();
        details1.Add(detail1);
        details1.Add(detail2);
        details1.Add(detail3);
        var details2 = new List<EmployeeDetails>() {detail4, detail5, detail6};
        var details3 = new List<EmployeeDetails>() { detail7, detail8, detail9 };
        Employees = new List<Employee>();
        Employees.Add(new Employee() { ID = 1, Name = "Name1" , Details = details1 });
        Employees.Add(new Employee() { ID = 2, Name = "Name2", Details = details2 });
        Employees.Add(new Employee() { ID = 3, Name = "Name3", Details = details3 });
        this.DataContext = this;
    }
    public List<Employee> Employees { get; set; }
}
public class Employee
{
    public int ID { get; set; }
    public string Name { get; set; }
    public List<EmployeeDetails> Details { get; set; }
}
public class EmployeeDetails
{
    public int ManagerID { get; set; }
    public string ManagerName { get; set; }
    public string ManagerMobile { get; set; }
}

方案 2 - 可以重复使用,但无法按预期工作:

Mainwindow.xaml.cs

   <local:CustomDataGrid ItemsSource="{Binding Employees}">            
        <local:CustomDataGrid.RowDetails>
            <DataGrid ItemsSource="{Binding Details}" />
        </local:CustomDataGrid.RowDetails>
    </local:CustomDataGrid>

MainWindow.xaml.cs

        --------------Same as Scenario 1-----------------

CustomDataGrid.xaml

<Grid>
    <DataGrid x:Name="maingrid" AutoGenerateColumns="True" >
        <DataGrid.RowDetailsTemplate>
            <DataTemplate>
                <ContentControl Content="{Binding RowDetails, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
            </DataTemplate>
        </DataGrid.RowDetailsTemplate>
    </DataGrid>
</Grid>

CustomDataGrid.xaml.cs

/// <summary>
/// Interaction logic for CustomDataGrid.xaml
/// </summary>
public partial class CustomDataGrid : UserControl
{
    public CustomDataGrid()
    {
        InitializeComponent();            
    }
    public static readonly DependencyProperty ItemsSourceProperty =
               DependencyProperty.Register("ItemsSource"
                   , typeof(IList)
                   , typeof(CustomDataGrid)
                   , new PropertyMetadata(null, OnItemSourcechanged));

    public IList ItemsSource
    {
        get { return (IList)GetValue(ItemsSourceProperty); }
        set { SetValue(ItemsSourceProperty, value); }
    }      
    public static readonly DependencyProperty RowDetailsProperty =
               DependencyProperty.Register("RowDetails"
                   , typeof(object)
                   , typeof(CustomDataGrid));
    private static void OnItemSourcechanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        (d as CustomDataGrid).maingrid.ItemsSource = (IList)e.NewValue;
    }
    public object RowDetails
    {
        get { return GetValue(RowDetailsProperty); }
        set { SetValue(RowDetailsProperty, value); }
    }
}

我预计场景 2 的工作方式与场景 1 类似,但奇怪的是它的工作方式不同。

Scenarion 2 的问题是,DataGrid中的RowDetail在通过打开另一个RowDetail项关闭后不会打开。

关于如何使方案 2 类似于方案 1 的任何想法。

将数据网格传递给 ContentControl over RowDetail 无法按预期工作

我会使用一个字符串来绑定详细信息属性的名称...

<local:CustomDataGrid NewItemsSource="{Binding Employees}" CustomRowDetails="Details" />

使用自定义数据网格设置所选项

<Grid>
    <DataGrid x:Name="maingrid" AutoGenerateColumns="True" ItemsSource="{Binding NewItemsSource}" SelectedItem="{Binding SelItem}" >
        <DataGrid.RowDetailsTemplate>
            <DataTemplate>
                <DataGrid ItemsSource="{Binding MyDetails, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
            </DataTemplate>
        </DataGrid.RowDetailsTemplate>
    </DataGrid>
</Grid>

通过反射

public partial class CustomDataGrid : UserControl, INotifyPropertyChanged
{
    public CustomDataGrid()
    {
        InitializeComponent();
        maingrid.DataContext = this;
    }
    public static readonly DependencyProperty NewItemsSourceProperty =
               DependencyProperty.Register("NewItemsSource"
                   , typeof(IEnumerable)
                   , typeof(CustomDataGrid)
                   , new PropertyMetadata(null, OnItemSourcechanged));

    public IEnumerable NewItemsSource
    {
        get { return (IEnumerable)GetValue(NewItemsSourceProperty); }
        set
        {
            SetValue(NewItemsSourceProperty, value);
        }
    }
    public static readonly DependencyProperty CustomRowDetailsProperty =
               DependencyProperty.Register("CustomRowDetails"
                   , typeof(string)
                   , typeof(CustomDataGrid));
    private static void OnItemSourcechanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        (d as CustomDataGrid).maingrid.ItemsSource = (IEnumerable)e.NewValue;
    }
    public string CustomRowDetails
    {
        get
        {
            return (string)GetValue(CustomRowDetailsProperty);
        }
        set {
            SetValue(CustomRowDetailsProperty, value);
        }
    }
    private object selItem;
    public object SelItem
    {
        get { return selItem; }
        set {
            selItem = value;
            MyDetails = (IEnumerable<object>)value.GetType().GetProperty(CustomRowDetails).GetValue(value, null); ;
            OnPropertyChanged(() => SelItem);
            OnPropertyChanged(() => MyDetails);
        }
    }
    private IEnumerable<object> myDetails;
    public IEnumerable<object> MyDetails
    {
        get { return myDetails; }
        set { myDetails = value;
            OnPropertyChanged(() => MyDetails);
        }
    }

    #region INotifyPropertyChanged Members
    /// <summary>
    /// Raised when a property on this object has a new value.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
    {
        if (selectorExpression == null)
            throw new ArgumentNullException("selectorExpression");
        MemberExpression body = selectorExpression.Body as MemberExpression;
        if (body == null)
            throw new ArgumentException("The body must be a member expression");
        OnPropertyChanged(body.Member.Name);
    }

    /// <summary>
    /// Raises this object's PropertyChanged event.
    /// </summary>
    /// <param name="propertyName">The property that has a new value.</param>
    protected virtual void OnPropertyChanged(string propertyName)
    {
        //this.VerifyPropertyName(propertyName);
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
        {
            var e = new PropertyChangedEventArgs(propertyName);
            handler(this, e);
        }
    }
    #endregion // INotifyPropertyChanged Members
}