I have a DataGrid within an ItemsControl, like shown below (Representing a collection within a collection).
When I place a button within a DataGrid cell, can I bind the command so that 2 parameters are passed into it? In this case I want to pass the instances of (Item,SubItem) that corresponds to the specific button.
How can I do this?
From what I have researched, I can make a helper class, which has the (Item,SubItem) set, and then pass that into the command. But I don't know how to go about actually doing it.
The code snippets below show what I am trying to do.
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<DataGrid ItemsSource="{Binding SubItems}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" Width="60">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Command="{Binding ???, Path=DataContext.Command}"
CommandParameter="{Binding ???}">
<TextBlock Text="x"/>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
class ViewModel
{
public ViewModel()
{
Items = new ObservableCollection<Item>;
}
public ObservableCollection<Item> Items { get; set; }
public RelayCommand Command => new RelayCommand(execute => SomeMethod((Item, SubItem) execute), canExecute => true);
private void SomeMethod(Item item, SubItem subItem)
{
//Method Logic
}
}
class Item
{
public Item()
{
SubItems = new ObservableCollection<SubItem>;
}
public ObservableCollection<SubItem> SubItems { get; set; }
public string Name;
public double Property;
etc...
}
class SubItem
{
public SubItem()
{
}
public string Name;
public double Property;
etc...
}
I have a DataGrid within an ItemsControl, like shown below (Representing a collection within a collection).
When I place a button within a DataGrid cell, can I bind the command so that 2 parameters are passed into it? In this case I want to pass the instances of (Item,SubItem) that corresponds to the specific button.
How can I do this?
From what I have researched, I can make a helper class, which has the (Item,SubItem) set, and then pass that into the command. But I don't know how to go about actually doing it.
The code snippets below show what I am trying to do.
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<DataGrid ItemsSource="{Binding SubItems}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" Width="60">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Command="{Binding ???, Path=DataContext.Command}"
CommandParameter="{Binding ???}">
<TextBlock Text="x"/>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
class ViewModel
{
public ViewModel()
{
Items = new ObservableCollection<Item>;
}
public ObservableCollection<Item> Items { get; set; }
public RelayCommand Command => new RelayCommand(execute => SomeMethod((Item, SubItem) execute), canExecute => true);
private void SomeMethod(Item item, SubItem subItem)
{
//Method Logic
}
}
class Item
{
public Item()
{
SubItems = new ObservableCollection<SubItem>;
}
public ObservableCollection<SubItem> SubItems { get; set; }
public string Name;
public double Property;
etc...
}
class SubItem
{
public SubItem()
{
}
public string Name;
public double Property;
etc...
}
Share
Improve this question
asked Feb 12 at 20:40
mmdnmmdn
536 bronze badges
3
|
1 Answer
Reset to default 0One we get into these complex hierarchies it can be more of an art than a science and there may not be a "right" way. Here's one approach. So, you said:
I want to pass the instances of (Item,SubItem) that corresponds to the specific button.
In my "personal" view the path of least resistance to give you those two objects is:
- Just call the command on the context you're actually holding which is
SubItem
- The
CommandParameter
is theSubItem
itself{Binding .}
. - SubItems and Items are Parented in the respective models. They can easily traverse in the models without having to do gyrations in the XAML.
XAML Command Binding
<Window.DataContext>
<local:ViewModel></local:ViewModel>
</Window.DataContext>
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<DataGrid
ItemsSource="{Binding SubItems}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Padding" Value="5,1"/>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button
Command="{Binding ButtonXCommand}"
CommandParameter="{Binding .}">
<TextBlock Text="x"/>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Command Target is SubItem
When the [x] button is clicked the command target is in the Subitem class. To react, tell the SubItem.Parent to "do something" like in this example where we say we say "remove me".
partial class SubItem : ObservableObject
{
[ObservableProperty]
string? _name;
[ObservableProperty]
double _property;
public Item? Parent { get; internal set; }
[RelayCommand]
private void ButtonX(SubItem subItem)
{
Parent?.SubItems.Remove(subItem);
}
}
But you might ask, how does SubItem come to have a Parent Item?
Item Model
In this approach to things, SubItem
has a Parent
because Item
reacts to additions to the SubItems list by setting itself as the Parent. In this example, Item
is listening to removals of SubItems. When the count goes to zero, it tells its Parent.Items
to "remove me".
partial class Item : ObservableObject
{
public ViewModel? Parent { get; internal set; }
[ObservableProperty]
ObservableCollection<SubItem> _subItems = new();
[ObservableProperty]
string? _name;
[ObservableProperty]
double _property;
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.PropertyName == nameof(SubItems))
{
if (SubItems != null)
{
SubItems.ToList().ForEach(_ => _.Parent = this);
SubItems.CollectionChanged += CollectionChangedListener;
}
}
}
private void CollectionChangedListener(object? sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
e.NewItems?.OfType<SubItem>().ToList().ForEach(_ => _.Parent = this);
break;
case NotifyCollectionChangedAction.Remove:
if (e.OldItems?.OfType<SubItem>().FirstOrDefault()?.Parent is Item item)
{
if (!item.SubItems.Any()) Parent?.Items.Remove(item);
}
break;
}
}
}
View Model
Like SubItem, Item has a Parent because ViewModel reacts to additions to the Items list by setting itself as the Parent.
partial class ViewModel : ObservableObject
{
[ObservableProperty]
ObservableCollection<Item> _items = new();
[RelayCommand]
private void ButtonX(object item)
{
if (item != null)
{
//Items.Remove(item); // Example: Removes the clicked item
}
}
protected override void OnPropertyChanged(PropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if(e.PropertyName == nameof(Items))
{
if(Items != null)
{
Items.ToList().ForEach(_ => _.Parent = this);
Items.CollectionChanged += CollectionChangedListener;
}
}
}
private void CollectionChangedListener(object? sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
e.NewItems?.OfType<Item>().ToList().ForEach(_ => _.Parent = this);
break;
}
}
}
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745203280a4616456.html
Item
,SubItem
)" be able to easily interact with each other. This is the reason (in my mind) for setting up a good parenting hierarchy that maintains itself through any changes to the respective collections. – IV. Commented Feb 13 at 11:14