Adding MergedDictionaries to styles via code - c#

I have this style in a Resource-File:
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Colors.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="TextBox" x:Key="StandardTextBox"/>
<Setter Property="Foreground" Value="{StaticResource Color1}"/>
</Style>
</ResourceDictionary>
(Colors.xaml contains my brushes)
My Code to use the style:
ResourceDictionary TetxboxStyles = new ResourceDictionary();
TetxboxStyles.Source = (new Uri("TextboxStyles.xaml", UriKind.RelativeOrAbsolute));
Resources.MergedDictionaries.Add(TetxboxStyles);
tb_input.Style = (Style)Find("StandardTextBox");
This works without a problem but it doesn't work when I dynamically add the Colors-Resource via code instead of in the TextboxStyles-File:
ResourceDictionary TetxboxStyles = new ResourceDictionary();
TetxboxStyles.Source = (new Uri("TextboxStyles.xaml", UriKind.RelativeOrAbsolute));
//Adding the Colors.xaml Resource
ResourceDictionary Colors = new ResourceDictionary();
brushes.Source = (new Uri("Colors.xaml", UriKind.RelativeOrAbsolute));
TetxboxStyles.MergedDictionaries.Add(Colors);
Resources.MergedDictionaries.Add(TetxboxStyles);
tb_input.Style = (Style)Find("StandardTextBox");
Output-Error:
System.Windows.Markup.XamlParseException
"{DependencyProperty.UnsetValue}"

I replaced StaticResource with DynamicResource and it works

Related

My resource dictionary is omitting my label from being showed

I have a wpf app and I am messing with loading themes (light and dark), I made two simple resource dictionary files which are created in a shared assembly:
Dark Theme (same structure for the light theme, but with different color values):
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush Color="#FF1E1E1E" x:Key="Background"/>
<SolidColorBrush x:Key="TextColorBrush" Color="White"/>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{StaticResource TextColorBrush}"/>
</Style>
<Style TargetType="Grid">
<Setter Property="Background" Value="{StaticResource Background}"/>
</Style>
</ResourceDictionary>
In my main application, App.xaml I am referencing my 2 theme dictionaries as such
<Application x:Class="Foo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Foo.Core.WPF;component/Resources/Dictionary_DarkTheme.xaml" x:Name="DarkTheme"/>
<ResourceDictionary Source="pack://application:,,,/Foo.Core.WPF;component/Resources/Dictionary_LightTheme.xaml" x:Name="LightTheme"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
The way I am setting up the resources based on which theme I am choosing is done in the App.xaml.cs
public enum Skin { Light, Dark }
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App : Application
{
public static Skin Skin { get; set; }
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
ChangeSkin(Skin.Light);
}
public void ChangeSkin(Skin newSkin)
{
Skin = newSkin;
if (Skin == Skin.Dark)
ApplyResources(Resources.MergedDictionaries[0].Source.ToString());
else if (Skin == Skin.Light)
ApplyResources(Resources.MergedDictionaries[1].Source.ToString());
}
private void ApplyResources(string src)
{
var dict = new ResourceDictionary() { Source = new Uri(src, UriKind.RelativeOrAbsolute) };
foreach (var mergeDict in dict.MergedDictionaries)
{
Resources.MergedDictionaries.Add(mergeDict);
}
foreach (var key in dict.Keys)
{
Resources[key] = dict[key];
}
}
}
And finally, my main window. Since I want these particular styles to be global I am not using any keys to identify them.
<Window x:Class="Foo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid>
<Label Content="hello"/>
</Grid>
</Window>
But my main issue is that the Label control doesn't show up in my application. I can see my background change color appropriately but my label control is just gone! What am I doing wrong? Many thanks in advance!
Do not add all theme ResourceDictionaries from the start to Application.Resources.MergedDictionaries, i.e. start with empty Application.Resources:
<Application x:Class="Foo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
Then change the theme by replacing Application.Current.Resources.MergedDictionaries with the current theme:
private void ChangeSkin(Skin skin)
{
ResourceDictionary theme = null;
switch (skin)
{
case Skin.Light:
theme = new ResourceDictionary { Source = new Uri("pack://application:,,,/Foo.Core.WPF;component/Resources/Dictionary_LightTheme.xaml") };
break;
case Skin.Dark:
theme = new ResourceDictionary { Source = new Uri("pack://application:,,,/Foo.Core.WPF;component/Resources/Dictionary_DarkTheme.xaml") };
break;
}
if (theme != null)
{
Application.Current.Resources.MergedDictionaries.Clear();
Application.Current.Resources.MergedDictionaries.Add(theme);
}
}
When changing themes only means to replace Colors and Brushes, you may also move your Styles to Application.Resources and use DynamicResource in the Style Setters.
<Application x:Class="Foo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="{DynamicResource TextColorBrush}"/>
</Style>
<Style TargetType="Grid">
<Setter Property="Background" Value="{DynamicResource Background}"/>
</Style>
</Application.Resources>
</Application>
Then your theme ResourceDictionaries would only contain Color and Brush resources:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush Color="#FF1E1E1E" x:Key="Background"/>
<SolidColorBrush x:Key="TextColorBrush" Color="White"/>
</ResourceDictionary>

Finding resource in dll instead of main application

I am working on a DLL that will place two hidden buttons in a WPF application.
Clicking those buttons on the right order will raise an event.
To remove the Mouse Over effect, I created a new style. I wanted the buttons to be completely transparent.
WPF Code
SecretCode.WPF secretCode = new SecretCode.WPF(testGrid, SecretCode.WPF.Location.Bottom, 75, 4000);
secretCode.SecretCodeActivated += secretCode_SecretCodeActivated;
APP.xaml
<Style x:Key="TransparentStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border>
<Border.Style>
<Style TargetType="{x:Type Border}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Transparent"/>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
<Grid Background="Transparent">
<ContentPresenter></ContentPresenter>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
However, the issue is that I don't want to place that style on the WPF project as I want the DLL to be completely independent.
Secret Code DLL
public WPF(Grid grid, Location location, int size, int timeout)
{
Button leftButton = new Button();
leftButton.Width = size;
leftButton.Height = size;
leftButton.Margin = new Thickness(0, 0, 0, 0);
leftButton.HorizontalAlignment = HorizontalAlignment.Left;
leftButton.VerticalAlignment = location == Location.Top? VerticalAlignment.Top : VerticalAlignment.Bottom;
leftButton.Background = Brushes.Transparent;
leftButton.Style = Application.Current.FindResource("TransparentStyle") as Style;
leftButton.Click += leftPanel_Click;
grid.Children.Add(leftButton);
Button rightButton = new Button();
rightButton.Width = size;
rightButton.Height = size;
rightButton.Margin = new Thickness(0, 0, 0, 0);
rightButton.HorizontalAlignment = HorizontalAlignment.Right;
rightButton.VerticalAlignment = location == Location.Top ? VerticalAlignment.Top : VerticalAlignment.Bottom;
rightButton.Background = Brushes.Transparent;
rightButton.Style = Application.Current.FindResource("TransparentStyle") as Style;
rightButton.Click += rightPanel_Click;
grid.Children.Add(rightButton);
timeoutTimer.Interval = timeout;
timeoutTimer.Elapsed += timeoutTimer_Tick;
}
Is there any way to do that?
In some DLL:
<ResourceDictionary >
<Style x:Key="BoringButtonStyle" TargetType="{x:Type Button}">
//.....
</Style>
</ResourceDictionary>
On your application:
<Application ...>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Globals;component/Styles/ButtonStyles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
And then you can use it as a DynamicResource freely:
<UserControl ...>
<Grid>
<Button style="{DynamicResource BoringButtonStyle}"/>
</Grid>
</UserControl>
An alternative way to the code in app.xaml is:
ResourceDictionary dict = new ResourceDictionary();
System.Windows.Application.LoadComponent(dict, new System.Uri("/SomeAssembly;component/SomeResourceDictionary.xaml",System.UriKind.Relative));
This is what I could comeup with for you. Hope this helps.
Use the below string in your DLL class.
private string xamlText = "<Style xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\" x:Key=\"TransparentStyle\" TargetType=\"Button\">" +
"<Setter Property=\"Template\">" +
" <Setter.Value> " +
"<ControlTemplate TargetType = \"Button\">" +
"<Border>" +
"<Border.Style>" +
"<Style TargetType=\"{x:Type Border}\">" +
"<Style.Triggers>" +
"<Trigger Property = \"IsMouseOver\" Value=\"True\">" +
"<Setter Property = \"Background\" Value=\"Transparent\"/>" +
"</Trigger>" +
"</Style.Triggers>" +
"</Style>" +
"</Border.Style>" +
"<Grid Background = \"Transparent\">" +
"<ContentPresenter></ContentPresenter>" +
"</Grid>" +
"</Border>" +
"</ControlTemplate>" +
"</Setter.Value>" +
"</Setter>" +
"</Style>";
Add the following code at the top of the WPF method
var stringReader = new StringReader(xamlText);
var xmlReader = XmlReader.Create(stringReader);
var style = XamlReader.Load(xmlReader) as Style;
Next, just do like so:
leftButton.Style = style;
and
rightButton.Style = style;
I had once an extreme case I had to use such method so I pulled it out.
In general, when you create a library, you define its setting set. These settings are specified through methods. However, you need to phrase your problem in the most general way you can. You need a way to set the pattern expected (the right order), a history member to check whether the pattern is being matched and styling/class attributes. You can store the settings into an ini file or an XML or into the memory of your program or under the hood inside the DLL.

WPF/XAML How to specify the assembly to load the resource from?

I'm working on a WPF class library, not an application. Here is an example of a Label I'm making in c# and I'd like to "style" it using XAML.
private void CreateElement(int i)
{
UIElementOut[i] = new Label();
var uiElement = (Label)UIElementOut[i];
uiElement.HorizontalAlignment = HorizontalAlignment.Center;
uiElement.VerticalAlignment = VerticalAlignment.Center;
uiElement.FontFamily = new FontFamily(FFontInput[i]);
uiElement.FontSize = Convert.ToDouble(FontSizeIn[i]);
uiElement.Content = TextIn[i];
Brush BgBrushColor = new SolidColorBrush(RGBAToMediaColor(FBgCol[i]));
Brush FgBrushColor = new SolidColorBrush(RGBAToMediaColor(FFgCol[i]));
uiElement.Background = BgBrushColor;
uiElement.Foreground = FgBrushColor;
Uri uri = new Uri("Styles/LabelStyle.xaml", UriKind.Relative);
StreamResourceInfo info = Application.GetContentStream(uri);
System.Windows.Markup.XamlReader reader = new System.Windows.Markup.XamlReader();
ResourceDictionary myResourceDictionary = (ResourceDictionary)reader.LoadAsync(info.Stream);
Application.Current.Resources.MergedDictionaries.Add(myResourceDictionary);
Style myLabelStyle = myResourceDictionary["LabelStyle"] as Style;
uiElement.Style = myLabelStyle;
}
For this I have ressourcedictionnary containing my LabelStyle, everything is compiling without problem.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1">
<Style x:Key="LabelStyle" TargetType="{x:Type Label}">
<Setter Property="Height" Value="53" />
<Setter Property="Width" Value="130" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="Margin" Value="99,71,0,0" />
<Setter Property="VerticalAlignment" Value= "Top" />
<Setter Property="Foreground" Value="#FFE75959" />
<Setter Property="FontFamily" Value="Calibri" />
<Setter Property="FontSize" Value="40" />
</Style>
but when I use my dll later on, the style is not applied and I have this error message :
ERR : Assembly.GetEntryAssembly() returns null. Set the Application.ResourceAssembly property or use the pack://application:,,,/assemblyname;component/ syntax to specify the assembly to load the resource from.
here is my actual App.xaml with build action setup to page :
<Application x:Class="WpfApplication1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication1"
StartupUri="MainWindow.xaml">
<Application.Resources>
</Application.Resources>
How to specify the assembly to load the resource from ?
I'm fairly new to WPF and I'm stuck on this problem, thanks in advance.
EDIT 1 :
I tried as my Assembly Name is WpfApplication1 (see here http://postimg.org/image/ksyj9xi5p/)
ResourceDictionary myResourceDictionary = Application.LoadComponent(new Uri("/WpfApplication1;component/Styles/LabelStyle.xaml", UriKind.RelativeOrAbsolute)) as ResourceDictionary;
instead of
ResourceDictionary myResourceDictionary = (ResourceDictionary)reader.LoadAsync(info.Stream);
and get the same error.
Did you try to replace your
Uri uri = new Uri("Styles/LabelStyle.xaml", UriKind.Relative);
by the suggestion that is indicated in you error, that is using the "Pack" syntax ?
pack://application:,,,/assemblyname;component/
Given the information you provided
Uri uri = new Uri("pack://application:,,,/WpfApplication1;component/Styles/LabelStyle.xaml", UriKind.Relative);
This might help you
ResourceDictionary myResourceDictionary = Application.LoadComponent(new Uri("/assemblyname;component/Styles/LabelStyle.xaml", UriKind.RelativeOrAbsolute)) as ResourceDictionary;
You can then find resources within it in the usual manner, e.g., myDictionary["LabelStyle"]
You can set the property: System.Windows.Application.ResourceAssembly as suggested in the error message. Read carefully the documentation. Note that the property can only be set once.

Remove usercontrol resources

I change the style of my user control by changing resource dictionaries. In other words I have:
Dictionary1.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Grid">
<Setter Property="Background" Value="Green"></Setter>
</Style>
<SolidColorBrush x:Key="Foo" Color="Blue"></SolidColorBrush>
</ResourceDictionary>
Dictionary2.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="Grid">
<Setter Property="Background" Value="Black"></Setter>
</Style>
<SolidColorBrush x:Key="Foo" Color="Orange"></SolidColorBrush>
</ResourceDictionary>
UserControl1:
<UserControl x:Class="WpfApplication1.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
mc:Ignorable="d" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" d:DesignHeight="103" d:DesignWidth="101">
<Grid >
<Ellipse Fill="{DynamicResource Foo}" />
</Grid>
</UserControl>
Code Behind
namespace WpfApplication1
{
using System; using System.Windows; using System.Windows.Controls;
public partial class UserControl1 : UserControl
{
public enum ControlTheme
{
Theme1 , Theme2
}
public UserControl1 ( )
{
InitializeComponent( );
}
public void ChangeTheme ( ControlTheme theme )
{
Resources.MergedDictionaries.Clear( );
int dic = 2;
if ( theme == ControlTheme.Theme1 )
dic = 1;
ResourceDictionary rd = new ResourceDictionary( );
rd.Source = new Uri( #"pack://application:,,,/WpfApplication1;component/Dictionary" + dic + ".xaml" );
Resources.MergedDictionaries.Add( rd );
}
}
}
Now I am able to change themes dynamically by calling the method: ChangeTheme
The problem that I have now is that if I place:
<UserControl.Resources>
<ResourceDictionary Source="Dictionary1.xaml" ></ResourceDictionary>
</UserControl.Resources>
on UserControl1 The method ChangeTheme no longer works. I am looking for a method that does something like:
//PseudoCode
var itemToRemove = this.UserControlResources.resources.where(x=> x.isDictionary==true);
this.UserControlResources.Remove(itemToRemove);
You are setting your a Dictionary in the Xaml without any MergedDictionary's so when you create your Merged Dictinarys they are being overridden by the base Dictionary. You can try one of two things.
The first being is to Create a MergedDictionary in your UserControls Xaml. This will work without changing your CodeBehind.
i.e.
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
The second would be to assign your newly created ResourceDictionary to the UserControls Resources thus overwriting the pre-existing ResourceDictionary. This will work without changing your Xaml.
i.e.
public void ChangeTheme(ControlTheme theme)
{
int dic = 2;
if (theme == ControlTheme.Theme1)
dic = 1;
ResourceDictionary rd = new ResourceDictionary();
rd.Source = new Uri(#"pack://application:,,,/WpfApplication1;component/Dictionary" + dic + ".xaml");
this.Resources.Clear();
this.Resources = rd;
}

WPF Theming and dynamic controls

I am trying to add control to ContentPresenter on then run, but control I've added does not apply theme.
Theres is code with reference to theme in xaml file:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/PocGraphDataTemplates.xaml" />
</ResourceDictionary.MergedDictionaries>
Also I've tried to set style in code behind, does not work:
this.graphLayout.Content = analyzerViewModel.AnalyzedLayout = new PocGraphLayout()
{
LayoutAlgorithmType = "FR"
};
ResourceDictionary rd = new ResourceDictionary();
rd.Source = new Uri("Resources/PocGraphDataTemplates.xaml", UriKind.Relative);
analyzerViewModel.AnalyzedLayout.Style = new Style(typeof(PocGraphLayout));
analyzerViewModel.AnalyzedLayout.Style.Resources.MergedDictionaries.Add(rd);
When control was static everything worked fine:
<ViewModel:PocGraphLayout x:Name="graphLayout"
Graph="{Binding Path=Graph}"
LayoutAlgorithmType="{Binding Path=LayoutAlgorithmType}"
Sample:LayoutManager.ManagedLayout="True"
OverlapRemovalAlgorithmType="FSA"
HighlightAlgorithmType="Simple" />
Any ideas?
PS. I am newbie in wpf.
just see the style applied are using DynamicResource instead of StaticResource

Categories

Resources