9

In MainWindow the commandbinding works fine. In UserControl1 it doesnt work. Note the datacontext is set correctly as is evidenced by the content of the button which is the result of a binding.

I am not trying to bind the command in the usercontrol to a command in mainwindow or any other such trickery. I am just trying to replicate what I did in MainWindow in UserControl1.

MainWindow XAML

<StackPanel>
    <Button Content="Click Here" Command="{Binding ClickHereCommand}" Height="25" Width="90"></Button>
    <local:UserControl1></local:UserControl1>
</StackPanel>

MainWindow Code Behind

public partial class MainWindow : Window
{
    public static RoutedCommand ClickHereCommand { get; set; }

    public MainWindow()
    {
        InitializeComponent();
        this.DataContext = this;
        ClickHereCommand = new RoutedCommand();
        CommandBindings.Add(new CommandBinding(ClickHereCommand, ClickHereExecuted));
    }

    public void ClickHereExecuted(object sender, ExecutedRoutedEventArgs e)
    {
        System.Windows.MessageBox.Show("hello");
    }
}

UserControl XAML

<UserControl x:Class="CommandBindingTest.UserControl1"
         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" 
         mc:Ignorable="d" 
         d:DesignHeight="300" d:DesignWidth="300" x:Name="root">

<Grid DataContext="{Binding ElementName=root}" >
    <Button Content="{Binding ButtonContent}" Command="{Binding ClickHereCommand}" Height="25" Width="90"></Button>
</Grid>
</UserControl>

UserControl Code Behind

public partial class UserControl1 : UserControl, INotifyPropertyChanged
{
    private string _ButtonContent;
    public string ButtonContent
    {
        get { return _ButtonContent; }
        set
        {
            if (_ButtonContent != value)
            {
                _ButtonContent = value;
                OnPropertyChanged("ButtonContent");
            }
        }
    }

    public static RoutedCommand ClickHereCommand { get; set; }


    public UserControl1()
    {
        InitializeComponent();
        ClickHereCommand = new RoutedCommand();
        CommandBindings.Add(new CommandBinding(ClickHereCommand, ClickHereExecuted));
        ButtonContent = "Click Here";
    }

    public void ClickHereExecuted(object sender, ExecutedRoutedEventArgs e)
    {
        System.Windows.MessageBox.Show("hello from UserControl1");
    }


    #region INotifyPropertyChanged Members
    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged(string name)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(name));
        }
    }
    #endregion
}
1
  • Are you checking the Output window for errors? Are there any? Commented Oct 23, 2012 at 17:23

2 Answers 2

15

It's the best solution:

 <Grid DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" >
        <Button Content="{Binding ButtonContent}" Command="{Binding ClickHereCommand}" Height="25" Width="90"></Button>
 </Grid>

Other solutions:

You forgot set DataContext to UserControl1.

  public UserControl1()
        {
            InitializeComponent();
            ClickHereCommand = new RoutedCommand();
            CommandBindings.Add(new CommandBinding(ClickHereCommand, ClickHereExecuted));
            ButtonContent = "Click Here";
            this.DataContext = this;
        }

And after this you must delete in UserControl1 DataContext in Grid.

This:

<Grid DataContext="{Binding ElementName=root}" >
    <Button Content="{Binding ButtonContent}" Command="{Binding ClickHereCommand}" Height="25" Width="90"></Button>
</Grid>

you must change to this:

<Grid>
        <Button Content="{Binding ButtonContent}" Command="{Binding ClickHereCommand}" Height="25" Width="90"></Button>
</Grid>

Solution without set DataContext in UserControl:

You must change ButtonContent and ClickHereCommand to DependencyProperty.

        public string ButtonContent
        {
            get { return (string)GetValue(ButtonContentProperty); }
            set { SetValue(ButtonContentProperty, value); }
        }

        public static readonly DependencyProperty ButtonContentProperty =
            DependencyProperty.Register("ButtonContent", typeof(string), typeof(UserControl1), new UIPropertyMetadata(string.Empty));

        public RoutedCommand ClickHereCommand
        {
            get { return (RoutedCommand)GetValue(ClickHereCommandProperty); }
            set { SetValue(ClickHereCommandProperty, value); }
        }

        public static readonly DependencyProperty ClickHereCommandProperty =
            DependencyProperty.Register("ClickHereCommand", typeof(RoutedCommand), typeof(UserControl1), new UIPropertyMetadata(null));

And in ctor of UserControl1:

 public UserControl1()
    {
        InitializeComponent();

        ClickHereCommand = new RoutedCommand();
        CommandBindings.Add(new CommandBinding(ClickHereCommand, ClickHereExecuted));            
        ButtonContent = "Click Here";
        //this.DataContext = this;
    }
Sign up to request clarification or add additional context in comments.

7 Comments

thank you for your answer but I beleive it is not correct. As I stated in my original question, "datacontext is set correctly as is evidenced by the content of the button". I intetionally set datacontext this way because in my app UserControl will contain dependency properties and I specifically do not want to set DataContext = this.
Based on your response I changed DataContext in the grid as follows: DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" and it works. It is very strange that the binding for the button content worked fine but the command binding did not.....
@Sam when I used your code first time the binding for Button content did not working for me. It will be working when you will change your properties to DependencyProperty. In this case you can use your binding DataContext="{Binding ElementName=root}".
It is not necessary to make those properties dependency properties as they are not the targets of bindings (i.e. they work just fine in MainWindow). This looks like some quirky wpf bug. I would like to accept what you wrote as an answer because you steered me in the right direction however neither of your answers quite hit the mark. Please propose setting data context as I described above and I will accept your answer (You were headed in the right direction).
Hmmm... interesting, I don't see any difference between DataContext="{Binding ElementName=root}" and DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}". In both case DataContext is UserControl1.
|
12

Here I am AGAIN almost two years after I posted this. I forgot about this little feature of WPF and sure enough it bit me again.

The answer marked above is partially correct but contains other content that is distracting and/or incorrect. For clarity I will highlight what the problem is and what the solution is. I'm doing this more for my benefit than yours because I'm sure I'll be back here in about sixteen months.

Here is the problem. Don't do this. Your bindings will all continue to work except for your routed commands:

<UserControl x:Class="CommandBindingTest.UserControl1"
     // snip
     d:DesignHeight="300" d:DesignWidth="300" x:Name="root">

    <Grid DataContext="{Binding ElementName=root}" >
        <Button Content="{Binding ButtonContent}" Command="{Binding ClickHereCommand}" Height="25" Width="90">
        </Button>
    </Grid>

The solution is use relative source in setting the data context as follows:

<Grid DataContext="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" >
    <Button Content="{Binding ButtonContent}" Command="{Binding ClickHereCommand}" Height="25" Width="90">
    </Button>
</Grid>

The only answer to the problem I asked about is the one above (there may in fact be other answers but they are not discussed thus far in this question). Specifically: Setting "DataContext = this" in the usercontrol is NOT a solution and will in fact break bindings in the control that hosts the user control. Also, only properties that are the targets of bindings must be dependency properties. That suggestion is incorrect .

3 Comments

It turns out your problem is simply a misconception on how to bind DataContext inside UserControls. If you get this, you will always remeber. Read stackoverflow.com/a/19199045/711061 it is enlightening
@sam How is the solution in the link you provided different from the solution provided in this question?
Altought the solution is the same, you don't know why you have to write that code; as a consequence you stumble every once in a while in this problem. I just wanted to help calrify a bit more the meaning of this code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.