Images from App.xaml Resource Dictionary not producing in Content Controls.
See original GitHub issue- .NET Core Version: .Net 5.0
- Windows version: 10
- Does the bug reproduce also in WPF for .NET Framework 4.8?: Unsure, it occurred on .Net 5.0 and the app is a bit too complicated to easily reproduce in an entirely new .NET Framework app at this point.
- Is this bug related specifically to tooling in Visual Studio (e.g. XAML Designer, Code editing, etc…)? I doubt it. The bug occurs during a debug session. But hard to pinpoint the exact source here.
Problem description:
Quite unusual really. I have a set of definitions for icons in my App.xaml resource dictionary:
<Application.Resources>
<ResourceDictionary>
<BitmapImage x:Key="Position" UriSource="Icons/Properties/Position.png"/>
<BitmapImage x:Key="XPosition" UriSource="Icons/Properties/Position-x.png"/>
<BitmapImage x:Key="YPosition" UriSource="Icons/Properties/Position-y.png"/>
</ResourceDictionary>
</Application.Resources>
The position image isn’t necessary here, but I’ll mention it later. It’s mainly the X and Y position icons that are relevant for now. All 3 images are marked with Build Action “Resource” and told to “Copy if newer”. I’ve tried changing the Build Action to “Content” and “Embedded Resource” and the issue persists.
Now, in my MainWindow, I have a bit of a long-winded approach to accessing these images, but the key thing to note is that the X and Y Positions should be behaving identically. But the code for MainWindow looks like this:
public MainWindow()
{
InitializeComponent();
// Have changed this to XPosition and Position as well
var prop = AnimatableProperties.YPosition;
//MessageBox.Show(Directory.GetCurrentDirectory());
var bmp = prop.Icon.Clone();
Stack1.Children.Add(new ContentControl { Content = new Image { Source = bmp, Width = 36 }, Width = 400 });
}
Here Stack1 was just a Stack Panel. But the issue persisted whether I added the children to a Canvas as well.
The Animatable Properties class is just a class for easier accessibility of these things. It looks like the following:
public static class AnimatableProperties
{
internal static AnimatableProperty<double, DoublePropertyControl> XPosition => new("XPosition");
internal static AnimatableProperty<double, DoublePropertyControl> YPosition => new("YPosition");
internal static AnimatablePropertyBranch Position => new("Position", XPosition, YPosition);
}
The relevant constructor for AnimatableProperty is:
public sealed class AnimatableProperty<TValue, TControl> : IAnimatableProperty where TControl : UIElement, IAnimatableProperty<TValue>, new()
{
...
public BitmapImage Icon { get => Control.Icon; internal set => Control.Icon = value; }
...
internal AnimatableProperty(string iconResourceKey) => Icon = IAnimatablePropertyExt.FromResource(iconResourceKey);
...
}
In short, this is just a long-winded approach to acquire the images from the resource dictionaries.
The relevant constructor and properties for AnimatablePropertyBranch are:
public sealed class AnimatablePropertyBranch : IAnimatableProperty
{
public BitmapImage Icon { get; set; }
...
private readonly ReadOnlyCollection<IAnimatableProperty> _values;
public ReadOnlyCollection<IAnimatableProperty> Values
{ get => _values; set { for (int i = 0; i < _values.Count; i++) { _values[i].Value = value[i].Value; } } }
...
internal AnimatablePropertyBranch(string iconResourceKey, params IAnimatableProperty[] properties)
=> (Icon, _values) = (IAnimatablePropertyExt.FromResource(iconResourceKey), new(properties));
...
}
Again, back to the MainWindow constructor, all that happened was that I added a basic content control containing the icon:
// Have changed this to XPosition and Position as well
var prop = AnimatableProperties.YPosition;
//MessageBox.Show(Directory.GetCurrentDirectory());
var bmp = prop.Icon.Clone();
Stack1.Children.Add(new ContentControl { Content = new Image { Source = bmp, Width = 36 }, Width = 400 });
Actual behavior: The issue is a bit complex, and there may be multiple underlying issues.
Note that Position
, XPosition
, YPosition
are all referencing the static properties of the AnimatableProperties
class.
But the following things happened:
- If the
prop
variable in the MainWindow constructor is eitherPosition
orYPosition
, the debug raises an IO Exception saying it cannot find the file. (Again, recall that the files are set to copy if newer, and Build Actions “Content”, “Resource”, and “Embedded Resource” have all been tested to try to fix this.) - If the
prop
variable is XPosition however, the system is able to produce an image. - If I don’t clone the images from the resource dictionary, the Content Control simply writes out the
BitmapImage.Source
property as a string, and doesn’t produce any image. - No images are copied into the Icons/Properties folder of the Debug folder for the project. Not even the XPosition folder, which displays fine.
- I have triple-checked the directories and everything, the files, file names are accurate. The image below contains a search result for files containing “Position” in the Project Directory. You’re free to double-check the spellings yourself. I added modification dates and times to show that they should all behave similarly, but don’t.
- If the Canvas or Stack Panel directly contains an image (ie. not through a Content Control, and therefore a Button as well, but an Image directly), then the images display perfectly fine for all 3 of
XPosition
,YPosition
, andPosition
. - Cleaning and rebuilding did not fix the issue. Neither did closing and reopening Visual Studio.
Note that XPosition and YPosition behave differently, despite having the exact same underlying code to produce the images.
Only IO Exceptions were called, and they mentioned that they were unable to find the files for the Position
and YPosition
properties specifically. It’s worth clarifying, that there was definitely no misspelling of the resource keys, as it identified the appropriated directories to search for.
Expected behavior:
The images should have been copied to the Icons/Properties folder of the debug directory, and been visible regardless whether or not they’re inserted into the Stack Panel or Canvas via a Content Control or not. At the very least, XPosition and YPosition should have behaved identically.
Minimal repro:
The issue is a little complicated that I’m not sure a minimal amount of code will even reproduce the original issue.
But the steps to produce it would be:
- Create a WPF project.
- Add an image to some folder.
- Add image as a resource in a resource dictionary in App.xaml.
- Add the image to a Panel in the MainWindow from the resources via a Content Control dynamically (ie. not in the XAML code).
If you’re interested in a minimal repro, it would take some time to produce something similar, but writing this down took long enough. So I’ll try to do it soon. A little busy in the upcoming days though, so unsure how soon that will be.
The project is on GitHub privately, should anyone wish to pull it to their computer, I can add them to the repo temporarily.
Issue Analytics
- State:
- Created 2 years ago
- Comments:5 (2 by maintainers)
Top GitHub Comments
Thanks @osamakawish Closing this for now.
In fact, unloading and reloading the project seems to have fixed the issue. But please fix this issue so that others don’t run into it. VS doesn’t seem to always carry the action for CopyToOutputDirectory over to the csproj file, and that could confuse devs who are new to WPF.