Object to add resources to ResourceDictionary - c#

I'd like to add several resources to a ResourceDictionary from within one single line of XAML. I'll try to make an example out of it.
Imagine this:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:util="clr-namespace:Util">
<util:MyResourceDictionaryA x:Key="MyResourceDictionaryA" />
<util:MyResourceDictionaryB x:Key="MyResourceDictionaryB" />
<util:MyResourceDictionaryC x:Key="MyResourceDictionaryC" />
<util:MyResourceDictionaryD x:Key="MyResourceDictionaryD" />
</ResourceDictionary>
I'd like to have something like:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:util="clr-namespace:Util">
<util:AllDictionaries x:Key="AllDictionaries" BaseName="MyResourceDictionary" />
</ResourceDictionary>
I've tried making a new ResourceDictionary like this:
public class AllDictionaries : ResourceDictionary
{
public string BaseName { get; set; }
public AllDictionaries ()
{
Clear();
var s = BaseName;
var DictionariesToLoad = new[] { "A", "B", "C", "D" };
foreach (var b in DictionariesToLoad )
{
var t = Type.GetType(b + s);
var resDir = Activator.CreateInstance(t) as ResourceDictionary;
Add(b + s, resDir);
}
}
}
However the constructor never gets called (or, not before something else in XAML references {StaticResource MyResourceDictionaryX} which hasn't been added to the application resources so it throws an exception). I also tried putting the code on the BaseName setter, to no avail.
This is an oversimplication of what I need, but I'm just trying to make the XAML more generic.
Any hints on how could one achieve this?
Note 1: the generated resource dictionaries need to be available (using their Key) resources for the rest of the XAML in the scope where I have created the AllDictionaries resource.
Note 2: for practical purposes, let's say I don't have access to MyResourceDictionaryX classes source code so I can't change anything on those

Merged Resource Dictionary already does what you want... it can be created through XAML or code.

Related

Refresh all String Resources when Culture Changed

I've written a WPF app in which user can change culture. The Method in app.xaml.cs looks like this :
public void SelectCulture(string culture)
{
// List all our resources
List<ResourceDictionary> dictionaryList = new List<ResourceDictionary>();
foreach (ResourceDictionary dictionary in Application.Current.Resources.MergedDictionaries)
{
dictionaryList.Add(dictionary);
}
// We want our specific culture
string requestedCulture = string.Format("Resources/StringResources.{0}.xaml", culture);
ResourceDictionary resourceDictionary = dictionaryList.FirstOrDefault(d => d.Source.OriginalString == requestedCulture);
if (resourceDictionary == null)
{
// If not found, we select our default language
//
requestedCulture = "Resources/StringResources.en-ES.xaml";
resourceDictionary = dictionaryList.FirstOrDefault(d => d.Source.OriginalString == requestedCulture);
}
// If we have the requested resource, remove it from the list and place at the end.\
// Then this language will be our string table to use.
if (resourceDictionary != null)
{
Application.Current.Resources.MergedDictionaries.Remove(resourceDictionary);
Application.Current.Resources.MergedDictionaries.Add(resourceDictionary);
}
// Inform the threads of the new culture
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(culture);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture);
}
This allows to change on runtime every string resources from different resourcedictionnary.
But not every resources: those which are used in xaml via DynamicResource like this :
<GroupBox Header="{DynamicResource RootSettings}" >
are correctly updated.
But those which are used in C# code like in ViewModel like this :
stTestConnection = System.Windows.Application.Current.Resources["Connectionsucceded"].ToString();
do not update.
All string Resources are declared in different resources dictionaries like this :
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:system="clr-namespace:System;assembly=mscorlib">
<!-- #region Global -->
<system:String x:Key="CriticalErrorTitle">Critical Error</system:String>
<system:String x:Key="ErrorTitle">Error</system:String>
<system:String x:Key="CloseApp">Application will close</system:String>
Any idea why the string resources used in C# are not updating but the ones used in XAML are ?
Ask me if more info needed.
Thanks
I had to do something similar recently with a runtime theme change performing a colour change using Xamarin in c#.
If I remember correctly then in Xaml with DynaminResource they updated automatically. But when in came to the c# code I had to do so manually. This was triggered by a xamarin OnThemeChanged.
If you have access to a similar method during culture change, you could put all your update logic there.

Drawing onto ESRI.ArcGIS.Client.FeatureLayer with a specific color

I am trying to build a feature that lets the user draw onto specific features layers with a specific color. By changing FeatureLayer.Renderer all annotations on the layer change to the specified color, even the annotations that were there from a previous session. I want to be able to have the old annotations there with their specific color and new ones be drawn with their specific color (potentially different).
Here is the XAML where the map and feature layer is defined
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="GraphicsDictionary.xaml" x:Name="LineSymbolResourceDictionary"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid Name="MapGrid">
<esri:Map x:Name="MyMap">
<esri:ArcGISTiledMapServiceLayer
ID="StreetMapLayer"
x:Name="BaseMap"
Url="http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer"
>
</esri:ArcGISTiledMapServiceLayer>
<esri:FeatureLayer
ID="MyFeatureLayer"
x:Name="MyFeatureLayer"
Url="http://123.123.123.12:6080/arcgis/rest/services/Prj/FeatureServer/0"
Renderer="{StaticResource BlueSimpleRenderer}"
EndSaveEdits="drawLayer_EndSaveEdits"
>
</esri:FeatureLayer>
</esri:Map>
</Grid>
The resource dictionary:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:esriSymbols="clr-namespace:ESRI.ArcGIS.Client.Symbols;assembly=ESRI.ArcGIS.Client"
xmlns:esri="clr-namespace:ESRI.ArcGIS.Client;assembly=ESRI.ArcGIS.Client"
x:Class="ClassName.GraphicsDictionary"
>
<esri:SimpleRenderer x:Key="BlueSimpleRenderer">
<esri:SimpleRenderer.Symbol>
<esriSymbols:SimpleLineSymbol x:Name="BlueLineSymbol" Color="#00007F" Width="5"/>
</esri:SimpleRenderer.Symbol>
</esri:SimpleRenderer>
The C# Code for drawing:
public void StartDrawing(FeatureLayer inputLayer, string inputColorInHex)
{
MyMap.Cursor = System.Windows.Input.Cursors.Pen;
//Below's the color that might be different from the original renderer
SimpleRenderer newRend= new SimpleRenderer
{
Symbol = new SimpleLineSymbol((Color)ColorConverter.ConvertFromString(inputColorInHex), 12)
};
inputLayer.Renderer = newRend as IRenderer;
MyDrawObject.DrawMode = ESRI.ArcGIS.Client.DrawMode.Freehand;
MyDrawObject.IsEnabled = true;
}
The renderer applies to all features (graphics) in the layer so if you want to use a renderer you could use a unique value renderer and set different styles for different attribute values though this implies that you know what values to use when defining the colours. Similarly if you want to control the colours based on a range of values then you can use a class breaks renderer.
In your situation though you may just want to set specific symbols for features since a symbol will override the renderer. You just apply the symbol to the graphic in order for it to be used. e.g.
var markerSym = new Esri.ArcGISRuntime.Symbology.SimpleMarkerSymbol
{
Style = Esri.ArcGISRuntime.Symbology.SimpleMarkerStyle.Diamond,
Color = Colors.Green,
Size = 18
};
var pointGraphic = new Esri.ArcGISRuntime.Layers.Graphic(point, markerSym);
graphicsLayer.Graphics.Add(pointGraphic);

How to update strings that are generated by a converter when I change language?

I have a WPF application in which I support multiple langauges. I can change the language runtime, as I load different sets of resource dictionaries containing the string resources upon change of language.
private void UpdateLoadedLanguages(object sender, SelectionChangedEventArgs e)
{
RemoveLoadedLanguages();
ResourceDictionary aResourceDictionary = new ResourceDictionary();
ResourceDictionary bResourceDictionary = new ResourceDictionary();
ResourceDictionary cLanguageResourceDictionary = new ResourceDictionary();
Language selectedLanguage = (Language) e.AddedItems[0];
switch (selectedLanguage)
{
case Language.enUS:
a.Source = new Uri("pack://application:,,,/[Projecta];component/src/Helpers/Language.en-US.xaml");
b.Source = new Uri("pack://application:,,,/[Projectb];component/src/resources/Language.en-US.xaml");
c.Source = new Uri("pack://application:,,,/[Projectc];component/src/resources/Language.en-US.xaml");
break;
case Language.nlNL:
a.Source = new Uri("pack://application:,,,/[Projecta];component/src/Helpers/Language.nl-NL.xaml");
b.Source = new Uri("pack://application:,,,/[Projectb];component/src/resources/Language.nl-NL.xaml");
c.Source = new Uri("pack://application:,,,/[Projectc];component/src/resources/Language.nl-NL.xaml");
break;
default:
a.Source = new Uri("pack://application:,,,/[Projecta];component/src/Helpers/Language.en-US.xaml");
b.Source = new Uri("pack://application:,,,/[Projectb];component/src/resources/Language.en-US.xaml");
c.Source = new Uri("pack://application:,,,/[Projectc];component/src/resources/Language.en-US.xaml");
break;
}
App.Current.Resources.MergedDictionaries.Add(aResourceDictionary );
App.Current.Resources.MergedDictionaries.Add(bResourceDictionary );
App.Current.Resources.MergedDictionaries.Add(cResourceDictionary );
}
A resource dictionary with the translations typically looks like this:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<system:String x:Key="DefaultApplicationTitleString">Title</system:String>
<system:String x:Key="DefaultDoneString">Done</system:String>
<system:String x:Key="DefaultCancelString">Cancel</system:String>
<system:String x:Key="DefaultDescriptionString">Description unknown</system:String>
<system:String x:Key="LanguageSelectionString">Language</system:String>
<system:String x:Key="AisleString">Aisle</system:String>
<system:String x:Key="LevelsString">levels</system:String>
</ResourceDictionary>
When I use one of the strings as defined above like directly below it works perfect.
Title="{DynamicResource DefaultApplicationTitleString}"
But with a converter like this ...
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string text = (string) Application.Current.FindResource("DefaultDescriptionString");
if (value != null)
{
string temp = (string) value;
char[] stringSeparators = { 'A', '.', '-' };
string[] stringElements= temp.Split(stringSeparators, StringSplitOptions.RemoveEmptyEntries);
string aisleString = (string) Application.Current.FindResource("AisleString");
string levelsString = (string) Application.Current.FindResource("LevelsString");
text = aisleString + " " + stringElements[1] + ", " + levelsString + " " + stringElements[2] + " - " +
stringElements[3];
}
return text;
}
... that I use like this ...
<TextBlock Text="{Binding Id, Converter={StaticResource ItemIdToTextConverter}}" />
... then my strings aren't updated when I change the language. If the property that I convert is updated on its viewModel then the language change does happen.
So my question: Does anyone have a solution for me where also my strings as a result of converters are updated on a language change?
My guesses for a solution are:
force an update of all frameworkelements that use a resource (How would I do this?)
or maybe do something with DynamicResourceExtenstion (I found this question: Return a dynamic resource from a converter, however I do not understand the explanation of the DynamicResourceExtension. So is this the way to go? If so, how would I implement this?)
This would be too much for a comment, but to little for an answer (or maybe not).
Converter is called only once when element is initializing. After that there is no link to what was the source if you are using StaticResource. If you use DynamicResource, then anytime when source get changed converter will be called again.
Do you change source in your localization approach? Nope.
Does converter gets invoked when language is changed? Yes, but only on notification from Id (PropertyChanged).
One way you can fix it is to subscribe your ViewModel to language switch event and call property changed event for each property. This will execute your converter despite source values still the same.
Other possibility would be to register somehow objects which required localized data. You will not use converter anymore, but an attached property (or behavior, depends on complexity). Enumerating (or keeping a list) over elements with such property and updating it shouldn't be hard.
Third way (which I am going to use in my very first localized wpf application) is reflection. Register all windows upong creation (un-register when unloading of course), define a schema of how you identify localizable properties of elements and go through it - win.
Regarding schema. My "localizable" controls with static content will be using already existing Uid to define a key, instead of defining things in separate resource dictionary it will be automatically generated). Dynamic ones I don't yet decide, perhaps there will be a behavior or attached property or even small peace of code behind (I like MVVM, but I am not making it an absolute) to reassign values from static class with predefined strings, which will be changed by reflection.

InvalidOperationException, the calling thread should be STA because

I am developping a project in order to manipulate a lot of objects with several modalities (mouse, leapmotion, touch ...). I made it using the MVVM pattern soI have several Views and ViewModels for all the components I will use. To make it easier to develop I chose to have a Canvas component in which I will manipulate Grids. Each Grid can contain any type of object (Shape, Text, Image, Documents...).
To be able to have all modalities linked to my method, I decided to build one listener per modality (1 for the mouse, 1 for the leapmotion...) and make them detect basic gestures (as Click, DoubleClick ...). All the gestures I chose to detect are associate with a method via a Dictionary. Anyway the linking is working as expected as it executes the right method. T o give an example I have the action calling in my mouse listener:
if (_leftClickCounter == 1 && _capturedLeft == false)
{
if (_dic.ContainsKey(Key.OnClick))
{
Action<object> action = _dic[Key.OnClick];
action.Invoke(null);
}
}
Where:
_dic is my dictionary
Key an enumeration of gestures (as OnClick, OnDoubleClick ...)
action the method to execute
In my example the method executed is:
public void Add(object sender)
{
ObjectModel objectModel = new ObjectModel();
ObjectView objectView = new ObjectView(objectModel);
this.objectViews.Add(objectView);
}
Where sender is just used for test purpose. It remains unused in the method. My execution stops when it tries to instanciate my ObjectView saying:
InvalidOperationException
The calling thread must be STA, because many UI components require this
My ObjectView.xaml.cs class is:
public partial class ObjectView : UserControl
{
public ObjectView(ObjectModel obj)
{
InitializeComponent();
EventLinker linker = new EventLinker(this.visualBox);
ObjectViewModel objectVM = new ObjectViewModel(obj, linker);
this.DataContext = objectVM;
}
}
And its ObjectView.xaml defining the UserControl to use is very basic:
<UserControl x:Class="AusyTouchMultimodal_v1.View.ObjectView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid x:Name="visualBox" Background="Blue"/>
</UserControl>
I dont have any compilation errors, just this InvalidOperationException. Can someone explain this issue to me?
Thanks!
Try calling your actions in ui thread, like this
if (_leftClickCounter == 1 && _capturedLeft == false)
{
if (_dic.ContainsKey(Key.OnClick))
{
Action<object> action = _dic[Key.OnClick];
// action.Invoke(null);
System.Windows.Application.Current.Dispatcher.BeginInvoke( call your action )
}
}

proper way of binding a ResourceDictionaries Source property

The problem I have is the following, I want to be able to have localization in my silverlight app without the usage of the usual resource file (since that approach did not work for me).
Any way I have several dictionaries for each language that I support, basically in the following:
Localization.xaml --> for the default language english
Localization.de.xaml --> for german
Localization.fr.xaml --> for french, I think you get the idea now
.
.
.
now when define the merged dictionaries in my app.xaml I would need to be able the dynamically define which Localization.xaml to use depending on the current culture.
I know I could do something like this in the code behind of the app.xaml:
private void Application_Startup(object sender, StartupEventArgs e)
{
this.RootVisual = new MainPage();
try
{ ResourceDictionary dict = new ResourceDictionary()
{
Source = new Uri("/assembly;component/.../Localizations." + Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName + ".xaml", UriKind.Relative)
};
Resources.MergedDictionaries.RemoveAt(0);
Resources.MergedDictionaries.Add(dict);
}
catch(Exception){}
}
There are two concern that prevent me form using it however.
First is that it is in the code behind, since I am using MVVM I would like to avoid doing that.
Second is that I have to foumble with the MergedDictionaries, the 0-th dictionary is basically the default Localization.xaml. So if I were to change the position of that dictionary, I have to ensure that I also edit it in the code behind, which leaves room for errors.
So what I did was to write a SourceProvider, which give ne the Uri based on the current culture.
the code looks like this:
public class SourceProvider
{
public static Uri LocalizationSource
{
get
{
Uri source = new Uri("assembly;component/.../Localization." + Thread.CurrentThread.CurrentCulture.TwoLetterISOLanguageName + ".xaml", UriKind.Relative);
try
{
ResourceDictionary dict = new ResourceDictionary(){Source = source};
}
catch(Exception)
{
source = new Uri("/assembly;component/.../Localization.xaml", UriKind.Relative);
}
return source;
}
}
}
Now in the App.xaml I can uses this as follows:
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ns="clr-namespace:namespace of the sourceprovider"
x:Class="Peripherie.Configurator.App">
<Application.Resources>
<ResourceDictionary>
<ns:SourceProvider x:Key="Sourcer"/>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="{Binding LocalizationSource, Source={StaticResource Sourcer}}"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
This all works fine and I can bascially use the correct source, however rhis will give me the "Value does not fall within the expected range" error, which is kinda annoying.
Also all StaticResources used are not found because of the error as well, so it would show error which are not actually there, potentially flodding me with thousands of error for missing resources
Is there a way to prevent that ?

Categories

Resources