Passing through StaticResource style defined in XAML in C# codebehind - c#

I am building a WPF application. XAML used for front end and C# for code behind
I have the following section of code that generates my XAML for me dynamically.
if (station_item.Checker_Setup.First().Checker_Log.OrderByDescending(log => log.date).First().Status.status_key == 2)
{
Path path = new Path();
path.Data = new RectangleGeometry(new Rect(0, 0, 19, 21), 3, 3);
path.Style = "{StaticResource statusIndicatorRed}";
TextBlock block = new TextBlock();
block.Text = station_item.station_name;
WrapBox.Children.Add(path);
WrapBox.Children.Add(block);
}
However where I have
path.Style = "{StaticResource statusIndicatorRed}";
I get the following error
Cannot implicitly convert type String to System.Windows.Style
The style is defined in my MainWindow.xaml as follows
<Style x:Key="statusIndicatorRed" TargetType="Path">
<Setter Property="Fill" Value="#B2203D" />
<Setter Property="Width" Value="19px" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="ToolTipService.ShowDuration" Value="30000" />
<Setter Property="Cursor" Value="Help" />
</Style>
How would I pass this style through in my code behind? Is this even a good way to do things?

This is what I did to fix the issue:
I created a new ResourceDictionary named Styles.xaml
In my App.xaml I referenced the resource as follows
<Application.Resources>
<ResourceDictionary Source="Styles.xaml" />
</Application.Resources>
In my codebehind I called the resource as follows
path.Style = (Style)App.Current.Resources["statusIndicatorRed"];

Related

Programmatically added Column to Datagrid keeps blue background style when selected

I have a Datagrid on my Window with 2 Columns added in XAML and 1 Column added in the code behind. I managed to remove the blue color from the first 2 columns by adding a DataGridCell Style with the background set to null.
But in the code behind I cannot get it working.
This is the Window.xaml
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:System="clr-namespace:System;assembly=mscorlib" x:Class="WpfApp1.MainWindow"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{x:Null}" />
<Setter Property="BorderBrush" Value="{x:Null}" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<DataGrid x:Name="CandidatesEpisodesMatrix"
ColumnWidth="*"
AutoGenerateColumns="False" CanUserAddRows="False" HeadersVisibility="Column" SnapsToDevicePixels="True" SelectionUnit="Cell" RowDetailsVisibilityMode="Collapsed" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Name}" />
<DataGridCheckBoxColumn/>
<!-- Other columns are added dynamically -->
</DataGrid.Columns>
<System:Object/>
<System:Object/>
</DataGrid>
</Grid>
</Window>
And this is my code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
AddMatrixColumn();
}
private void AddMatrixColumn()
{
var factory = new FrameworkElementFactory(typeof(CheckBox));
var columnCellTemplate = new DataTemplate(typeof(CheckBox));
columnCellTemplate.VisualTree = factory;
var style = new Style();
style.Setters.Add(new Setter(HorizontalAlignmentProperty, HorizontalAlignment.Stretch));
style.Triggers.Add(new DataTrigger
{
Binding = new Binding("IsSelected"),
Value = true,
Setters =
{
new Setter(BackgroundProperty, Brushes.BlueViolet),
new Setter(BorderBrushProperty, Brushes.Aqua),
}
});
var headerStyle = new Style();
headerStyle.Setters.Add(new Setter(HorizontalAlignmentProperty, HorizontalAlignment.Center));
var column = new DataGridTemplateColumn();
column.Header = "episode.Name";
column.HeaderStyle = headerStyle;
column.CellTemplate = columnCellTemplate;
column.CellStyle = style;
CandidatesEpisodesMatrix.Columns.Add(column);
}
}
I had hoped to change the background color by adding a Trigger to the style, but it doesn't work. What am I doing wrong?
Found it! I was almost there with the Trigger. Not a DataTrigger, but a normal Trigger should be used. This is what the trigger should be:
style.Triggers.Add(new Trigger
{
Property = DataGridCell.IsSelectedProperty,
Value = true,
Setters =
{
new Setter(BackgroundProperty, Brushes.Transparent), // Or whatever color
new Setter(BorderBrushProperty, Brushes.Transparent),
}
});

Resource style xaml to c#

I have style resource in my button to make it rounded, and i want to create it using c#(code behind), how do i do it?
<Button.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="5"/>
</Style>
</Button.Resources>
Create the Style:
Style style = new Style() { TargetType = typeof(Border) };
style.Setters.Add(new Setter() { Property = Border.CornerRadiusProperty, Value = new CornerRadius(5) });
style.Seal();
Add it to the Button:
button.Resources.Add(typeof(Border), style);
XAML:
<Button x:Name="button" Content="..." />

wpf - add setter from code

I try to add button from code and add triggers and setters to it. I wolud like to create button like this:
<Button Height="25" Width="100" Name="TestColorButton" Margin="10, 5, 0, 0">
<TextBlock Text="{Binding Text, ElementName=ColorTextBox}"></TextBlock>
<Button.Style>
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="{Binding Fill, ElementName=NormalRectangle}"/>
<Setter Property="Template"> <!-- I need this setter -->
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{Binding Fill, ElementName=MouseOverRectangle}"/>
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="{Binding Fill, ElementName=ClickRectangle}"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
I found how to do everything except one setter. I checked it by comment. I tried many times and I got only something like this:
ContentPresenter contentPresenter = new ContentPresenter
{
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center
};
Border border = new Border();
var binding = new Binding("Background");
binding.RelativeSource = new RelativeSource(RelativeSourceMode.Self);
BindingOperations.SetBinding(border, BackgroundProperty, binding);
border.Child = contentPresenter;
ControlTemplate controlTemplate = new ControlTemplate(typeof (Button));
Setter templateSetter = new Setter(TemplateProperty, controlTemplate);
style.Setters.Add(templateSetter);
I know it can not work, but I don't know how to do it differently.
Template can be added by code easily. here an example with a label.
// create the label with some context
var lbl = new Label() { DataContext = new ImageData(imagePath) };
// control template i want to put on the label
ControlTemplate labelLayout = new ControlTemplate();
// will be my main container that will be by template later on
FrameworkElementFactory grdContainer = new FrameworkElementFactory(typeof(Grid));
grdContainer.Name = "myContainer";
// another element but ill put it in the grid (template)
FrameworkElementFactory myImage = new FrameworkElementFactory(typeof(Image));
// some bindings and setters
myImage.SetBinding(Image.SourceProperty, new Binding("ImagePath"));
myImage.SetValue(Image.HorizontalAlignmentProperty, HorizontalAlignment.Center);
myImage.SetValue(Image.HeightProperty, height);
myImage.SetValue(Image.WidthProperty, double.NaN);
myImage.SetValue(Image.SnapsToDevicePixelsProperty, true);
// add the image to the grid
grdContainer.AppendChild(myImage);
// set the visual layout of the template to be the grid (main container)
labelLayout.VisualTree = grdContainer;
// set the template (line you are looking for mostly)
lbl.Template = labelLayout;

Set StaticResource style of a control dynamically in the code

Let's say, I've got something like this (in MainPage.xaml):
<Page.Resources>
<Style TargetType="TextBlock" x:Key="TextBlockStyle">
<Setter Property="FontFamily" Value="Segoe UI Light" />
<Setter Property="Background" Value="Navy" />
</Style>
</Page.Resources>
Then, I would like to apply that StaticResource style to my dynamic created TextBlock (file MainPage.xaml.cs).
Is there any possibility to do this instead of doing something like this:
myTextBlock.FontFamily = new FontFamily("Segoe UI Light");
myTextBlock.Background = new SolidColorBrush(Color.FromArgb(255,0,0,128));
It has been more than 4 years now since this question was asked, but I want to post an answer just to share my findings.
For example if there is a Style BlueButton described in Application resource in App.xaml (Xamarin Cross-Platform App development), it can be used as follows
<?xml version="1.0" encoding="utf-8" ?><Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="SharedUi.App">
<Application.Resources>
<ResourceDictionary>
<Style x:Key="BlueButton" TargetType="Button">
<Setter Property="TextColor" Value="White" />
<Setter Property="FontSize" Value="20" />
<Setter Property="BackgroundColor" Value="Blue"/>
<Setter Property="HeightRequest" Value="70"/>
<Setter Property="FontAttributes" Value="Bold"/>
</Style>
</ResourceDictionary>
</Application.Resources></Application>
Then in the code behind
Button newButton1 = new Button
{
Text = "Hello",
WidthRequest = (double)15.0,
Style = (Style)Application.Current.Resources["BlueButton"]
};
You can set, Something like this,
TextBlock myTextBlock= new TextBlock ()
{
FontFamily = new FontFamily("Segoe UI Light");
Style = Resources["TextBlockStyle"] as Style,
};
You can use this:
Style textBlockStyle;
try
{
textBlockStyle = FindResource("TextBlockStyle") as Style;
}
catch(Exception ex)
{
// exception handling
}
if(textBlockStyle != null)
{
myTextBlock.Style = textBlockStyle;
}
or TryFindResource approach:
myTextBlock.Style = (Style)TryFindResource("TextBlockStyle");

Design time check of markup extension arguments in WPF designer

I've written a Markup extension for WPF that allows me to do
<!-- Generic Styles -->
<Style x:Key="bold" TargetType="Label">
<Setter Property="FontWeight" Value="ExtraBold" />
</Style>
<Style x:Key="italic" TargetType="Label">
<Setter Property="FontStyle" Value="Italic" />
</Style>
<Style x:Key="gridHeader" TargetType="Label"
BasedOn="{WPF:CombiStyle bold italic }" >
It is a very usefull extension and it works great at runtime. However
at design time I can't see the styles applied or that if
I mistype bold and italic they might not be found
as StaticResources.
Any hacks I can do to get this working?
The source code for the extension is
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Markup;
namespace MarkupExtensions
{
[MarkupExtensionReturnType(typeof(Style))]
public class CombiStyleExtension : MarkupExtension
{
private string[] MergeStyleProviders { get; set; }
public CombiStyleExtension(string s0)
{
MergeStyleProviders = s0.Split(new[]{' '});
}
public override object ProvideValue(IServiceProvider
serviceProvider)
{
return MergeStyleProviders
.Select(x => StringToStyle(serviceProvider, x))
.Aggregate(new Style(), RecursivelyMergeStyles);
}
private static Style StringToStyle(IServiceProvider serviceProvider, string x)
{
var style = new StaticResourceExtension() { ResourceKey = x }.ProvideValue(serviceProvider) as Style;
if (style==null)
{
throw new ArgumentException("Argument could not be converted to a style");
}
return style;
}
private static Style RecursivelyMergeStyles(Style accumulator,
Style next)
{
if (next.BasedOn != null)
{
RecursivelyMergeStyles(accumulator, next.BasedOn);
}
MergeStyle(accumulator, next);
return accumulator;
}
private static void MergeStyle(Style targetStyle, Style sourceStyle)
{
targetStyle.TargetType = sourceStyle.TargetType;
// Merge the Setters...
foreach (var setter in sourceStyle.Setters)
targetStyle.Setters.Add(setter);
// Merge the Triggers...
foreach (var trigger in sourceStyle.Triggers)
targetStyle.Triggers.Add(trigger);
}
}
}
Update: added screenshots for VS2012 (works fine) and Blend for VS2012 (works partially: base styles on BasedOn-styles are not picked up correctly for some reason).
Also checked it in VS2013 Preview and Blend for VS2013 Preview - there it works partially and exactly the same as in Blend for VS2012. Hope they fix this in release.
The thing is that Visual Studio designer very likes when object that you try to describe in XAML has public default constructor that it use to instanciate design-time instance of that object.
I've updated a bit your CombiStyleExtension.cs class to take this into account and Visual Studio 2010 designer like it now. However Blend 4 designer still don't, sorry.
Take a look:
using System;
using System.Linq;
using System.Windows;
using System.Windows.Markup;
namespace WpfApplication7
{
[MarkupExtensionReturnType(typeof(Style))]
public class CombiStyleExtension : MarkupExtension
{
/// <summary>
/// Set space-separated style names i.e. "size16 grey verdana".
/// </summary>
public string Names { private get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
return Names.Split(new[] { ' ' })
.Select(x => Application.Current.TryFindResource(x)
as Style)
.Aggregate(new Style(), RecursivelyMergeStyles);
}
private static Style RecursivelyMergeStyles(Style accumulator,
Style next)
{
if(accumulator == null || next == null)
return accumulator;
if(next.BasedOn != null)
RecursivelyMergeStyles(accumulator, next.BasedOn);
MergeStyle(accumulator, next);
return accumulator;
}
private static void MergeStyle(Style targetStyle, Style sourceStyle)
{
if(targetStyle == null || sourceStyle == null)
{
return;
}
targetStyle.TargetType = sourceStyle.TargetType;
// Merge the Setters...
foreach(var setter in sourceStyle.Setters)
targetStyle.Setters.Add(setter);
// Merge the Triggers...
foreach(var trigger in sourceStyle.Triggers)
targetStyle.Triggers.Add(trigger);
}
}
}
Usage of this markup extension changed also just a bit. How it was:
BasedOn="{WPF:CombiStyle bold italic }"
and how it now:
BasedOn="{WPF:CombiStyle Names='bold italic'}"
And just to save some time for you here is a bit of xaml to copy-paste-run-and-watch:
MainWindow.xaml:
<Window x:Class="WpfApplication7.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WPF="clr-namespace:WpfApplication7"
Title="MainWindow" Height="350" Width="569">
<Window.Resources>
<!-- Did not managed to make the type-level style work -->
<!-- from app.xaml, so put it here. Just in case. -->
<Style TargetType="{x:Type Label}"
BasedOn="{WPF:CombiStyle Names='size16 grey verdana'}" />
</Window.Resources>
<StackPanel>
<Label Content="Type-level: size16 + grey + verdana" />
<Label Content="'h1': size24 + royalBlue" Style="{DynamicResource h1}" />
<Label Content="'warning': size24 + yellow + bold + shadow"
Style="{DynamicResource warning}" />
<Label Content="Inline: size12 + italic"
Style="{WPF:CombiStyle Names='size12 italic'}" />
<Label Content="Inline: size16 + bold + italic + red"
Style="{WPF:CombiStyle Names='size16 bold italic red'}" />
<Label Content="Inline: size24 + green"
Style="{WPF:CombiStyle Names='size24 green'}" />
</StackPanel>
</Window>
App.xaml:
<Application x:Class="WpfApplication7.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:WPF="clr-namespace:WpfApplication7"
StartupUri="MainWindow.xaml">
<Application.Resources>
<!-- Sizes -->
<Style x:Key="size12" TargetType="Label">
<Setter Property="FontSize" Value="12" />
</Style>
<Style x:Key="size16" TargetType="Label">
<Setter Property="FontSize" Value="16" />
</Style>
<Style x:Key="size24" TargetType="Label">
<Setter Property="FontSize" Value="24" />
</Style>
<!-- Bold/Italic -->
<Style x:Key="bold" TargetType="Label">
<Setter Property="FontWeight" Value="ExtraBold" />
</Style>
<Style x:Key="italic" TargetType="Label">
<Setter Property="FontStyle" Value="Italic" />
</Style>
<!-- Colors -->
<Style x:Key="grey" TargetType="Label">
<Setter Property="Foreground" Value="#333333" />
</Style>
<Style x:Key="royalBlue" TargetType="Label">
<Setter Property="Foreground" Value="RoyalBlue" />
</Style>
<Style x:Key="green" TargetType="Label">
<Setter Property="Foreground" Value="Green" />
</Style>
<Style x:Key="yellow" TargetType="Label">
<Setter Property="Foreground" Value="Yellow" />
</Style>
<Style x:Key="red" TargetType="Label">
<Setter Property="Foreground" Value="#D00000" />
</Style>
<!-- Fonts -->
<Style x:Key="verdana" TargetType="Label">
<Setter Property="FontFamily" Value="Verdana" />
</Style>
<!-- Effects -->
<Style x:Key="shadow" TargetType="Label">
<Setter Property="Effect">
<Setter.Value>
<DropShadowEffect ShadowDepth="0" />
</Setter.Value>
</Setter>
</Style>
<!-- Predefined Combinations -->
<Style x:Key="h1" TargetType="{x:Type Label}"
BasedOn="{WPF:CombiStyle Names='size24 royalBlue'}" />
<Style x:Key="warning" TargetType="{x:Type Label}"
BasedOn="{WPF:CombiStyle Names='size24 yellow bold shadow'}">
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="Yellow" />
</Style>
</Application.Resources>
</Application>
Enjoy ;)
No Unfortunately, My understanding of the problem is this,
Design time resolution seems to work in WPF because of dependency properties. Since MarkupExtension is not derived from dependency object your extension is never evaluated at design time. As to weather this was an oversight or intentional is debatable.
There may be another way to solve this though which would be slightly different. Create a new attached property called MergeStyles. In that property you could specify the style names you wish to merge and apply. When the value is changed simply update the style of the attachee using your code above. You could use the position each style to be merged comes in to determine hierarchy.
It's not exactly what you want but it may get you half way there. The upshot to this is that you can bind to it though.

Categories

Resources