Gopichandar
Gopichandar

Reputation: 2832

Passing DataGrid to ContentControl over RowDetail not working as expected

What I need is to have a UserControl that is capable of list items similar to DataGrid which mean it will have list of rows and each row will have a RowDetail for it but with some more additonal feature along with it.

I have tried it with two different scenario.

Scenario 1 - Cannot be reused but Working as expected:

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; }

}

Scenario 2 - Can be reused but not working as expected:

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); }
    }
}

I expected the Scenario 2 will work similar to Scenario 1, but weird thing is it doesn't work similarly.

Problem with Scenarion 2 is, the RowDetail in DataGrid doesn't openup once it got closed by opening the other RowDetail item.

Any ideas on how can I make the Scenario 2 to work similar to scenario 1.

Upvotes: 0

Views: 209

Answers (1)

user6996876
user6996876

Reputation:

I would use a string to bind the name of the details property...

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

with a custom datagrid setting the selected item

<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>

via reflection

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

}

Upvotes: 1

Related Questions