I am currently building an application that consists of several components, each of which is essentially a WPF user control with a little C# code around it for the plugin system to work (using MEF).
The problem I am having is that each component should include an icon and for niceness purposes I defined that as a System.Windows.Media.Brush so I can just use the DrawingBrush exported from Design there. Now I need to access that piece of XAML from non-WPF C# where I currently have the horrible workaround of instantiating the user control and asking it for the resource:
private Brush CachedIcon = null;
public override Brush Icon
{
get
{
if (CachedIcon == null)
{
CachedIcon = (Brush)(new BlahControl().TryFindResource("Icon"));
}
return CachedIcon;
}
}
I couldn't find a way to read that resource (which is a .xaml file, and referenced in a ResourceDictionary in the custom control) from a "normal" C# class. Anything belonging to WPF has that nice TryFindResource method but how to do that otherwise? I don't want to have the XAML file with the icon lying around un-embedded.
In your XAML code make sure the icon resource has the build option set to "Resource", and then reference the resource to make it a xaml static resource
<UserControl.Resources>
<BitmapImage x:Key="icon1" UriSource="Resources/Icon1.ico" />
</UserControl.Resources>
Then in your .Net 2.0 code you will find the resource in the "{xamlName}.g.resource" stream
Example code that loads all icons from a xaml dll into a dictionary:
using System.IO;
using System.Reflection;
using System.Collections;
using System.Resources;
...
var icons = new Dictionary<String, Bitmap>();
var externalBaml = Assembly.LoadFile(Path.Combine(Environment.CurrentDirectory, "MyXaml.dll"));
Stream resourceStream = externalBaml.GetManifestResourceStream(externalBaml.GetName().Name + ".g.resources");
using (ResourceReader resourceReader = new ResourceReader(resourceStream)) {
foreach (DictionaryEntry resourceEntry in resourceReader) {
if (resourceEntry.Key.ToString().ToUpper().EndsWith(".ICO")) {
icons.Add(resourceEntry.Key.ToString(), Image.FromStream(resourceEntry.Value as Stream) as Bitmap);
}
}
}
My suggestions are:
Provide metadata on your control about where the icon can be found. You can do this with your own custom attribute (see example 1 below). This metadata will allow you to load the icon without creating an instance of the control.
Since you're using MEF, you can use metadata in your export to achieve the same as above. Details here. See example 2 below.
Treat your icon as an ImageSource rather than a Brush. You can use WPF's Image control to show your ImageSource, or you can paint it with an ImageBrush.
Use the technique provided by TFD to read the resource with the name specified in the metadata. Unfortunately, WPF does not appear to provide anything like a BamlReader, which would make it much cleaner to load the WPF resource from a non-WPF context.
Example 1:
[Icon("MyIconResourceName")]
public class BlahControl : Control
{
...
}
Example 2:
[Export(typeof(IApplicationComponent))]
[ExportMetadata("IconResource", "MyIconResourceName")]
public class BlahControl : Control
{
...
}
You can read resources from your assembly as stream.
Example code here:
http://www.wpftutorial.net/ReadWPFResourcesFromWinForms.html
Define the icons at the app level instead of in the control, either in the app.xaml or a master resource dictionary xaml file. Then you can use the same TryFindResource method, but without creating an instance of the control.
Related
We are switching a huge WPF Appl. to .NET 6.0. At least one CustomControl which worked on 4.8 does not get it's template applied which is referenced via Generic.xaml. I am not sure if Generic.xaml is not loaded or something else needs to be considered.
The Style can be added manually to Application.Resources.MergedDictionaries via a simple "Add" call with the Source set to it. I can see the CustomControl Style afterwards with the Key being the correct Type. It is still not applied, as there is no visual representation and no call is made to the overriden OnApplyTemplate method.
If all else fails, can I apply a template manually if I have the given style, like just apply the Style manually to a newly created instance?
Also: the Projects are now SDK-Style, AssemblyInfo.cs was taken over and "generate assembly info" is set to false. It contains the standard ThemeInfo entry.
Just for clarification following the code which successfully finds the Generic.xaml. But before 6.0 Generic.xaml was loaded without doing anything.
Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary
{
Source = new Uri("/Contracts;component/Themes/Generic.xaml", UriKind.Relative)
});
Thank you all for your help!
I had the same Problem.
maybe u need to add the Assembly.cs at TopLevel of ur CustomControlLibrary
using System.Windows;
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
How can I extend the .text and .caption property of ALL controls. I want to create a multilanguage App, which load the text from a custom file.
I want to do this:
button1.Text.LoadLocalizedText()
label1.Caption.LoadLocalizedText()
Now, I need the following text result, to get the correct text.
I need "class.button1.Text" as string and for the label "class.label1.Caption" as string.
After I load the custom text, I must set the text dynamically to the "source" property like text or caption.
I hope, someone could help me.
Thanks.
If you want to write a method you can call on all controls, write a extension method for control (see #Adriani6's answer)
public static void LoadLocalizedText(this Control ctrl) {...}
If you want to load a resource localized by your culture, you should use your resource files and create files for each culture.
Create 2 resource files like Resource1.resx and Resource1.de.resx and save your corresponding values to the resource files.
Then, you can load your resources like
[ProjectNamespace.]Resources.Resource1.ResourceManager.GetString("myResource");
or, alternatively with an overload to specify the culture yourself
Resources.Resource1.ResourceManager.GetString("myResource", System.Globalization.CultureInfo.GetCultureInfo("de"));
See Microsoft Docs
If this is WinForms, you can write an extension method for the Control class.
public static class LocalizationExtension
{
public static void LoadLocalizedText(this Control ctrl)
{
ctrl.Text = "My loaded string";
}
}
This way you can target all controls that inherit from Control such as Button and Label.
You just call it using Button.LoadLocalizedText() and Label.LocdLocalizedText() etc..
I've successfully been able to use a static .svg file as an image in WPF by following the guidance in another question.
The basic approach there is to use the SharpVectors library, and do:
<svgc:SvgViewbox Source="path/to/file.svg"/>
In place of an <Image .../> tag.
However I am struggling trying to find a similar method to use an SVG within a System.Windows.Controls.Ribbon - where i'd like to use it as the SmallImageSource of a RibbonMenuButton.
I have tried the following:
<RibbonMenuButton Label="Test">
<RibbonMenuButton.SmallImageSource>
<svgc:SvgViewbox Source="path/to/file.svg"/>
</RibbonMenuButton.SmallImageSource>
</RibbonMenuButton>
Which produces the compiler error message:
The specified value cannot be assigned. The following type was
expected: "ImageSource".
I think the key problem is that an svgc:SvgViewBox is not an "image source", but I don't know how to properly convert or otherwise work around this.
I'm open to alternate approaches which don't use SharpVectors, but it is extremely convenient to have source image files in SVG format and not have to manually convert to any other format.
SharpVectors includes a converter extension which can be used to 'output' an ImageSource.
This is documented in section "1.2 WPF Extensions and Type Converters" of their usage guidelines.
Example:
<RibbonMenuButton Label="Test" LargeImageSource="{svgc:SvgImage path/to/file.svg}"/>
(where svgc is the defined name of the SharpVectors namespace in your XAML.)
The svgc:SvgImage binding extension produces a DrawingImage which is a type of ImageSource. This works perfectly at runtime with the SVG image rendered into the button.
Unfortunately at design-time the button image is blank.
Please try using the newly added property; AppName, which is used to try and resolve the URI of the resource file at design-time.
See the samples for the SvgImage and newly added SvgImageConverter, especially the toolbar demo using the SVG icons.
https://github.com/ElinamLLC/SharpVectors/tree/master/TutorialSamples/ControlSamplesWpf
SvgImageConverter provides binding support, if you need it unlike the SvgImage.
I am trying to get the Icon of a NotifyIcon in WPF.
So I have added a .ico file to my solution in a Resources folder and set the build action to Resource.
I am trying to grab this resource in code behind like so:
var icon = (Icon) Application.Current.FindResource("/Resources/icon.ico")
This doesn't work.
In addition to this: Application.Current.Resources.Count returns 0.
EDIT
var i = new Icon(Application.GetResourceStream(new Uri("/systemtrayicon.ico", UriKind.Relative)).Stream);
With the icon in the root and the build action set to Resource.
Still not working.
EDIT AGAIN:
I needed to Clean the solution and rebuild as per: WPF throws "Cannot locate resource" exception when loading the image
This will works 100%
ni.Icon = new Icon(Application.GetResourceStream(new Uri("pack://application:,,,<Image Location From root>")).Stream);
Example:
notify.Icon = new Icon(Application.GetResourceStream(new Uri("pack://application:,,,/images/favicon.ico")).Stream);
You have to pass resourceName as a parameter to the FindResource method, not the path for the Resource. Sample code would look like:
var icon = (Icon) Application.Current.FindResource("myImage")
Please note in the above sample code "myImage" is the resource name.
Refer to Application.FindResource Method on MSDN.
You say, Application.Current.Resources.Count is Zero, that means you do not have any Resource defined in your App.xaml file.
You can add resources to App.xaml like this:
<Application.Resources>
<Image x:Key="myImage" Source="img.png" />
</Application.Resources>
It appears that your icon is an embedded resource. FindResource cannot work with embedded resources. Set BuildAction of your icon to Resource.
Refer to this MSDN page for more reading on WPF Resources.
UPDATE
Code for accessing Embedded Resources
Assembly.GetExecutingAssembly().GetManifestResourceStream("myImg.png");
However, if you had added this image to the Resources.Resx and you should simply be able to use Resources.ResourceName.
UPDATE 2
Adding resources to App.xaml or any ResourceDictionary is better, so that you can use them as Static/Dynamic resources via StaticResource or DynamicResource markup extensions.
If you do not want to add it to App.xaml resources and still want to access it, one option as I mentioned above is to add it to the Resources.Resx and use Resources.ResourceName to refer the icon/image
Another way is to create System.Drawing.Icon by yourself, sample code:
new System.Drawing.Icon(Application.GetResourceStream(new Uri("/Resources/icon.ico")));
Personally, I would go with XAML resources and add them to App.xaml or a ResourceDictionary.
I have 2 questions regarding a tutorial that I am going through.
Q1.
Through the tutorial they use a datasource
Using the data in the app
To use the data in the app, you create an instance of the data source
as a resource in App.xaml. You name the instance feedDataSource.
BR211380.wedge(en-us,WIN.10).gifTo add a resource to an app
Double-click App.xaml in Solution Explorer. The file opens in the XAML editor.
Add the resource declaration, <local:FeedDataSource x:Key="feedDataSource"/>, to the root ResourceDictionary, after the
MergedDictionaries collection.
and then they use it in the OnLaunch method.
var connectionProfile = Windows.Networking.Connectivity.NetworkInformation.GetInternetConnectionProfile();
if (connectionProfile != null)
{
FeedDataSource feedDataSource = (FeedDataSource)App.Current.Resources["feedDataSource"];
if (feedDataSource != null)
{
if (feedDataSource.Feeds.Count == 0)
{
await feedDataSource.GetFeedsAsync();
}
}
}
I am wondering why do they store it in resource? Wy not just create an instance of the class and get the results from it?
Q2.
Later down the article they use this datasource items with "grid view items". I seen this done in their other template projects. I am wondering is there the standard way of making your interface?
At first I thought maybe just drop some image buttons on the screen and hook up their click events but now I am not sure.
The XAML Resource essentially does create an instance for you and makes it available in the Resources collection, so you could instantiate the class yourself. Having it as a resource keeps this object around and makes it accessible across the various pages in your application. You could certainly create the class explicitly, and if you enforce the singleton pattern on it, it would be semantically equivalent.
I'm not sure I see the context of your second question in the tutorial, but in general the pattern you are seeing is Model-View-ViewModel (MVVM), which is the de facto standard pattern for Windows Store apps. feedDataSource is providing the model and portions of that are assigned to DefaultViewModel, which is the DataContext for all of the binding markup in the XAML pages, which are the views. The idea behind this to separate your data from your model, so that when you do things like load a new data feed, etc., all you need to do is change the data source, and all of the data binding markup will automatically reflect the new data in your user interface.
If you find yourself writing code that looks like TextBox.Text = "My text", then you're deviating from the pattern.