I have a WPF application with a theme (ShinyRed.xaml) and I want to have a button that when clicked changes the theme to ShinyBlue.xaml
I load in the theme initially in App.xaml:
<Application.Resources>
<ResourceDictionary Source="/Themes/ShinyBlue.xaml"/>
</Application.Resources>
How might I do this?
How you could do it:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary x:Name="ThemeDictionary">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Themes/ShinyRed.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
<!-- ... -->
public partial class App : Application
{
public ResourceDictionary ThemeDictionary
{
// You could probably get it via its name with some query logic as well.
get { return Resources.MergedDictionaries[0]; }
}
public void ChangeTheme(Uri uri)
{
ThemeDictionary.MergedDictionaries.Clear();
ThemeDictionary.MergedDictionaries.Add(new ResourceDictionary() { Source = uri });
}
//...
}
In your change method:
var app = (App)Application.Current;
app.ChangeTheme(new Uri("New Uri here"));
Here is an article that will walk you through it:
http://svetoslavsavov.blogspot.com/2009/07/switching-wpf-interface-themes-at.html
Basically you need to remove the "old" theme from the resource dictionary and then merge in the new one. The above article shows you how to make this change very simple.
Im using the following command to set the theme at runtime:
Application.Current.Resources.Source = new Uri("/Themes/ShinyRed.xaml", UriKind.RelativeOrAbsolute);
App.xaml
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/Font.xaml" />
<ResourceDictionary Source="Themes/Light.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
In your code:
> Application.Current.Resources.MergedDictionaries[1].Source = new Uri("Themes/Dark.xaml", UriKind.RelativeOrAbsolute);
you can check with this to be sure nothing grow
Application.Current.Resources.MergedDictionaries.Count.ToString();
H.B.'s answer did not run for me, I had to do this (works, tested):
Uri dictUri = new Uri(#"/Resources/Themes/MyTheme.xaml", UriKind.Relative);
ResourceDictionary resourceDict = Application.LoadComponent(dictUri) as ResourceDictionary;
Application.Current.Resources.MergedDictionaries.Clear();
Application.Current.Resources.MergedDictionaries.Add(resourceDict);
To pretty it up:
// Place in App.xaml.cs
public void ChangeTheme(Uri uri)
{
ResourceDictionary resourceDict = Application.LoadComponent(uri) as ResourceDictionary;
Application.Current.Resources.MergedDictionaries.Clear();
Application.Current.Resources.MergedDictionaries.Add(resourceDict);
}
// Example Usage (anywhere in app)
private void ThemeRed_Click(object sender, RoutedEventArgs e)
{
var app = App.Current as App;
app.ChangeTheme(new Uri(#"/Resources/Themes/RedTheme.xaml", UriKind.Relative));
}
Related
my problem is that the embedded dlls of MahApps.Metro and MahApps.Metro.IconPacks are not working fine.
Used the Visual Studio debugger to check if they are getting loaded and it worked fine. If it could not load them the program would throw a xaml exception.
But for some reason the ResourceDictionaries which are merged are not working in runtime.
Should look like this
But looks like this
(As you can see the text color is different and it is missing the icon on the right side => does not load the styles.)
It looks like the first picture if both Dll's are provided in the directory of the program.
My App.xaml
<Application x:Class="Launcher.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:converter="clr-namespace:Launcher.Class.Converter"
>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/FlatSlider.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro.IconPacks;component/Themes/IconPacks.xaml" />
<ResourceDictionary Source="Controls\ColorBrushes.xaml"/>
<ResourceDictionary Source="Controls\CustomMetroWindow.xaml"/>
<ResourceDictionary Source="Controls\CustomListView.xaml"/>
<ResourceDictionary Source="Controls\NewsStyle.xaml"/>
<ResourceDictionary Source="Controls\TextImageBox.xaml"/>
<ResourceDictionary Source="Controls\GlowMetroButton.xaml"/>
<ResourceDictionary Source="Controls\ToggleSwitchWin10.xaml"/>
<ResourceDictionary Source="Simple Styles.xaml"/>
</ResourceDictionary.MergedDictionaries>
<converter:InverseBooleanConverter x:Key="InverseBooleanConverter" />
<converter:BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter" />
<converter:BooleanToVisibilityCollapsedConverter x:Key="BoolToVisibilityCollapsedConverter" />
<converter:MultiObjectToBooleanConverter x:Key="MultiObjectToBooleanConverter" />
<converter:DownloadProgressToVisibilityConverter x:Key="DownloadProgressToVisibilityConverter" />
<converter:MultiObjectToStatusBarColorConverter x:Key="MultiObjectToStatusBarColorConverter" />
<converter:MultiBooleanConverter x:Key="MultiBooleanConverter" />
<converter:MultiBooleanToVisibilityConverter x:Key="MultiBooleanToVisibilityConverter" />
<converter:OpacityToBooleanConverter x:Key="OpacityToBooleanConverter" />
</ResourceDictionary>
</Application.Resources>
My Program.cs
[STAThread]
public static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += (sender, args) =>
{
var resourceName = Assembly.GetExecutingAssembly().GetName().Name + ".Dll." + new AssemblyName(args.Name).Name + ".dll";
if (!resourceName.Contains("resources"))
{
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName))
{
if (stream != null)
{
var assemblyData = new Byte[stream.Length];
stream.Read(assemblyData, 0, assemblyData.Length);
return Assembly.Load(assemblyData);
}
}
}
else
{
Assembly requestedAssembly = args.RequestingAssembly;
AssemblyName requestedAssemblyName = new AssemblyName(args.Name);
while (true)
{
// requesting name in format: %assemblyname%.resources
// rewrite to: %assemblyName%.%assemblyName%.%culture%.resources.dll
//
var baseName = requestedAssemblyName.Name.Substring(0, requestedAssemblyName.Name.Length - ".resources".Length);
var name = string.Format("{0}.Dll.Lang.{1}.{2}.resources.dll", baseName, requestedAssemblyName.CultureInfo.Name, Assembly.GetExecutingAssembly().GetName().Name);
// by default for resources the requestingAssembly will be null
Assembly asm = null;
var assemblies = AppDomain.CurrentDomain.GetAssemblies();
// resources have the same name as their belonging assembly, so find by name
var parentName = requestedAssemblyName.Name.Substring(0, requestedAssemblyName.Name.Length - ".resources".Length);
// I'd love to use linq here, but Cecil starts fucking up when I do (null reference exception on assembly.Write)
// without a Linq query it works fine, though
// ReSharper disable once LoopCanBeConvertedToQuery
foreach (var assembly in assemblies)
{
if (assembly.GetName().Name == parentName)
{
asm = assembly;
}
}
if (asm == null)
{
// cannot find assembly from which to load
return null;
}
using (var stream = asm.GetManifestResourceStream(name))
{
if (stream != null)
{
var bytes = new byte[stream.Length];
stream.Read(bytes, 0, bytes.Length);
return Assembly.Load(bytes);
}
}
// did not find the specific resource yet
// attempt to use the parent culture, this follows the .Net resource fallback system
// e.g. if sub resource de-DE is not found, then .Parent will be "de", if that is not found parent will probably be default resource
var fallback = requestedAssemblyName.CultureInfo.Parent.Name;
if (string.IsNullOrEmpty(fallback))
{
// is empty if no longer a parent
// return null so .Net can load the default resource
return null;
}
var alteredAssemblyName = requestedAssemblyName.FullName;
alteredAssemblyName = alteredAssemblyName.Replace(string.Format("Culture={0}", requestedAssemblyName.CultureInfo.Name), string.Format("Culture={0}", fallback));
requestedAssemblyName = new AssemblyName(alteredAssemblyName);
}
}
return null;
};
App.Main();
}
The control
<TextBox x:Name="Username"
Controls:TextBoxHelper.Watermark="{lex:Loc Key=LoginWindow.YourUsername}" Margin="0,20,0,9"
Text="{Binding Config.AuthUsername}"
IsEnabled="{Binding LoggingIn, Converter={StaticResource InverseBooleanConverter}}" TextAlignment="Justify"
>
<TextBox.Resources>
<Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource MetroTextImageBox}">
<Setter Property="Controls:TextBoxHelper.ButtonTemplate">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ButtonBase}">
<Grid Background="{TemplateBinding Background}">
<Rectangle>
<Rectangle.Fill>
<VisualBrush>
<VisualBrush.Visual>
<iconPacks:PackIconModern Kind="User" Foreground="{StaticResource MainIconBrush}" />
</VisualBrush.Visual>
</VisualBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</TextBox.Resources>
</TextBox>
This code works for me: (add all libraries as Embedded Resource and set startup object to this class)
public class Program
{
private static Assembly ExecutingAssembly = Assembly.GetExecutingAssembly();
private static string[] EmbeddedLibraries =
ExecutingAssembly.GetManifestResourceNames().Where(x => x.EndsWith(".dll")).ToArray();
[STAThreadAttribute]
public static void Main()
{
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
App.Main();
}
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
// Get assembly name
var assemblyName = new AssemblyName(args.Name).Name + ".dll";
// Get resource name
var resourceName = EmbeddedLibraries.FirstOrDefault(x => x.EndsWith(assemblyName));
if (resourceName == null)
{
return null;
}
// Load assembly from resource
using (var stream = ExecutingAssembly.GetManifestResourceStream(resourceName))
{
var bytes = new byte[stream.Length];
stream.Read(bytes, 0, bytes.Length);
return Assembly.Load(bytes);
}
}
}
The project file portion was cut off previous answer.
Add this to project file
<!--Then add this is project file.-->
<Target Name="EmbedReferencedAssemblies" AfterTargets="ResolveAssemblyReferences">
<ItemGroup>
<!-- get list of assemblies marked as CopyToLocal -->
<AssembliesToEmbed Include="#(ReferenceCopyLocalPaths)" Condition="'%(Extension)' == '.dll'" />
<!-- add these assemblies to the list of embedded resources -->
<EmbeddedResource Include="#(AssembliesToEmbed)">
<LogicalName>%(AssembliesToEmbed.DestinationSubDirectory)%(AssembliesToEmbed.Filename)%(AssembliesToEmbed.Extension)</LogicalName>
</EmbeddedResource>
</ItemGroup>
<Message Importance="high" Text="Embedding: #(AssembliesToEmbed->'%(DestinationSubDirectory)%(Filename)%(Extension)', ', ')" />
</Target>
<!-- no need to copy the assemblies locally anymore -->
<Target Name="_CopyFilesMarkedCopyLocal" />
I had the same issue you described and came across fix that resolves the issue.
public partial class App : Application {
public App() {
AppDomain.CurrentDomain.AssemblyResolve += OnResolveAssembly;
}
private static Assembly OnResolveAssembly(object sender, ResolveEventArgs args) {
var executingAssembly = Assembly.GetExecutingAssembly();
var assemblyName = new AssemblyName(args.Name);
var path = assemblyName.Name + ".dll";
if (assemblyName.CultureInfo.Equals(CultureInfo.InvariantCulture) == false) {
path = String.Format(#"{0}\{1}", assemblyName.CultureInfo, path);
}
using (var stream = executingAssembly.GetManifestResourceStream(path)) {
if (stream == null)
return null;
var assemblyRawBytes = new byte[stream.Length];
stream.Read(assemblyRawBytes, 0, assemblyRawBytes.Length);
return Assembly.Load(assemblyRawBytes);
}
}
}
%(AssembliesToEmbed.DestinationSubDirectory)%(AssembliesToEmbed.Filename)%(AssembliesToEmbed.Extension)
'%(DestinationSubDirectory)%(Filename)%(Extension)',
', ')" />
Maybe someone could show me how to correctly implement Async message into Metro window that it would have application's current theme and accent?
The code taken from the demo sample works, but theme and accent remain default:
private async void ClosingApp(object sender, System.ComponentModel.CancelEventArgs e)
{
e.Cancel = !_shutdown;
if (_shutdown) return;
var mySettings = new MetroDialogSettings()
{
AffirmativeButtonText = "Quit",
NegativeButtonText = "Cancel",
AnimateShow = true,
AnimateHide = false
};
var result = await this.ShowMessageAsync("Quit application?",
"Sure you want to quit application?",
MessageDialogStyle.AffirmativeAndNegative, mySettings);
_shutdown = result == MessageDialogResult.Affirmative;
if (_shutdown)
Application.Current.Shutdown();
}
When I simply change the theme:
private void MenuItem_Click(object sender, RoutedEventArgs e)
{
// set the Red accent and dark theme only to the current window
var theme = ThemeManager.GetAppTheme("BaseDark");
var accent = ThemeManager.GetAccent("Red");
ThemeManager.ChangeAppStyle(Application.Current, accent, theme);
}
I get the default white and blue MessageBox. What am I doing wrong?
I've tried your code and it is working. I've only transformed the MenuItem_Click in a Button_Click but it is irrelevant.
I'm getting the black background and a red Quit like below, iff I close the App after clicking the settings button.
instead of the initial
I have the standard Window1.xaml starting with
<Controls:MetroWindow x:Class="TestFrontend.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Controls="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
Closing="App_Closing"
Title="Test" Height="600" Width="600"
>
and the Resources in the App.xaml
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="Window1.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! -->
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
<!-- Accent and AppTheme setting -->
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
I just needed to add the default Resource Dictionaries
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" />
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseLight.xaml" />
in the App.xaml
How can i load style from below xaml using XamlReader.Load()
<Window
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:dxg="http://schemas.devexpress.com/winfx/2008/xaml/grid"
xmlns:dxgt="http://schemas.devexpress.com/winfx/2008/xaml/grid/themekeys"
>
<ResourceDictionary>
<Style x:Key="LastRowHighlighted"
BasedOn="{StaticResource {dxgt:GridRowThemeKey ResourceKey=RowStyle}}"
TargetType="{x:Type dxg:GridRowContent}">
</Style>
</ResourceDictionary>
Try something like this:
public void LoadStyle(string fileName)
{
if (File.Exists(fileName))
{
try
{
using (FileStream fileStream = new FileStream(fileName, FileMode.Open,
FileAccess.Read, FileShare.Read))
{
ResourceDictionary resourceDictionary = (ResourceDictionary)XamlReader.
Load(fileStream);
Resources.MergedDictionaries.Clear(); // optional
Resources.MergedDictionaries.Add(resourceDictionary);
}
}
catch { }
}
}
Is there a way to create a ResourceDictionary Locator.
Right now I have in xaml:
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="../../Resources/StringResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
Where the code behind sets the list of dictionaries based on currentculture
I'd like to have
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<Locator "StringResources"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
Or something like that such that I don't have to modify each v.xaml.cs files
You could create your own ResourceDictionary:
public class ResourceDictionaryLocator : ResourceDictionary
{
public ResourceDictionaryLocator()
{
switch (CurrentLocalization)
{
case "English":
base.Source = new Uri("pack://application:,,,/Languages/English.xaml");
break;
case "French":
base.Source = new Uri("pack://application:,,,/Languages/French.xaml");
break;
}
}
}
Then consume it like this:
<Application x:Class="TestingWPF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestingWPF"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<local:ResourceDictionaryLocator />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
This is generally not done, however. You can load different resource dictionaries at runtime. Something like the following:
Application.Current.Resources.MergedDictionaries.Clear();
Application.Current.Resources.MergedDictionaries.Add(dictionary);
You can create a MarkupExtension that will return the requested ResourceDictionary:
public class ResourceDictionaryLocator : MarkupExtension
{
public string DictionaryName { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
// Logic to return the wanted ResourceDictionary
if (DictionaryName == "...")
{
}
return null;
}
}
Then use it in xaml:
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<local:ResourceDictionaryLocator DictionaryName="StringResources" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
I really don't know if this is posible, but do you want to make that your application be 'Localizable'? Baybe this article could be helpful to you: http://www.codeproject.com/KB/WPF/WPF_Resx_Localization/.
Hey Guys (and girls if any :)
I needed a different list box selection policy than the one provided by default with WPF listboxes, i.e. beeing able to have an extended selection withou any modifier key.
No problem on that, here is what I did :
class EnhancedCcyPairListBox : ListBox
{
protected override DependencyObject GetContainerForItemOverride()
{
return new CcyPairListBoxItem();
}
}
internal class CcyPairListBoxItem : ListBoxItem
{
protected override void OnPreviewMouseLeftButtonDown(MouseButtonEventArgs e)
{
IsSelected = !IsSelected;
e.Handled = true;
}
}
I can't tell if it's the best way to do, but it seems to work exactly as I expected.
Except that... by doing so, I have lost the defaults ListBoxItem style I had before. How can I tell to my derived classes to keep their default style ?
Thank you very much !
Add this above the EnhancedCcyPairListBox declaration
[StyleTypedProperty(Property = "ItemContainerStyle", StyleTargetType = typeof(CcyPairListBoxItem))]
Edit
Add this to the static constructor of CcyPairListBoxItem"
DefaultStyleKeyProperty.OverrideMetadata(
typeof(CcyPairListBoxItem), new FrameworkPropertyMetadata(typeof(CcyPairListBoxItem)));
and in Themes/Generic.xaml add
<Style TargetType="{x:Type CcyPairListBoxItem}"
BasedOn="{StaticResource {x:Type ListBoxItem}}"/>
Because the WPF context of our application is quite special, I wanted to be sure that it was not an issue that was very specific.
So I took the above two classes and imported them in a brand new WPF test project. I created a Themes directory that registered a Resource dictionnary in which I put the Style as NVM mentionned earlier.
Content of Generic.xaml :
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/InheritedListBoxStyling;component/Themes/Styles/TempDic.xaml" />
<!-- Reference here the Resource dictionnary used for your own component -->
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
Content of TempDic.xaml :
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:InheritedListBoxStyling="clr-namespace:InheritedListBoxStyling">
<Style TargetType="{x:Type InheritedListBoxStyling:CcyPairListBoxItem}"
BasedOn="{StaticResource {x:Type ListBoxItem}}"/>
</ResourceDictionary>
Content of Window1.xaml.cs
namespace InheritedListBoxStyling
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
DataContext = this;
for (int i = 0; i < 50; i++)
{
source.Add("toto " + i);
}
l1.ItemsSource = source;
l2.ItemsSource = source;
}
public ObservableCollection<string> source = new ObservableCollection<string>();
}
}
Content of Window1.xaml :
<Window x:Class="InheritedListBoxStyling.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:InheritedListBoxStyling="clr-namespace:InheritedListBoxStyling"
Title="Window1" Height="300" Width="300">
<Grid>
<ListBox x:Name="l1" SelectionMode="Extended"
Margin="0,0,12,12" Height="100"
HorizontalAlignment="Right"
VerticalAlignment="Bottom" Width="120" />
<InheritedListBoxStyling:EnhancedCcyPairListBox
x:Name="l2" SelectionMode="Extended"
Height="100" Margin="12,12,0,0"
VerticalAlignment="Top"
HorizontalAlignment="Left" Width="120" />
</Grid>
</Window>
And here the result :
=> the default styling is not applied and, as in my "real case" issue, the only kind of styling that seems to be applied is the gray selected item style.
Any idea about what's going on or what I'm doing wrong ?