Кнопка Bind в DataTemplate для команды в форме ViewModel

Моя проблема аналогична описанной в этом вопросе:
WPF MVVM Управление привязкой кнопок в DataTemplate

Вот мой XAML:

<Window x:Class="MissileSharp.Launcher.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MissileSharp Launcher" Height="350" Width="525">
    <Grid>
        <!-- when I put the button here (outside the list), the binding works -->
        <!--<Button Content="test" Command="{Binding Path=FireCommand}" />-->
        <ListBox ItemsSource="{Binding CommandSets}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <!-- I need the button here (inside the list), and here the binding does NOT work -->
                    <Button Content="{Binding}" Command="{Binding Path=FireCommand}" />
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Window>

Это просто a ListBox, связанный с ObservableCollection<string> с именем CommandSets (который находится в ViewModel).
Эта привязка работает (она отображает кнопку для каждого элемента в коллекции).

Теперь я хочу привязать кнопку к команде (FireCommand), которая также находится в ViewModel.
Здесь соответствующая часть ViewModel:

public class MainWindowViewModel : INotifyPropertyChanged
{
    public ICommand FireCommand { get; set; }
    public ObservableCollection<string> CommandSets { get; set; }

    public MainWindowViewModel()
    {
        this.FireCommand = new RelayCommand(new Action<object>(this.FireMissile));
    }

    private void FireMissile(Object obj)
    {
        System.Windows.MessageBox.Show("fire");
    }
}

Связывание этой кнопки НЕ работает.
Из того, что я понял из question, связанного выше, привязка не работает, потому что:
(исправьте меня, если я ошибаюсь)

  • Кнопка находится внутри ListBox, поэтому она "знает" привязку ListBox (в этом случае ObservableCollection), но не привязка главного окна
  • Я пытаюсь связать команду в главной ViewModel главного окна (которое кнопка не знает)

Сама команда определенно правильна, потому что, когда я нажимаю кнопку вне ListBox (см. выше приведенный выше пример XAML), выполняется работа по связыванию и команда.

По-видимому, мне просто нужно указать кнопке привязку к основной ViewModel формы.
Но я не могу понять правильный синтаксис XAML.

Я попробовал несколько подходов, которые я нашел после некоторых поисковых запросов, но никто из них не работал у меня:

<Button Content="{Binding}" Command="{Binding RelativeSource={RelativeSource Window}, Path=DataContext.FireCommand}" />

<Button Content="{Binding}" Command="{Binding Path=FireCommand, Source={StaticResource MainWindow}}" />

<Button Content="{Binding}" Command="{Binding Path=FireCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />

Может кто-то угодить:

  • дайте мне правильный XAML, чтобы связать кнопку внутри ListBox с командой в форме MainViewModel?
  • укажите мне ссылку, где этот расширенный материал привязки объясняется так, как может понять начинающий WPF/MVVM?
    Мне кажется, что я просто копирую и вставляю тайные заклинания XAML, и до сих пор у меня нет никакой подсказки (и я не могу найти хорошую документацию), как бы я сам разобрался, в каких случаях мне понадобится RelativeSource или StaticResource или что-то другое вместо "нормального" связывания.

Ответы

Ответ 1

Это:

{Binding DataContext.FireCommand,
         RelativeSource={RelativeSource AncestorType=ListBox}}

Не нужно доходить до корня, если вы фактически не измените DataContext на этом пути, но поскольку ListBox, похоже, привязывается к свойству на главной виртуальной машине, этого должно быть достаточно.

Единственное, что я рекомендую прочитать, это Обзор привязки данных и Binding класс (включая его свойства).


Также приведено краткое объяснение того, как строятся привязки: привязка состоит из источника и Path относительно этого источника, по умолчанию источником является текущий DataContext. Источники, которые могут быть установлены явно: Source, ElementName и RelativeSource. Установка любого из них приведет к переопределению источника DataContext.

Итак, если вы используете источник типа RelativeSource и хотите получить доступ к чему-то в DataContext на этом уровне, DataContext должен появиться в Path.

Ответ 2

Большинству это может показаться несвязанным, но этот поиск - только 1 из 3 результатов, которые вы найдете при поиске команд привязки данных к элементам управления внутри шаблона данных - поскольку это относится к формам Xamarin. Так что, может быть, это поможет кому-то сейчас.

Как и я, вы можете задаться вопросом, как связывать команды внутри BindableLayout. Благодарим jesulink2514 за ответы на форумах Xamarin, где многие, возможно, не заметили его из-за всех комментариев. Здесь его решение, но я включаю ссылку ниже:

<ContenPage x:Name="MainPage">
<ListView Grid.Row="1"
              ItemsSource="{Binding Customers}"
              VerticalOptions="Fill"
              x:Name="ListviewCustomer">
      <ListView.ItemTemplate>
        <DataTemplate>
      <Label Text="{Binding Property}"/>
          <Button Command="{Binding BindingContext.ItemCommand, Source={x:Reference MainPage}}" 
                         CommandParameter="{Binding .}">Click me</Button>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>
</ContentPage>

https://forums.xamarin.com/discussion/comment/217355/#Comment_217355