Jacob
Jacob

Reputation: 78920

Why doesn't this Silverlight 4 DependencyObject's DependencyProperty getting data bound?

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.

Upvotes: 2

Views: 1159

Answers (2)

Ozan
Ozan

Reputation: 4415

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));
    }
  }
}

Upvotes: 1

Jacob
Jacob

Reputation: 78920

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.

Upvotes: 0

Related Questions