WPF - Change theme from dark to light by clicking proper button - Avalonia - c#

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");
}

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.

WPF Color Theme

I have a simple WPF Application to change color themes.
ResourceDictionary blueDict = new ResourceDictionary() { Source = new Uri(#"/Styles/Colors/Blue/BlueColors.xaml", UriKind.Relative) };
ResourceDictionary greenDict = new ResourceDictionary() { Source = new Uri(#"/Styles/Colors/Green/GreenColors.xaml", UriKind.Relative) };
ResourceDictionary yellowDict = new ResourceDictionary() { Source = new Uri(#"/Styles/Colors/Yellow/YellowColors.xaml", UriKind.Relative) };
ResourceDictionary genericDict = new ResourceDictionary() { Source = new Uri(#"/Styles/Colors/GenericColors.xaml", UriKind.Relative) };
On the MainWindow I have a ComboBox that stores three enum values "Blue, Green, Yellow". That's what it does when the selected index has changed:
Application.Current.Resources.MergedDictionaries.Clear();
Themes newTheme = (Themes)cbxThemes.SelectedItem;
if (newTheme == currentTheme)
return;
switch (newTheme)
{
case Themes.Blue:
Application.Current.Resources.MergedDictionaries.Add(blueDict);
break;
case Themes.Green:
Application.Current.Resources.MergedDictionaries.Add(greenDict);
break;
case Themes.Yellow:
Application.Current.Resources.MergedDictionaries.Add(yellowDict);
break;
default:
break;
}
Application.Current.Resources.MergedDictionaries.Add(genericDict);
currentTheme = newTheme;
The first time, everything works well and I can choose whatever color I want, but when I change Colors again, nothing happens.
Is there anything that doesn't update in the background?
The code works, if you output the Application.Current.Resources.MergedDictionaries you can even see the new source. Only the UI is not updated.
I found a solution:
Simply replace
Application.Current.Resources.MergedDictionaries.Add(yourDictionary);
with
Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary() { Source = new Uri(#"yourPath.xaml", UriKind.Relative) });
As often in programming I have no idea why it works but first of all I'm happy. If someone could explain this to me, that'd be great.
Try replacing this line:
Themes newTheme = (Themes)cbxThemes.SelectedItem;
with this line:
string newTheme = ((ComboBoxItem)cbxThemes.SelectedItem).Content.ToString();
Ofcourse you'd also need to change how you handle the comparison between newTheme and currentTheme.
If you don't want to use a string, you can convert it to enum by using:
Themes newTheme;
Enum.TryParse(((ComboBoxItem)cbxThemes.SelectedItem).Content.ToString(), out newTheme);

How to change the title of Timepicker and localization in custom renderer

I have Xamarin forms time picker following custom renderer for IOS
[assembly: ExportRenderer(typeof(TimePicker), typeof(Time24PickerRenderer))]
namespace LabOraTimeStamp.iOS.Renderers
{
public class Time24PickerRenderer:TimePickerRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<TimePicker> e)
{
base.OnElementChanged(e);
var timePicker = (UIDatePicker)Control.InputView;
timePicker.Locale = new NSLocale("no_nb");
//Get the Done button
var toolbar = (UIToolbar)Control.InputAccessoryView;
var doneBtn = toolbar.Items[1];
//Set the Done to OK
doneBtn.Title = "OK";
}
}
}
I wanted to change the default "done" to "Ok".
1) How can I do that? the line mentioned above for setting the title does not affect anything.
2) I already implemented localization for xamarin forms.I just wanted to use existing Resx values from custom renderer to show the string for appropriate culture.How can I achieve that?
So the reason why your code isn't working is because the done button is created with the UIBarButtonSystemItem.Done style. It doesn't care about the Title property. Renderer code here.
To work around that issue you could try replacing the Xamarin created done button with your own custom Ok button.
//Get the Done button
var toolbar = (UIToolbar)Control.InputAccessoryView;
// Replace Xamarin's buttons with custom ones.
var spacer = new UIBarButtonItem(UIBarButtonSystemItem.FlexibleSpace);
var doneButton = new UIBarButtonItem();
doneButton.Title = "OK";
doneButton.Clicked += (o, a) => Control.ResignFirstResponder();
toolbar.SetItems(new [] { spacer, doneButton}, false);

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 ?

DevExpress DropDownButton Problems

I'm trying to create a DevEx drop down button. Unfortunately, I'm running into two problems I can't figure out:
1) I can't get the popup menu to skin correctly, i.e. it doesn't skin as "Office 2010 Blue". The code I'm using is shown below:
private void InitializeSendToPricingSheetButton()
{
var barManager = new BarManager();
if (barManager.Controller == null) barManager.Controller = new BarAndDockingController();
barManager.Controller.PaintStyleName = "Skin";
barManager.Controller.LookAndFeel.UseDefaultLookAndFeel = false;
barManager.Controller.LookAndFeel.SkinName = "Office 2010 Blue";
barManager.ItemClick += HandleSendToPricingSheetClick;
barManager.Items.AddRange(new[] { new BarButtonItem(barManager, "Foo"), new BarButtonItem(barManager, "Bar"), new BarButtonItem(barManager, "Baz") });
var popupMenu = new PopupMenu { Manager = barManager };
foreach (var barItem in barManager.Items) popupMenu.ItemLinks.Add((BarItem)barItem);
popupMenu.ItemLinks[1].BeginGroup = true;
dropDownButtonSendToPricingSheet.DropDownControl = popupMenu;
}
2) This button is on a form. If the form loses focus (e.g. I click on Firefox), the pop-up menu still remains on-top. It won't go away until clicked.
Any suggestions would be much appreciated. Thanks for helping me deal with DevEx insanity.
I have solution to your second question.
You should add drop down button event handler as below:
dropDownButton1.LostFocus += new EventHandler(HidePopUp);
Handler method should be as below:
private void HidePopUp(object sender,object e)
{
dropDownButton1.HideDropDown();
}
For your second question, you should assign value to the bar manager property as:
BarManager manager = new BarManager();
manager.Form = this; // refers to current form
Find below link for reference
https://www.devexpress.com/Support/Center/Question/Details/Q274641
It is probably simpler to use DefaultLookAndFeel
Add this comp to your form and set the theme you'd like to use.
There is no need to set the theme for individual components.
defaultLookAndFeel1.LookAndFeel.SetSkinStyle("Office 2010 Blue");

Categories

Resources