1

I'm binding the command property of two hyperlinks in my xaml to a command in the view (which is the datacontext):

<TextBlock>
   <Hyperlink x:Name="linkCheckAll" Command="{Binding CheckAllZonesCommand}" CommandParameter="{Binding}">Check All</Hyperlink>
   <TextBlock Margin="0,0,20,0"/>
   <Hyperlink x:Name="linkUncheckAll" Command="{Binding UncheckAllZonesCommand}" CommandParameter="{Binding}">Uncheck All</Hyperlink>
</TextBlock>

Which looks like this:

enter image description here

I'm getting the following error when binding my commands:

System.Windows.Data Error: 40 : BindingExpression path error: 'CheckAllZonesCommand' property not found on 'object' ''ZonesView' (HashCode=56756307)'. BindingExpression:Path=CheckAllZonesCommand; DataItem='ZonesView' (HashCode=56756307); target element is 'Hyperlink' (HashCode=50738642); target property is 'Command' (type 'ICommand')
System.Windows.Data Error: 40 : BindingExpression path error: 'UncheckAllZonesCommand' property not found on 'object' ''ZonesView' (HashCode=56756307)'. BindingExpression:Path=UncheckAllZonesCommand; DataItem='ZonesView' (HashCode=56756307); target element is 'Hyperlink' (HashCode=53994596); target property is 'Command' (type 'ICommand')

"ZonesView" is my my dataContext, and I'm positive it contains the commands in question:

public class ZonesView : BaseViewModel
{
    public static ICommand CheckAllZonesCommand = new DelegateCommand()
    {
        ExecuteMethod = new Action<object>(delegate(object o) { ((ZonesView)o).CheckAllZones(); }),
        CanExecuteMethod = new Func<bool>(delegate() { return true; })
    };
    public void CheckAllZones()
    {
        foreach( CheckBox cb in ZonesCheckBoxes.Values.Where(cb => (cb.IsChecked != true) && cb.Name.Contains((String)ActiveTab.Header) ))
        {
            cb.IsChecked = true;
            ZoneCheckBoxClicked(cb, null);
        }
    }

    public static ICommand UncheckAllZonesCommand = new DelegateCommand()
    {
        ExecuteMethod = new Action<object>(delegate(object o) { ((ZonesView)o).UncheckAllZones(); }),
        CanExecuteMethod = new Func<bool>(delegate() { return true; })
    };
    public void UncheckAllZones()
    {
        foreach( CheckBox cb in ZonesCheckBoxes.Values.Where(cb => (cb.IsChecked != false) && cb.Name.Contains((String)ActiveTab.Header)) )
        {
            cb.IsChecked = false;
            ZoneCheckBoxClicked(cb, null);
        }
    }

From what I can tell, I've done everything right. The commands are public, they are the correct type, and the datacontext of the hyperlinks is correct (as you can tell by the BindingExpression path error message) - so what's going wrong?


I've tried making the Commands in the ZonesView class static, but it didn't change anything.

Alain
  • 26,663
  • 20
  • 114
  • 184

3 Answers3

7

Since my comment raised no attention whatsoever: you should be binding to properties, but you do to fields instead. Explanation of why it's done this way has already been provided in this question:

Change CheckAllZonesCommand and UncheckAllZonesCommand to properties:

public ICommand UncheckAllZonesCommand { get; set; }
public ICommand CheckAllZonesCommand { get; set; }

And initialize them in, let's say - constructor.

Community
  • 1
  • 1
k.m
  • 30,794
  • 10
  • 62
  • 86
  • Sorry, I was busy working on my own solution so I didn't catch your comment until the three of us has posted our solutions. You nailed the problem - I wasn't realizing the difference between defining a 'property' and a 'field', which to me just becoming known as an unbindable property. – Alain Jan 24 '12 at 19:21
  • @Alain: properties are more than just a [fancy field](http://msdn.microsoft.com/en-us/library/bb384054.aspx). If the concept isn't very clear, there's [many](http://stackoverflow.com/questions/1523548/why-we-need-properties-in-c-sharp) [great](http://stackoverflow.com/questions/205691/new-automatic-properties-in-c-sharp-3-0-whats-the-benefit) questions here on SO which you can check. – k.m Jan 24 '12 at 20:08
1

try something like this... so that you are biding to properties, not fields. you may even want to raise PropertyChanged events in the setter.

public class ZonesView : BaseViewModel
{
   public ZonesView()
   {
      this.CheckAllZonesCommand = new DelegateCommand()
      {
         ExecuteMethod = new Action<object>(delegate(object o){ ((ZonesView)o).CheckAllZones(); }),
         CanExecuteMethod = new Func<bool>(delegate() { return true; })
       };
   }

    public ICommand CheckAllZonesCommand {get;private set;}
}
Muad'Dib
  • 28,542
  • 5
  • 55
  • 68
  • This is nice because it avoids having to create a new private property associated with each command. It feels strange to have to define command behaviour every instantiation though - since it's a statically defined class behaviour. – Alain Jan 24 '12 at 19:19
  • you could define your commands in a seperate static class, and make the datamodle properties like "wrappers"... there are many many options – Muad'Dib Jan 24 '12 at 19:47
0

I guess the solution is similar to some of the things posted, but it isn't the same as any of them. I'm not sure about the subtleties of this difference, but by merely making my commands private, and then returning them through a public property get method, it started working:

public ICommand PublicCommand { get { return _privateCommand; } }

Here's the fix to the above code:

protected static DelegateCommand _CheckAllZonesCommand = new DelegateCommand()
{
    ExecuteMethod = new Action<object>(delegate(object o) { ((ZonesView)o).CheckAllZones(); }),
    CanExecuteMethod = new Func<bool>(delegate() { return true; })
};
public ICommand CheckAllZonesCommand { get { return _CheckAllZonesCommand; } }

protected static DelegateCommand _UncheckAllZonesCommand = new DelegateCommand()
{
    ExecuteMethod = new Action<object>(delegate(object o) { ((ZonesView)o).UncheckAllZones(); }),
    CanExecuteMethod = new Func<bool>(delegate() { return true; })
};
public ICommand UncheckAllZonesCommand { get { return _UncheckAllZonesCommand; } }
Alain
  • 26,663
  • 20
  • 114
  • 184
  • It is basically the same as the other two answers - you're exposing the commands via properties rather than fields. The subtle differences are when the field objects the properties expose get initialized. Your answer does this when the VM class is first used, as they are static. Muad'Dib's answer initializes them at class instantiation time, via the constructor. I typically initialize on first access: `get { if(_command==null) { _command = new ... } return _command; }`. There's very little (but not *no*) difference between these options, and it shouldn't matter to you which you use. – Esoteric Screen Name Jan 24 '12 at 20:58