Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I Bind WPF Commands between a UserControl and a parent Window

People also ask

What is command binding in WPF?

The command is the action to be executed. The command source is the object which invokes the command. The command target is the object that the command is being executed on. The command binding is the object which maps the command logic to the command.

What is difference between user control and window in WPF?

A window is managed by the OS and is placed on the desktop. A UserControl is managed by wpf and is placed in a Window or in another UserControl. Applcations could be created by have a single Window and displaying lots of UserControls in that Window.

How do I close a Windows UserControl WPF?

or by simply: var myWindow = Window. GetWindow(this); myWindow. Close();


This is trivial, and made so by treating your UserControl like what it is--a control (that just happens to be made up from other controls). What does that mean? It means you should place DependencyProperties on your UC to which your ViewModel can bind, like any other control. Buttons expose a Command property, TextBoxes expose a Text property, etc. You need to expose, on the surface of your UserControl, everything you need for it to do its job.

Let's take a trivial (thrown together in under two minutes) example. I'll leave out the ICommand implementation.

First, our Window

<Window x:Class="UCsAndICommands.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:t="clr-namespace:UCsAndICommands"
        Title="MainWindow" Height="350" Width="525">
    <Window.DataContext>
        <t:ViewModel />
    </Window.DataContext>
    <t:ItemsEditor Items="{Binding Items}"
                   AddItem="{Binding AddItem}"
                   RemoveItem="{Binding RemoveItem}" />
</Window>

Notice we have our Items editor, which exposes properties for everything it needs--the list of items it is editing, a command to add a new item, and a command to remove an item.

Next, the UserControl

<UserControl x:Class="UCsAndICommands.ItemsEditor"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:t="clr-namespace:UCsAndICommands"
             x:Name="root">
    <UserControl.Resources>
        <DataTemplate DataType="{x:Type t:Item}">
            <StackPanel Orientation="Horizontal">
                <Button Command="{Binding RemoveItem, ElementName=root}"
                        CommandParameter="{Binding}">Remove</Button>
                <TextBox Text="{Binding Name}" Width="100"/>
            </StackPanel>
        </DataTemplate>
    </UserControl.Resources>
    <StackPanel>
        <Button Command="{Binding AddItem, ElementName=root}">Add</Button>
        <ItemsControl ItemsSource="{Binding Items, ElementName=root}" />
    </StackPanel>
</UserControl>

We bind our controls to the DPs defined on the surface of the UC. Please, don't do any nonsense like DataContext=this; as this anti-pattern breaks more complex UC implementations.

Here's the definitions of these properties on the UC

public partial class ItemsEditor : UserControl
{
    #region Items
    public static readonly DependencyProperty ItemsProperty =
        DependencyProperty.Register(
            "Items",
            typeof(IEnumerable<Item>),
            typeof(ItemsEditor),
            new UIPropertyMetadata(null));
    public IEnumerable<Item> Items
    {
        get { return (IEnumerable<Item>)GetValue(ItemsProperty); }
        set { SetValue(ItemsProperty, value); }
    }
    #endregion  
    #region AddItem
    public static readonly DependencyProperty AddItemProperty =
        DependencyProperty.Register(
            "AddItem",
            typeof(ICommand),
            typeof(ItemsEditor),
            new UIPropertyMetadata(null));
    public ICommand AddItem
    {
        get { return (ICommand)GetValue(AddItemProperty); }
        set { SetValue(AddItemProperty, value); }
    }
    #endregion          
    #region RemoveItem
    public static readonly DependencyProperty RemoveItemProperty =
        DependencyProperty.Register(
            "RemoveItem",
            typeof(ICommand),
            typeof(ItemsEditor),
            new UIPropertyMetadata(null));
    public ICommand RemoveItem
    {
        get { return (ICommand)GetValue(RemoveItemProperty); }
        set { SetValue(RemoveItemProperty, value); }
    }        
    #endregion  
    public ItemsEditor()
    {
        InitializeComponent();
    }
}

Just DPs on the surface of the UC. No biggie. And our ViewModel is similarly simple

public class ViewModel
{
    public ObservableCollection<Item> Items { get; private set; }
    public ICommand AddItem { get; private set; }
    public ICommand RemoveItem { get; private set; }
    public ViewModel()
    {
        Items = new ObservableCollection<Item>();
        AddItem = new DelegatedCommand<object>(
            o => true, o => Items.Add(new Item()));
        RemoveItem = new DelegatedCommand<Item>(
            i => true, i => Items.Remove(i));
    }
}

You are editing three different collections, so you may want to expose more ICommands to make it clear which you are adding/removing. Or you could cheap out and use the CommandParameter to figure it out.


Refer the below code. UserControl.XAML

<Grid>
    <ListBox ItemsSource="{Binding Things}" x:Name="lst">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding ThingName}" Margin="3"/>
                    <Button Content="Remove" Margin="3" Command="{Binding ElementName=lst, Path=DataContext.RemoveCommand}" CommandParameter="{Binding}"/>
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
</Grid>

Window.Xaml

<Window x:Class="MultiBind_Learning.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MultiBind_Learning"
    Title="Window1" Height="300" Width="300">
<StackPanel Orientation="Horizontal">
    <Button Content="Add" Width="50" Height="25" Command="{Binding AddCommnd }"/>
    <local:UserControl2/>
</StackPanel>

Window.xaml.cs

public partial class Window1 : Window
{
    public Window1()
    {
        InitializeComponent();
        this.DataContext = new ThingViewModel();
    }
}

ThingViewModel.cs

  class ThingViewModel
{
    private ObservableCollection<Thing> things = new ObservableCollection<Thing>();

    public ObservableCollection<Thing> Things
    {
        get { return things; }
        set { things = value; }
    }

    public ICommand AddCommnd { get; set; }
    public ICommand RemoveCommand { get; set; }

    public ThingViewModel()
    {
        for (int i = 0; i < 10; i++)
        {
            things.Add(new Thing() { ThingName="Thing" +i});
        }

        AddCommnd = new BaseCommand(Add);
        RemoveCommand = new BaseCommand(Remove);
    }

    void Add(object obj)
    {
      things.Add(new Thing() {ThingName="Added New" });
    }

    void Remove(object obj)
    {
      things.Remove((Thing)obj);
    }
}

Thing.cs

class Thing :INotifyPropertyChanged
{
    private string thingName;

    public string ThingName
    {
        get { return thingName; }
        set { thingName = value; OnPropertyChanged("ThingName"); }
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void OnPropertyChanged(string propName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propName));
        }
    }
}

BaseCommand.cs

public class BaseCommand : ICommand
{
    private Predicate<object> _canExecute;
    private Action<object> _method;
    public event EventHandler CanExecuteChanged;

    public BaseCommand(Action<object> method)
    {
        _method = method;            
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        _method.Invoke(parameter);
    }
}

Instead of Base command you can try RelayCommand from MVVMLight or DelegateCommand from PRISM libraries.


By default, your user control will inherit the DataContext of its container. So the ViewModel class that your window uses can be bound to directly by the user control, using the Binding notation in XAML. There's no need to specify DependentProperties or RoutedEvents, just bind to the command properties as normal.