Reputation: 2832
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.
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; }
}
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
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