Silverlight: привязка команды кнопки в заголовке DataGrid в ItemsControl
У меня есть небольшая проблема, связывающая команду кнопки со свойством "внешнего" контекста данных.
Изображение ниже показывает макет, который у меня есть. Я пытаюсь привязать CommandParameter кнопки "Очистить" (выделено красным) к LocationId. ItemsControl повторяет ObservableCollection of Locations (определение местоположения см. Ниже).
Кнопка Очистить в основном пытается УДАЛИТЬ Адреса, прикрепленные к Местоположению, чтобы очистить DataGrid. Для этого мне нужно передать LocationId ViewModel.
Фактическая команда срабатывает отлично, но привязка к CommandParameter не совсем правильная.
Вот основные классы Location & Address:
class Location
{
int Id;
ObservableCollection<Address> Addresses;
}
class Address
{
string AddressText;
}
А вот XAML с тремя альтернативными попытками и сообщениями об ошибках:
<ItemsControl ItemsSource="{Binding Locations, Mode=TwoWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel VerticalAlignment="Stretch" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Id}"/>
<sdk:DataGrid x:Name="ResponseDataGrid" ItemsSource="{Binding Addresses}">
<sdk:DataGrid.Columns>
<sdk:DataGridTextColumn Header="Response" Width="*" Binding="{Binding AddressText}"/>
<sdk:DataGridTemplateColumn Width="100">
<sdk:DataGridTemplateColumn.HeaderStyle>
<Style TargetType="sdk:DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Button Content="Clear"
Command="{Binding DataContext.ClearLocationCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding Id, RelativeSource={RelativeSource AncestorType=ItemsControl}}"/>
<!--System.Windows.Data Error: BindingExpression path error: 'Id' property not found on 'System.Windows.Controls.ItemsControl' -->
<!--CommandParameter="{Binding ItemsSource.Id, RelativeSource={RelativeSource AncestorType=ItemsControl}}"/>-->
<!--System.Windows.Data Error: BindingExpression path error: 'Id' property not found on 'System.Collections.ObjectModel.ObservableCollection`1[UI.Location]' -->
<!--This gives me the ID but uses a specific index so only works for the first repeated Location-->
<!--CommandParameter="{Binding ItemsSource[0].Id, RelativeSource={RelativeSource AncestorType=ItemsControl}}"/>-->
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</sdk:DataGridTemplateColumn.HeaderStyle>
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Accept">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Click">
<i:InvokeCommandAction
Command="{Binding DataContext.SelectedAddressCommand , RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding SelectedItem, RelativeSource={RelativeSource AncestorType=sdk:DataGrid}}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
</sdk:DataGrid.Columns>
</sdk:DataGrid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Из-за ошибок кажется, что кнопка не может полностью увидеть повторяющийся объект Location, она может видеть либо коллекцию Locations, либо конкретное проиндексированное Location, но не повторяющееся, сгенерированное Location, которое мне НУЖНО!
Спасибо!
2 ответа
Итак, у вас есть ViewModel с ObservableCollection<Location>
И нажав на Очистить очищает ObservableCollection<Address>
указанного Места, верно?
Почему бы вам просто не поместить команду Clear в класс Location? Я не знаю о логике, запускаемой этой командой, но при этом вы сможете получить доступ к правильному свойству местоположения Id.
Редактировать: вот мой пример работы:
Классы
public class Location
{
public Location(int id, IEnumerable<Address> addresses)
{
this.Id = id;
this.Addresses =new ObservableCollection<Address>(addresses);
this.ClearLocationCommand = new RelayCommand<int>(e => MessageBox.Show(string.Format("Clear command called on Location {0}", this.Id)));
}
public int Id { get; set; }
public ObservableCollection<Address> Addresses { get; set; }
public ICommand ClearLocationCommand { get; set; }
}
public class Address
{
public Address(string text)
{
this.AddressText = text;
}
public string AddressText { get; set; }
}
public class MainWindowViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public MainWindowViewModel()
{
Locations = new ObservableCollection<Location>(new []
{
new Location(1, new [] { new Address("A1") }),
new Location(2, new [] { new Address("A2"), new Address("A3"), }),
});
}
public ObservableCollection<Location> Locations { get; set; }
private void OnPropertyChanged(string porpertyName)
{
var e = this.PropertyChanged;
if (e != null)
{
e(this, new PropertyChangedEventArgs(porpertyName));
}
}
}
XAML
<Window x:Class="TestBindingButtons.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:TestBindingButtons="clr-namespace:TestBindingButtons" Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<TestBindingButtons:MainWindowViewModel />
</Window.DataContext>
<Grid>
<ItemsControl ItemsSource="{Binding Locations, Mode=TwoWay}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel VerticalAlignment="Stretch" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Id}"/>
<DataGrid x:Name="ResponseDataGrid" ItemsSource="{Binding Addresses}">
<DataGrid.Columns>
<DataGridTextColumn Header="Response" Width="*" Binding="{Binding AddressText}"/>
<DataGridTemplateColumn Width="100">
<DataGridTemplateColumn.HeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Button Content="Clear"
Command="{Binding DataContext.ClearLocationCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridTemplateColumn.HeaderStyle>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Accept">
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
PD: Я не размещал реализацию RelayCommand, используйте свою собственную. Я не использовал ваш "SDK" фреймворк, просто обычный материал для Microsoft.
Если вы переместите ClearLocationCommand
собственность на Location
класс, не забудьте изменить следующие привязки:
<Button Content="Clear"
Command="{Binding DataContext.ClearLocationCommand, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
CommandParameter="{Binding Id, RelativeSource={RelativeSource AncestorType=ItemsControl}}"
/>
чтобы:
<Button Content="Clear"
Command="{Binding ClearLocationCommand}"
CommandParameter="{Binding Id}"
/>
Потому что каждый внешний Location
объект теперь имеет свою собственную команду очистки, и вы уже на правильном DataContext
в этом ряду предметов.