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.
I would like to give the user an option to change the theme from dark to light, by clicking the button.
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyApp"
x:Class="CoreBackup.App">
<Application.DataTemplates>
<local:ViewLocator/>
</Application.DataTemplates>
<Application.Styles>
<StyleInclude Source="avares://Avalonia.Themes.Default/DefaultTheme.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Accents/BaseDark.xaml"/>
<StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Default.xaml"/>
</Application.Styles>
How I can get access from other ViewModel to the Application.Styles?
I don't know if the problem is solved. So may be this helps to switch the theme from BaseDark to BaseLight.
private void ChangeLayoutExecute(object o)
{
// create new style
var newStyle = new StyleInclude(new Uri("avares://AvaloniaApplicationTest/App.xaml"));
newStyle.Source = new Uri("avares://Avalonia.Themes.Default/Accents/BaseLight.xaml");
// load style to get access to the ressources
var baseDarkStyle = newStyle.Loaded as Style;
// get the original source (BaseDark)
var ressourceFromAppXaml = ((Style)((StyleInclude)Application.Current.Styles[1]).Loaded).Resources;
foreach (var item in baseDarkStyle.Resources)
{
// for secure lookup if the key exists for the resource otherwise create it
if (ressourceFromAppXaml.ContainsKey(item.Key))
ressourceFromAppXaml[item.Key] = item.Value;
else
ressourceFromAppXaml.Add(item.Key, item.Value);
}
// set source name for the new theme
((StyleInclude)Application.Current.Styles[1]).Source = new Uri("avares://Avalonia.Themes.Default/Accents/BaseLight.xaml");
}
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);
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.
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.