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

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.

Related

How to Convert XAML to c#?

I'm using accordion control.
<dxa:AccordionControl.Resources>
<Style TargetType="dxa:AccordionItem">
<Setter Property="Foreground" Value="Orange"/>
<Style.Triggers>
<Trigger Property="IsMouseOverHeader" Value="True">
<Setter Property="Foreground" Value="black"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="Black"/>
</Trigger>
</Style.Triggers>
</Style>
</dxa:AccordionControl.Resources>
But I don't use it anymore ↑
because I changed the contents of the item dynamically.
Setter setter = new Setter();
Style style2 = new Style();
style2.TargetType = new AccordionItem().GetType();
setter.Property = AccordionItem.ForegroundProperty;
setter.Value = Brushes.Red;
style2.Setters.Add(setter);
ResourceDictionary resourceDictionary = new ResourceDictionary();
resourceDictionary.Add(style2.TargetType, style2);
//Trigger trigger = new Trigger();
//trigger.Property = AccordionItem.IsMouseOverHeaderProperty;
//trigger.Value = true;
accordionControlHistoryMenu.Resources = resourceDictionary;
How Can I express it this xamlcode convert xaml to C# source?
You don't need to store the style in a ResourceDictionary, just assign it directly:
Xaml:
<dxa:AccordionControl.Resources x:Name="myControl">
Code:
myControl.Style = style2;
While this answers your question, it's almost never the correct way to do this. Your styles should be binding to dynamic data that your view model layer is creating.

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>

DataGridHyperlinkColumn text color when highlighted

I have a simple WPF application which displays reddit links in a DataGrid:
Notice however that the link in the DataGridHyperlinkColumn isn't visible when a row is selected, due to the color of the link and the color of the row highlight.
What's a good way to resolve this? Change the link text color? Change the row highlight color?
If possible, please show your suggestion in terms of C# code as opposed to XAML as this application isn't using XAML. Otherwise, a XAML solution is fine; I'll just convert it to C#. :-)
For reference, here's the code used for the Title column:
var event_setter = new EventSetter()
{
Event = Hyperlink.ClickEvent,
Handler = (RoutedEventHandler)((sender, e) =>
{
System.Diagnostics.Process.Start((data_grid.SelectedItem as Link).Url);
})
};
var style = new Style();
style.Setters.Add(event_setter);
var hyperlink_column = new DataGridHyperlinkColumn()
{
Header = "Title",
Binding = new Binding("Title"),
ElementStyle = style,
Width = 600
};
data_grid.Columns.Add(hyperlink_column);
You could add an implicit Hyperlink style to your DataGrid:
const string Xaml = "<Style TargetType=\"Hyperlink\" xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\">" +
"<Style.Triggers>" +
"<DataTrigger Binding=\"{Binding IsSelected, RelativeSource={RelativeSource AncestorType=DataGridCell}}\" Value=\"True\">" +
"<Setter Property=\"Foreground\" Value=\"White\" />" +
"</DataTrigger>" +
"</Style.Triggers>" +
"</Style>";
data_grid.Resources.Add(typeof(Hyperlink), System.Windows.Markup.XamlReader.Parse(Xaml) as Style);
data_grid.Columns.Add(hyperlink_column);
The Selector.IsSelected property of DataGridHyperLink Column can be used and when the selection on particular item changes you can update the style with trigger.
<DataGridHyperlinkColumn.CellStyle>
<Style TargetType="{x:Type Hyperlink}">
<Setter Property="Foreground" Value="Blue"/>
<Style.Triggers>
<Trigger Property="Selector.IsSelected" Value="True">
<Trigger.Setters>
<!--change the value for the property based on your needs-->
<Setter Property="Foreground" Value="Yellow"/>
</Trigger.Setters>
</Trigger>
</Style.Triggers>
</Style>
</DataGridHyperlinkColumn.CellStyle>
Pure XAML solution:
<DataGrid>
<DataGrid.Resources>
<Style TargetType="{x:Type Hyperlink}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=DataGridCell}}"
Value="True">
<DataTrigger.Setters>
<Setter Property="Foreground" Value="Yellow"/>
</DataTrigger.Setters>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridHyperlinkColumn Width="180"
Header="Url"
Binding="{Binding Path=Uri, Mode=OneWay}" />
</DataGrid.Columns>
</DataGrid>
Here's a version of the answer provided by #mm8 converted from XAML to C#:
var data_trigger = new DataTrigger()
{
Binding = new Binding()
{
Path = new PropertyPath("IsSelected"),
RelativeSource = new RelativeSource() { AncestorType = typeof(DataGridCell) }
},
Value = true
};
data_trigger.Setters.Add(new Setter(ForegroundProperty, new SolidColorBrush(Colors.White)));
var style = new Style(typeof(Hyperlink));
style.Triggers.Add(data_trigger);
data_grid.Resources.Add(typeof(Hyperlink), style);
Here's a version of the answer provided by #mm8 converted from XAML to C# which uses some extension methods to avoid intermediate variables:
data_grid.Resources.Add(
typeof(Hyperlink),
new Style(typeof(Hyperlink))
.AddTrigger(
new DataTrigger()
{
Binding = new Binding()
{
Path = new PropertyPath("IsSelected"),
RelativeSource = new RelativeSource() { AncestorType = typeof(DataGridCell) }
},
Value = true
}
.AddSetter(new Setter(ForegroundProperty, new SolidColorBrush(Colors.White)))));

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.

Adding MergedDictionaries to styles via code

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

Categories

Resources