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 в этом ряду предметов.

Другие вопросы по тегам