StaticResource Binding doesn't work as ImageSource - c#

If I use this code:
<btn:Button ButtonImage="{Binding Path=BtnImage, FallbackValue=../Images/Search.png}"/>
my Button has a default image if BtnImage isn't set. When I try to change it into:
<UserControl.Resources>
<ImageSource x:Key="DefaultImage">
../Images/Search.png
</ImageSource>
</UserControl.Resources>
...
<btn:Button ButtonImage="{Binding Path=BtnImage, FallbackValue={StaticResource DefaultImage}}"/>
My default image isn't displayed. I want to understand why and how can I fix this because I am a fan of this StaticResource approach.
Edit:
My used Button is a dummy one:
<UserControl x:Class="WPF_Controls.Controls.Button"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="Btn">
<Button DataContext="{Binding ElementName=Btn}"
Command="{Binding Path=ButtonCommand}">
<Image Source="{Binding Path=ButtonImage}" />
</Button>
</UserControl>
Solution:
If I use:
<UserControl.Resources>
<system:String x:Key="DefaultImage">pack://application:,,,/DK_WPF_Controls;component/Images/Search.png</system:String>
</UserControl.Resources>
everything works as expected!

In case there is an "Images" folder in your Visual Studio project, which contains the "Search.png" file, and the file's Build Action is set to Resource, the following should work:
<UserControl.Resources>
<BitmapImage x:Key="DefaultImage" UriSource="/Images/Search.png"/>
</UserControl.Resources>
where
UriSource="/Images/Search.png"
is a XAML shortcut for a full Resource File Pack URI, i.e.
UriSource="pack://application:,,,<assembly name>;component/Images/Search.png"
Alternatively you can also use implicit conversion from string to ImageSource like
<ImageSource x:Key="DefaultImage">/Images/Search.png</ImageSource>

Related

Set image path as variable in the application resources of Avalonia for re-using

I have a logo, which is used in certain places of my application. So I'd like to store it's path in a variable of my App.axaml file, which should allow me to reference this path as variable in the entire application. This works fine with colors like StepBoxBG
<Application.Resources>
<Color x:Key="StepBoxBG">#5eba00</Color>
<Image x:Key="LogoPath">/Assets/Logos/logo.png</Image>
</Application.Resources>
which I reference using DynamicResource in e.g. a border element like this
<Border Background="{DynamicResource StepBoxBG}" Padding="20">
...
</Border>
But when my logo path is referenced in the same way
<Image Height="90" Source="{DynamicResource LogoPath}" />
no logo is displayed. The path is correct, because when I use the path directly in the Image element it works:
<Image Height="90" Source="/Assets/Logos/logo.png" />
I found this question and tried it, so the App.axaml looks like this:
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:My.App"
xmlns:imaging="clr-namespace:Avalonia.Media.Imaging;assembly=Avalonia.Visuals"
x:Class="ULabs.Image2Card.App">
<Application.DataTemplates>
<local:ViewLocator/>
</Application.DataTemplates>
<Application.Styles>
<FluentTheme Mode="Light"/>
</Application.Styles>
<Application.Resources>
<Color x:Key="StepBoxBG">#5eba00</Color>
<imaging:Bitmap x:Key="LogoPath">
<x:Arguments>
<x:String>/Assets/Logos/logo.png</x:String>
</x:Arguments>
</imaging:Bitmap>
</Application.Resources>
</Application>
Now it throws an exception, because it refers this as an absolute path instead of being relative to the project:
System.IO.DirectoryNotFoundException: "Could not find a part of the path 'C:\Assets\Logos\logo.png'."
I set build action to AvaloniaResource so it should be included in my assembly. Also tried <x:String>Assets/Logos/ul-logo.png</x:String>, now the exception refers to the debug folder (bin/Debug/net5.0/Assets).
How can I specify a resource that just holds the /Assets/Logos/logo.png path and resolve it as hard-coded paths in the <Image> element would do?
You cannot use a Control subclass (Image) in <Application.Resources>. In WPF you would usually use a BitmapImage.
However, BitmapImage is not available in Avalonia, but you can resort to ImageBrush as an alternative, see this great example:
https://github.com/AvaloniaUI/Avalonia/issues/7211#issuecomment-998036759
Adapting the example only slightly to your use case, you could define Logo as follows in App.axaml:
<Application.Resources>
<Color x:Key="StepBoxBG">#5eba00</Color>
<ImageBrush x:Key="Logo" Source="/Assets/Logos/logo.png" />
</Application.Resources>
Finally, one would refer to it in this way:
<Image Height="90" Source="{Binding Source={StaticResource Logo}, Path=Source}" />

How to get the actual object of StaticResource from codebehind without using TryFindResource?

I have used a StaticResource in XAML file. I have named the StaticResource using x:Name. Now I want to access the actual object of the StaticResource.
Here are the files:
In XAML file:
<Window x:Class="MyProject.MainWindow"
...
Title="MainWindow" Height="350" Width="525">
<Grid>
<StaticResource ResourceKey="MyButtonResource" x:Name="MyResource" />
</Grid>
</Window>
In Code-behind CS file:
Button buttonFromStaticResource = MyResource.SomeProperty as Button;
Here, I need something like SomeProperty or any method to get the actual object (in this case, it is a Button object).
Edit:
A way to get the object is to use TryFindResource:
Button buttonFromStaticResource = this.TryFindResource("MyButtonResource") as Button;
But this solution involves a string parameter. Any better solution than this so that I can use MyResource directly (by leveraging x:Name in XAML file), without using any string?
How to read static Resource in c#, Check a below code
<Window x:Class="MyProject.MainWindow"
...
Title="MainWindow" Height="350" Width="525">
<Grid>
<StaticResource ResourceKey="MyButtonResource" x:Name="MyResource" />
</Grid>
</Window>
c#
var currentResources= this.Resources["MyButtonResource"];

WPF/MVVM - Initialize image from XAML and bind to property in ViewModel

I have an application written in WPF MVVM.
I want to initialize an embedded image from XAML (so that I can see it in the designer) but also bind it to the ViewModel so I can manipulate from code.
I can successfully initialize it like this:
<Image x:Name="Image1" Source="pack://application:,,,/images/image1.png" Height="200" Width="55" Opacity="0.35">
How do I bind it to the ViewModel?
If you want to see some data in design time you can define DesignTime viewmodel.
<Window
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=designTimeViewModels:DesignViewModel,
IsDesignTimeCreatable=True}"
/>
And to Bind Image Source use folowing code:
<Image Source="{Binding DisplayedImagePath}" />
ViewModel:
public string DisplayedImagePath
{
get { return "/AssemblyName;component/Images/ImageName.jpg"; }
}
from this topic: Binding an Image in WPF MVVM
You could use FallbackValue...
<BitmapImage x:Key="Image1" UriSource="pack://application:,,,/images/image1.png" />
<Image x:Name="Image1" Source={Binding Image1, FallbackValue={StaticResource Image1}}" />
This has the possible downside of having your image how up at run time if the conditions for use of the fallback value is triggered; so you would need to avoid that situation.

WPF Using images in generic.xaml template

I am writing a style for a custom control derived directly from Control. Visual Studio places the style for a "Custom Control (WPF)" in the Themes\generic.xaml file. My style contains an image which I can't get displayed, seems there's something special about how to set the Source for an image from within the generic.xaml file.
I managed to reproduce the issue with a simpler scenario. Create a "WPF Custom Control library" then add a style for buttons like so, in the themes\generic.xaml . Here's my complete generic.xaml:
<ResourceDictionary
...
<Style TargetType="{x:Type Button}">
<Setter Property="Content">
<Setter.Value>
<Image Source="SmallHandle.png"></Image>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
After this I have create a UserControl (in the same project) containing just a button (for the sake of testing out the style) like so:
<UserControl x:Class="BlendControls.UserControl1"
...
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Button/>
</UserControl>
I have added the SmallHandle.png in the root project directory, in the themes directory, I have added it also to the good old Resources page, tried changing the build action to resource, embedded resource, tried copying the image manually to the build directory, but to no effect. The image is never displayed.
This must be related to the generic.xaml file, because copying the entire style to the same file where the Button is placed works fine. That is, the following works as expected:
<UserControl x:Class="BlendControls.UserControl1"
...
d:DesignHeight="300" d:DesignWidth="300">
<UserControl.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Content">
<Setter.Value>
<Image Source="SmallHandle.png"></Image>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Button></Button>
</UserControl>
So, how should I set the Source of images from generic.xaml? Or alternatively, where should I put the styles/templates for custom controls?
---- Solution ----
As pointed out by Sheridan, I have to use the "full" pack URI notation as:
pack://application,,,/MyAssembly;components/SmallHandle.png
This looks strange to me, as the image is in the same assembly. Not sure, looks like I am referencing from outside the dll.
There's nothing unusual about accessing an image in Generic.xaml, you're just not referencing it correctly. You can reference a resource file in the project assembly using this format:
<Image Source="/AssemblyName;component/Subfolder/SmallHandle.png" />
If your images are directly inside the project root (which is not recommended), then you can access them like this:
<Image Source="/AssemblyName;component/SmallHandle.png" />
If your images are in a folder in another project, then you can access it like this:
<Image Source="/ReferencedAssembly;component/Subfolder/SmallHandle.png" />
See the Pack URIs in WPF page on MSDN for more information.
UPDATE >>>
In .NET 4, the above Image.Source values would work. However, Microsoft made some horrible changes in .NET 4.5 that broke many different things and so in .NET 4.5, you'd need to use the full pack path like this:
<Image Source="pack://application:,,,/AssemblyName;component/Images/image_to_use.png">
If you don't feel as though your generic.xaml is being picked up, you can reference it from your App.cs.xaml like this:
<App.Resources>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/MY.NAMESPACE;component/Themes/generic.xaml" />
</ResourceDictionary.MergedDictionaries>
</App.Resources>
Your generic.xaml file should be marked as "Resource".
Also, your image file should be marked as "Resource".
Finally, reference your ImageSource like this:
<Image Source="Themes/IMAGE.png" />
or try
<Image Source="../Themes/IMAGE.png" />
Personally, I like to put my style templates in their own .xaml file, and reference them all as MergedDictionaries.
Typed base style in Themes\Generic style is automatically applied only to Custom Control.
If you need use typed based style in your user control you need add generic.xaml to user control resources.
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/Generic.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
Also change Image Source URI to
<Image Source="pack://application:,,,/WpfCustomControlLibrary1;component/SmallHandle.png" />

Set resource string to XAML

I know how to set string from resource
<TextBlock x:Uid="Text1"/>
where Text1.Text is "Hello"
But I want to do like this
<TextBlock Text = {something here to get GreetingText}/>
where GreetingText is "Hello"
So that I may get the same string from code also as
var loader = new Windows.ApplicationModel.Resources.ResourceLoader();
var string = loader.GetString("GreetingText");
Include this
xmlns:system="clr-namespace:System;assembly=mscorlib"
Have a resource of system:string like this.
<Window.Resources>
<system:String x:Key="GreetingText">Hello</system:String>
</Window.Resources>
and use it in xaml as
<TextBlock Text="{StaticResource GreetingText}" />
and use it in code behind as
string s = (string)objectofMainWindow.Resources["GreetingText"];
Edit: Answer to your comment
Its this way. Resource Dictionary is inside Window.Resources
<Window
xmlns:system="clr-namespace:System;assembly=mscorlib"
Your Rest namespaces
/>
<Window.Resources>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ATTFamilyMap.strings">
<system:String x:Key="GreetingText">Hello</system:String>
</ResourceDictionary>
</Window.Resources>
Your Code
</Window>
Nikhil's answer is on the right track, but is right for other platforms.
For windows 8, you need to do the following in your resource directory:
<x:String x:Key="MyString">This is a resource</x:String>
In your xaml:
<TextBlock Text="{StaticResource MyString}"/>
In code:
string myString = (string)(App.Current.Resources["MyString"]);

Categories

Resources