C
C#2y ago
Davaaron

❔ WPF - Use command of viewmodel instead of item on click

Hi, I have a list of strings and display them as buttons. The list is held by a viewmodel and the viewmodel has a Command for the button click. Now, instead of creating an object for strings and add the Command to the items, I would like to have the Command at the viewmodel level.
<Grid x:Name="LayoutRoot" Margin="5">
<ItemsControl ItemsSource="{Binding Options}" Grid.Row="1">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding CloseDialogCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type vm:NotificationDialogViewModel}}}" CommandParameter="true" Content="{Binding }" MinWidth="75" Height="25" Margin="10,5,10,5" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
<Grid x:Name="LayoutRoot" Margin="5">
<ItemsControl ItemsSource="{Binding Options}" Grid.Row="1">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding CloseDialogCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type vm:NotificationDialogViewModel}}}" CommandParameter="true" Content="{Binding }" MinWidth="75" Height="25" Margin="10,5,10,5" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
public class NotificationDialogViewModel : BindableBase, IDialogAware
{
private DelegateCommand<string> _closeDialogCommand;
public DelegateCommand<string> CloseDialogCommand =>
_closeDialogCommand ?? (_closeDialogCommand = new DelegateCommand<string>(CloseDialog));

protected virtual void CloseDialog(string parameter)
{
ButtonResult result = ButtonResult.None;

if (parameter?.ToLower() == "true")
result = ButtonResult.OK;
else if (parameter?.ToLower() == "false")
result = ButtonResult.Cancel;

RaiseRequestClose(new DialogResult(result, new DialogParameters($"parameter={parameter}")));
}

}
public class NotificationDialogViewModel : BindableBase, IDialogAware
{
private DelegateCommand<string> _closeDialogCommand;
public DelegateCommand<string> CloseDialogCommand =>
_closeDialogCommand ?? (_closeDialogCommand = new DelegateCommand<string>(CloseDialog));

protected virtual void CloseDialog(string parameter)
{
ButtonResult result = ButtonResult.None;

if (parameter?.ToLower() == "true")
result = ButtonResult.OK;
else if (parameter?.ToLower() == "false")
result = ButtonResult.Cancel;

RaiseRequestClose(new DialogResult(result, new DialogParameters($"parameter={parameter}")));
}

}
6 Replies
Davaaron
DavaaronOP2y ago
The error is: System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='Davaaron.BankOverview.ViewModels.Dialogs.NotificationDialogViewModel', AncestorLevel='1''. BindingExpression:Path=CloseDialogCommand; DataItem=null; target element is 'Button' (Name=''); target property is 'Command' (type 'ICommand')
HimmDawg
HimmDawg2y ago
Your AncestorType is a ViewModel. It's must always be a control tho
Davaaron
DavaaronOP2y ago
Oh okay.. so I have to add some kind of event to the items (buttons) and then listen in the NotificationDialogViewModel.. alright
Klarth
Klarth2y ago
Your VM method is a bit weird given it basically has the result twice: RaiseRequestClose(new DialogResult(result, new DialogParameters($"parameter={parameter}"))); But besides that, your binding isn't correct. Indirect bindings go through controls first. I prefer ElementName bindings here because they're simpler to write.
<Grid x:Name="LayoutRoot" Margin="5">
<ItemsControl x:Name="options" ItemsSource="{Binding Options}" Grid.Row="1">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding DataContext.CloseDialogCommand, ElementName=options}" CommandParameter="true" Content="{Binding}" MinWidth="75" Height="25" Margin="10,5,10,5" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
<Grid x:Name="LayoutRoot" Margin="5">
<ItemsControl x:Name="options" ItemsSource="{Binding Options}" Grid.Row="1">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Command="{Binding DataContext.CloseDialogCommand, ElementName=options}" CommandParameter="true" Content="{Binding}" MinWidth="75" Height="25" Margin="10,5,10,5" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
So the Command binds to the ItemsControl, then into its DataContext (your VM...which I assume is the one you want?).
Davaaron
DavaaronOP2y ago
Thank you very much, that worked!! 🙂
Accord
Accord2y ago
Was this issue resolved? If so, run /close - otherwise I will mark this as stale and this post will be archived until there is new activity.
Want results from more Discord servers?
Add your server