Home > database >  WPF Executing an ICommand on MainWindow from a UserControl inside a Page
WPF Executing an ICommand on MainWindow from a UserControl inside a Page

Time:10-01

I'm showing a list of Orders, (each Order is a UserControl), in more than one Page. Every time the user left-click the control, main window should load & show the order in a new Page using an ICommand.

x:Name of MainWindow is: mainWindow

MainViewModel

This is de view model associated to the MainWindow.

public class MainViewModel : BaseViewModel
{
    public INavigator Navigator { get; set; }
    public ICommand OpenOrderCommand { get; set; }

    public MainViewModel(INavigator navigator) : base()
    {
        Navigator = navigator;
        OpenOrderCommand = new RelayCommand(OpenOrder);
    }

    private void OpenOrder(object obj)
    {
        // Loads the order from db
    }
}

I've added an InputBindings tag in the UserControl to detect when the user left-click it.

UserControl

<Grid.InputBindings>
    <MouseBinding MouseAction="LeftClick" 
                  Command="{Binding DataContext.OpenOrderCommand,
                            RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
                  CommandParameter="{Binding OrderId}"/>
</Grid.InputBindings>

But OpenOrderCommand is never fired, and I get a XAML binding failure with this text:

System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Window', AncestorLevel='1''. BindingExpression:Path=DataContext.OpenOrderCommand; DataItem=null; target element is 'MouseBinding' (HashCode=20815867); target property is 'Command' (type 'ICommand')

How can I execute a ICommand of MainWindow from a UserControl?

CodePudding user response:

Make sure the UserControl and the Window reside in the same element tree. In case there is a tree boundary, e.g. a Frame, the RelativeSource lookup will fail.


Besides that, your control should expose its own ICommand property (like a Button). It would thus not be dependant on the view model of its parent Window.

Add an ICommand dependency property

public static readonly DependencyProperty MyCommandProperty =
    DependencyProperty.Register(
        nameof(MyCommand), typeof(ICommand), typeof(MyUserControl));

public ICommand MyCommand
{
    get { return (ICommand)GetValue(MyCommandProperty); }
    set { SetValue(MyCommandProperty, value); }
}

and bind to it in the UserControl's XAML

<Grid.InputBindings>
    <MouseBinding
        MouseAction="LeftClick" 
        Command="{Binding MyCommand,
                  RelativeSource={RelativeSource AncestorType=UserControl}}"
        CommandParameter="{Binding OrderId}"/>
</Grid.InputBindings>

When you use the control, write

<local:MyUserControl MyCommand="{Binding OpenOrderCommand}"/>

CodePudding user response:

In the error message it says it can't find the binding source. Try to bind it like this:

<Grid.InputBindings>
        <MouseBinding MouseAction="LeftClick" 
                      Command="{Binding OpenOrderCommand}"
                      CommandParameter="{Binding OrderId}"/>
</Grid.InputBindings>

and to set the DataContext like this:

public MainWindow()
{
     InitializeComponent();
     DataContext = new MainViewModel();
}

In my test code the Grid only raised the event when I had explecitly set a value for the Background-Property. Transparent was also a value which worked.

  • Related