ResourceDictionary Locator - wpf

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/.

Related

Metro MahApps MessageBox theme

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 to load style using xamlreader

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 { }
}
}

Change theme at runtime

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

WPF : Keeping default style when inheriting from ListBoxItem

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 ?

Redefine/alias a resource in WPF?

Is there a way to redefine/alias an existing SolidColorBrush (or any other resource, actually)?
Case in point: I have a brush in an external ResourceDictionary that I want to reference by my own key instead of the original key. I don't want to be dependent on the external reference, since the actual brush is prone to change in the future.
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<SolidColorBrush x:Key="SomeExternalResource">Red</SolidColorBrush>
</Window.Resources>
<Grid>
<Grid.Resources>
<StaticResourceExtension ResourceKey="SomeExternalResource" x:Key="SomeAliasedResource"/>
</Grid.Resources>
<Border Background="{StaticResource SomeAliasedResource}"/>
</Grid>
</Window>
I don't want to be dependent on the
external reference, since the actual
brush is prone to change in the
future.
You'll still be dependent on the external resource, just not in as many places.
I have an update to Ruedi's solution. This works for resources both within the same resource dictionary and anywhere within the application.
public class Alias : MarkupExtension
{
public string ResourceKey { get; set; }
public Alias()
{
}
public Alias(string resourceKey)
{
ResourceKey = resourceKey;
}
public override object ProvideValue(IServiceProvider serviceProvider)
{
return _ProvideLocalValue(serviceProvider) ?? _ProvideApplicationValue();
}
private object _ProvideLocalValue(IServiceProvider serviceProvider)
{
var rootObjectProvider = (IRootObjectProvider)
serviceProvider.GetService(typeof(IRootObjectProvider));
if (rootObjectProvider == null) return null;
var dictionary = rootObjectProvider.RootObject as IDictionary;
if (dictionary == null) return null;
return dictionary.Contains(ResourceKey) ? dictionary[ResourceKey] : null;
}
private object _ProvideApplicationValue()
{
var value = Application.Current.TryFindResource(ResourceKey);
return value;
}
}
The usage is similar to above, but the key is to use Alias as the markup extension where used, not StaticResource. Somewhere in the application resource stack, define the resources...
<Application x:Class="WPF.App"
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"
xmlns:wpf="clr-namespace:WPF"
StartupUri="MainWindow.xaml">
<Application.Resources>
<system:String x:Key="Text">Display some text.</system:String>
<system:String x:Key="Other">4</system:String>
<wpf:Alias x:Key="Alias" ResourceKey="Text"/>
<wpf:Alias x:Key="Second" ResourceKey="Other"/>
</Application.Resources>
</Application>
And wherever you're referencing the aliases...
<Window x:Class="WPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:wpf="clr-namespace:WPF"
Title="MainWindow" Height="150" Width="300">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Text="{wpf:Alias Alias}"/>
<TextBlock Text="{wpf:Alias Second}"/>
</StackPanel>
</Window>
My solution required referencing strings, but it works for any object you want to alias.
You can try to tuse the StaticResourceExtension, but in global resource dictionaries this does not work (strange compiler and runtime errors):
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<SolidColorBrush x:Key="StatusColor_OK" Color="#ff32a248" />
<StaticResourceExtension
x:Key="AliasKey"
ResourceKey="StatusColor_Error" />
</ResourceDictionary>
To overcome this problem, I created the following class:
/// <summary>
/// Defines an Alias for an existing resource. Very similar to
/// <see cref="StaticResourceExtension"/>, but it works in
/// ResourceDictionaries
/// </summary>
public class Alias: System.Windows.Markup.MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
IRootObjectProvider rootObjectProvider = (IRootObjectProvider)
serviceProvider.GetService(typeof (IRootObjectProvider));
if (rootObjectProvider == null) return null;
IDictionary dictionary = rootObjectProvider.RootObject as IDictionary;
if (dictionary == null) return null;
return dictionary[ResourceKey];
}
public object ResourceKey { get; set; }
}
Usage:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<SolidColorBrush x:Key="StatusColor_OK" Color="#ff32a248" />
<Alias
x:Key="AliasKey"
ResourceKey="StatusColor_Error" />
</ResourceDictionary>

Resources