views:

169

answers:

1

I'm attempting to bind an IsLoading property to the Cursor property of my UI's LayoutRoot Grid. I'm trying to have the main app cursor become an hourglass whenever the property says it's loading.

I'm binding the property as follows:

<Grid Cursor="{Binding IsLoading, Converter={StaticResource CursorConverter}}">

The key "CursorConverter" maps to the BoolToCursorConverter in the resources. The converter code is:

public class BoolToCursorConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (parameter == null)
            return ((bool)value == true) ? Cursors.Wait : Cursors.Arrow;
        return false;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        Cursor cursor = value as Cursor;
        if (cursor != null)
            return cursor == Cursors.Wait ? true : false;
        return false;
    }
}

When I try to run this though I get the XamlParseException "The given key was not present in the dictionary."

Any help would be appreciated, thanks,

+3  A: 

Why you get that error

Do you have something like this in the content of a Resources property?

<local:BoolToCursorConverter x:Key="CursorConverter" />

If not then that's what is wrong but I'm guessing you already do.

In that case you I suspect you have placed it in the Resources property of the Grid it applies to. That would be why it can not be found. StaticResource are resolved immediately as the Xaml is parsed. Hence any key used must already be loaded into a resource dictionary prior to use. The Xaml parser knows nothing of the contents of the Grid's Resources property because it hasn't processed it yet. Hence:-

<UserControl>
   <Grid Cursor="{Binding IsLoading, Converter={StaticResource CursorConverter}}">
     <Grid.Resources>
       <local:BoolToCursorConverter x:Key="CursorConverter" />
     </Grid.Resources>
     <!-- Contents here -->
   </Grid>
</UserControl>

will fail. Where as:-

<UserControl>
   <UserControl.Resources>
     <local:BoolToCursorConverter x:Key="CursorConverter" />
   </UserControl.Resources >
   <Grid Cursor="{Binding IsLoading, Converter={StaticResource CursorConverter}}">
     <!-- Contents here -->
   </Grid>
</UserControl>

would at least not fail in finding the converter.

What you actually need to do

I've presented the above to answer your question but I can see it doesn't really help you. You can't bind to the Cursor property like this. (It doesn't expose a public identifier field, Xaml uses the NameOfThing + "Property" convention to find a field that is the DependencyProperty for the property being bound).

The solution is to create an Attached property:-

public class BoolCursorBinder
{
    public static bool GetBindTarget(DependencyObject obj) { return (bool)obj.GetValue(BindTargetProperty); }
    public static void SetBindTarget(DependencyObject obj, bool value) { obj.SetValue(BindTargetProperty, value); }

    public static readonly DependencyProperty BindTargetProperty =
            DependencyProperty.RegisterAttached("BindTarget", typeof(bool), typeof(BoolCursorBinder), new PropertyMetadata(false, OnBindTargetChanged));

    private static void OnBindTargetChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        FrameworkElement element = sender as FrameworkElement;
        if (element != null)
        {
            element.Cursor = (bool)e.NewValue ? Cursors.Wait : Cursors.Arrow;
        }
    }
}

Now you can actually do the binding like this:-

 <Grid local:BoolCursorBinder.BindTarget="{Binding IsLoading}">
AnthonyWJones
Worked like a charm, thanks!!
Nick Gotch
And thanks for the great explanation too
Nick Gotch