views:

262

answers:

2

I have no idea why data binding is not happening for certain objects in my Silverlight 4 application. Here's approximately what my XAML looks like:

<sdk:DataGrid>
  <u:Command.ShortcutKeys>
    <u:ShortcutKeyCollection>
      <u:ShortcutKey Key="Delete" Command="{Binding Path=MyViewModelProperty}"/>
    </u:ShortcutKeyCollection>
  </u:Command.ShortcutKeys>
</sdk:DataGrid>

The data context is set just fine since other data bindings that I have set on the grid are working just fine. The Command.ShortcutKeys is an attached DependencyProperty that is declared as follows:

public static readonly DependencyProperty ShortcutKeysProperty = DependencyProperty.RegisterAttached(
    "ShortcutKeys", typeof(ShortcutKeyCollection),
    typeof(Command), new PropertyMetadata(onShortcutKeysChanged));

private static void onShortcutKeysChanged(
    DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
    var shortcuts = args.NewValue as ShortcutKeyCollection;
    if (obj is UIElement && shortcuts != null)
    {
        var element = obj as UIElement;
        shortcuts.ForEach(
            sk => element.KeyUp += (s, e) => sk.Command.Execute(null));
    }
}

public static ShortcutKeyCollection GetShortcutKeys(
    DependencyObject obj)
{
    return (ShortcutKeyCollection)obj.GetValue(ShortcutKeysProperty);
}

public static void SetShortcutKeys(
    DependencyObject obj, ShortcutKeyCollection keys)
{
    obj.SetValue(ShortcutKeysProperty, keys);
}

I know this attached property is working just fine since the event handlers are firing. However, the Command property of the ShortcutKey objects are not getting data bound. Here's the definition of ShortcutKey:

public class ShortcutKey : DependencyObject
{
    public static readonly DependencyProperty KeyProperty = DependencyProperty.Register(
        "Key", typeof(Key), typeof(ShortcutKey), null);
    public static readonly DependencyProperty CommandProperty = DependencyProperty.Register(
        "Command", typeof(ICommand), typeof(ShortcutKey), null);

    public Key Key
    {
        get { return (Key)GetValue(KeyProperty); }
        set { SetValue(KeyProperty, value); }
    }

    public ICommand Command
    {
        get { return (ICommand)GetValue(CommandProperty); }
        set { SetValue(CommandProperty, value); }
    }
}

public class ShortcutKeyCollection : ObservableCollection<ShortcutKey> { }

The property that is getting bound to has its value set in the constructor of my view model, and its type is ICommand. So why isn't my Command property getting data bound? Also, have you found an effective way to debug data binding issues in Silverlight?

Edit:

At least one thing that was wrong was that ShortcutKey derived from DependencyObject instead of FrameworkElement, which is apparently the only root class that binding can be applied to. However, even after that change, the binding continued to not work properly.

A: 

It looks like unless an object is inserted into the visual tree, no DataContext inheritance takes place, and thus no data binding works. I couldn't find a way to get the container's data context to be passed to the ShortcutKey objects, so as a workaround, I set up the binding in the code behind.

Hopefully someone else has a different answer that will show me how I won't have to resort to setting up this data binding in the code.

Jacob
+1  A: 

You need to specify the Source of the Binding, since the DataContext is not inherited by members of the ObservableCollection.

edit:

Try setting the ShortcutKey.DataContext in onShortcutKeysChanged:

private static void onShortcutKeysChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
  var shortcuts = args.NewValue as ShortcutKeyCollection;
  if (obj is FrameworkElement && shortcuts != null)
  {
    var element = obj as FrameworkElement;
    ForEach(ShortcutKey sk in shortcuts) 
    {
      sk.DataContext = element.DataContext;
      element.KeyUp += (s, e) => sk.Command.Execute(null));
    }
  }
}
Ozan
Could you add an example of how to set the source for the `Binding` through XAML and without needing a `StaticResource`? I tried a few ways of doing this, and none of them succeeded.
Jacob
Thanks for the example, but I had already tried doing almost exactly that. With that code, the `DataContext` would get properly set on the `ShortcutKey` objects, but the binding operation still didn't take place.
Jacob
Is MyViewModelProperty a DependencyProperty or does you viewmodel implement IPropertyChanged?
Ozan
The view model implements INotifyPropertyChanged.
Jacob
I don't see any obvious error, it seems like you need to debug through. Try setting the binding in onShortcutKeysChanged: sk.SetBinding(ShortcutKey.CommandProperty, new System.Windows.Data.Binding("MyViewModelProperty")); and see if it arrives at MyViewModelProperty.get or see what happens.
Ozan
That works and is basically what I did in my workaround of creating the bindings in the code behind. But I'm wanting to find a way to set the binding through XAML. The getter for the property is not hit with XAML bindings, so it looks like XAML binding is completely ignored in some cases.
Jacob
Try registering CommandProperty with a callback to see if it is set at all and with what Binding.
Ozan
The setter is not invoked at all with the XAML binding, but with the binding done through C# it is invoked when expected.
Jacob
I'm at a loss. If you can package up a minimal project that reproduces the error and post it somewhere I would like to take a look at it.
Ozan