1

I can't get the following to work, I must be missing something elementary. My objective is to have a style on a dialog (Window) to set an image on a button within it. So what I have is in the dialog code I added a DependencyProperty like so:

public static readonly DependencyProperty ImageRefreshProperty =
  DependencyProperty.Register(nameof(ImageRefreshProperty), typeof(ImageSource),
    typeof(MyDlg), new PropertyMetadata(new BitmapImage(
      new Uri(@"pack://application:,,,/component/Resources/refresh.png"))));
public ImageSource ImageRefresh {
  get { return (ImageSource)GetValue(ImageRefreshProperty); }
  set { SetValue(ImageRefreshProperty, value); }
}

In Xaml I have this:

<Button DockPanel.Dock="Left" Style="{StaticResource buttonIcon}">
  <Image Source="{Binding Path=ImageRefresh,
      RelativeSource={RelativeSource AncestorType=local:MyDlg}}" />
</Button>

This works fine as long as I use code to change the image, like

dlg.ImageRefresh = new BitmapImage(
  new Uri("pack://application:,,,/component/Resources/refr.png"));

But ideally I would like to set the image through a style, in a way like this:

<Style TargetType="{x:Type MyDlg}">
  <Setter Property="ImageRefresh" Value="pack://application:,,,/component/Resources/refr.png" />
</Style>

The error I am getting is:

System.Windows.Markup.XamlParseException:
''Provide value on 'System.Windows.StaticResourceExtension' threw an exception.' 
Inner Exception System.Windows.Markup.XamlParseException:
ArgumentNullException: Key cannot be null. Arg_ParamName_Name 

I have also tried a Setter Value that defines a BitmapImage within it instead of a string, but I still get the same error:

<Setter Property="ImageRefresh">
  <Setter.Value>
    <BitmapImage UriSource="pack://application:,,,/component/Resources/refr.png"/>
  </Setter.Value>
</Setter>

Setting a break point to the getter/setter of the DependencyProperty does not even get hit, nor does a Converter on the binding of the Source of the Image.

What am I missing here?

Edit: to see if the ImageSource has anything to do with it, I tested my code with another property of type bool? to set a button's IsEnabled property in the dialog, but the result is the same. So the error is not with the Image, its pack URL (which works by the way) but apparently with something else.

Daap
  • 345
  • 4
  • 10
  • Wondering how that Pack URI is supposed to work. Do you really have a top-level `/component` folder in your Visual Studio project? If not, the URI should either be `pack://application:,,,/Resources/refr.png` or `pack://application:,,,/AssemblyName;component/Resources/refr.png` – Clemens Jan 26 '21 at 07:32
  • And where exctly do you declare that Style? – Clemens Jan 26 '21 at 07:34
  • The pack URI is not the issue. As stated, it works perfectly fine when setting the image through code, but trying to set it through a Style is what gives the error. I also edited the question with another test I did with a property of type bool? instead of ImageSource. – Daap Jan 26 '21 at 08:22
  • The style is set in my list of styles used for many other things (I simplified the code for the question, but it's part of a large code base), and the dialog has this style set on it (in fact removing that will work without issue, but obviously not with the expected property changed). – Daap Jan 26 '21 at 08:22

2 Answers2

1

I found the answer myself after some digging around in my code. The problem was the definition of the DependencyProperty. My code had:

public static readonly DependencyProperty ImageRefreshProperty =
  DependencyProperty.Register(nameof(ImageRefreshProperty) /*Wrong*/,
    typeof(ImageSource),
    typeof(MyDlg), new PropertyMetadata(new BitmapImage(
      new Uri(@"pack://application:,,,/component/Resources/refresh.png"))));

but it should be:

public static readonly DependencyProperty ImageRefreshProperty =
  DependencyProperty.Register(nameof(ImageRefresh) /*Correct*/,
    typeof(ImageSource),
    typeof(MyDlg), new PropertyMetadata(new BitmapImage(
      new Uri(@"pack://application:,,,/component/Resources/refresh.png"))));

A very small oversight with big consequences...

Daap
  • 345
  • 4
  • 10
  • Did setting the DP from outside your dialog work? I was still getting errors when setting from `App.xaml`. Same value set from code-behind or the dialog markup was working without a problem. – D M Jan 27 '21 at 02:35
  • Yes, it works perfectly fine now from a Style. – Daap Jan 27 '21 at 06:00
0

Inner Exception System.Windows.Markup.XamlParseException: ArgumentNullException: Key cannot be null. Arg_ParamName_Name

From the error message, you're missing x:Key on your Style.

<Style x:Key="MyDialogStyle" TargetType="{x:Type MyDlg}">
    <Setter Property="ImageRefresh Value="..."/>
</Style>

According to this answer you can use x:Key={x:Type MyDlg}" if you don't feel like setting an explicit key.

<Style x:Key="{x:Type MyDlg}" TargetType="{x:Type MyDlg}">
    <Setter Property="ImageRefresh Value="..."/>
</Style>
D M
  • 5,769
  • 4
  • 12
  • 27
  • 1
    I'm afraid that's not it. I cleaned up my code a bit for the question here on SO but actually my style had a key but I still get the error. – Daap Jan 26 '21 at 04:31
  • Hmm, I'm working on a reproduction. Will update tomorrow. – D M Jan 26 '21 at 05:44
  • I k now for sure the `string` setter doesn’t work as the DependencyProperty is expecting an `ImageSource` but you’ve already caught that. It looked to me like the setter/getter were never being hit because the framework was running some validation against the default value of the property during initialization. – D M Jan 26 '21 at 05:53
  • 1
    A Style in a ResourceDictionary never needs an `x:Key` as long as the TargetType is unique. If you omit it, the Key will implicitly be set to the TargetType. – Clemens Jan 26 '21 at 07:24
  • 1
    The comment about the "string setter" also makes no sense. There is built-in type conversion from string to target types like ImageSource. – Clemens Jan 26 '21 at 07:25