Transform XAML to C# - c#

I did not find it on the Internet
Help rewrite this code to C#
<Style x:Key="GroupHeaderStyle" TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander x:Name="exp" IsExpanded="True" >
<Expander.Header>
<TextBlock/>
</Expander.Header>
<ItemsPresenter/>
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
My code
An error in the code takes off.
Style GroupStyle = new Style() { TargetType = typeof(GroupItem) };
Setter setter = new Setter { Property = Control.TemplateProperty };
ControlTemplate template = new ControlTemplate { TargetType = typeof(GroupItem) };
var expander = new FrameworkElementFactory(typeof(Expander));
expander.SetValue(Expander.IsExpandedProperty, false);
expander.SetValue(Expander.HeaderProperty, new TextBlock { }); // Error: System.NotSupportedException
expander.SetValue(Expander.ContentProperty, new ItemsPresenter());
template.VisualTree = expander;
setter.Value = template;
GroupStyle.Setters.Add(setter);
Doing datagrid grouping

As #arconaut has said, first line should use typeof. So first tree lines are:
Style GroupStyle = new Style() { TargetType = typeof(GroupItem) };
Setter setter = new Setter { Property = Control.TemplateProperty };
ControlTemplate template = new ControlTemplate { TargetType = typeof(GroupItem) };
Then I believe IsExpandedProperty should be set to True based on the above XAML.
expander.SetValue(Expander.IsExpandedProperty, true);
Finally you need to pass FrameworkElementFactory as argument for SetValue() in the last two lines.
expander.SetValue(Expander.HeaderProperty,
new FrameworkElementFactory(typeof(TextBlock)));
expander.SetValue(Expander.ContentProperty,
new FrameworkElementFactory(typeof(ItemsPresenter)));

Try replacing your first line with this:
Style GroupStyle = new Style() { TargetType = typeof(GroupItem) };
The x:Type markup extension has a similar function to the typeof() operator in C#
https://learn.microsoft.com/en-us/dotnet/desktop/xaml-services/xtype-markup-extension

Related

How to add a Style to UserControl Resources in C# programmatically?

I'm trying to do this XAML:
<UserControl.Resources>
<Style TargetType="Label">
<Setter Property="Foreground" Value="Blue"/>
</Style>
</UserControl.Resources>
in C# code.
Here's my attempt in the UserControl Constructor:
InitializeComponent();
string labelForegroundColor = "Blue";
string labelXAMLStyle = #"<Style xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' TargetType=""Label"">
<Setter Property=""Foreground"" Value=""{LabelForegroundColor}""/>
</Style>";
labelXAMLStyle = labelXAMLStyle.Replace("{LabelForegroundColor}", labelForegroundColor);
StringReader mainLabelStyleXAMLStringReader = new StringReader(labelXAMLStyle);
XmlReader mainLabelStyleXAMLXMLReader = XmlReader.Create(mainLabelStyleXAMLStringReader);
Style mainLabelStyle = (Style)XamlReader.Load(mainLabelStyleXAMLXMLReader);
this.Resources.Add("LabelStyle", mainLabelStyle);
When I have the XAML in my UserControl it obviously works, but when I remove the XAML and add the code in my UserControl Constructor; it doesn't.
Where am I going wrong?
Do I have to add some sort of Resource Dictionary?
How can I get it right to set the style of all Label's in my one specific UserControl?
You can try to create a style programatically and then add it to the resources.
Style style = new Style(typeof(Label));
style.Setters.Add(new Setter(Label.ForegroundProperty, Brushes.Blue));
Resources[typeof(Label)] = gridStyle;

How to fill area between 2 data series with color using WPF Toolkit Data Visualization Controls?

So far I've successfully created a data series using WPF Toolkit Data Visualization Controls, like this:
As you can see, there is 3 data series (top, middle, bottom). I'm using AreaSeries for each and apply this style:
<Style x:Key="TopAreaSeriesStyle" TargetType="{x:Type chart:AreaSeries}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type chart:AreaSeries}">
<Canvas x:Name="PlotArea">
<Path StrokeThickness="1.5" Stroke="Black" Fill="Yellow" Opacity=".4">
<Path.Style>
<Style TargetType="{x:Type Path}">
<Setter Property="Data">
<Setter.Value>
<MultiBinding Converter="{StaticResource geoExclusionConverter}">
<Binding ElementName="TopAreaSeries" Path="Geometry"/>
<Binding ElementName="MiddleAreaSeries" Path="Geometry"/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</Path.Style>
</Path>
</Canvas>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="MiddleAreaSeriesStyle" TargetType="{x:Type chart:AreaSeries}"...>
<Style x:Key="BottomAreaSeriesStyle" TargetType="{x:Type chart:AreaSeries}"...>
I have to create this style so that colored area is rendered only between area series. For each area series, I must apply this style, so there is 3 styles that differ in where area of clipping occurs. For example, Area of the top AreaSeries that collided with area of the middle AreaSeries is clipped and so does the middle AreaSeries to the bottom AreaSeries.
The clipping process is handled by a converter class which consumed 2 parameters defined in MultiBinding tag. First parameter takes the geometry that will be clipped, and second parameter take the geometry that is used to clip.
public class GeometryExclusionConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (values[0] == null && values[1] == null)
return new PathGeometry();
else
{
var geo1 = values[0] as Geometry;
var geo2 = values[1] as Geometry;
return new CombinedGeometry(GeometryCombineMode.Exclude, geo1, geo2);
}
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
By using this method, I can get all I need. But the problem is, there can be more than 7 data series, so that I must create a lot of styles that differs only in parameters that get passed. And it makes a lot of dependency (style and converter). It would be nice if all this stuff can be handled in one place.
I've searched on how to create a style with parameters so I can somehow apply a style by passing parameters on it, but none of them seemed working.
UPDATE
Using advice proposed by #AnjumSKhan, I am able to simplify the styling, by putting those code on OnInitialized event.
public class ClippedAreaSeries : AreaSeries
{
public string ClippedArea
{
get { return (string)GetValue(ClippedAreaProperty); }
set { SetValue(ClippedAreaProperty, value); }
}
public static readonly DependencyProperty ClippedAreaProperty =
DependencyProperty.Register("ClippedArea", typeof(string), typeof(ClippedAreaSeries), new PropertyMetadata(null));
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
string templateXml =
#"
<ControlTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
>
<Canvas x:Name='PlotArea'>
<Path StrokeThickness='1.5' Stroke='Black' Fill='Yellow' Opacity='.25'>
<Path.Style>
<Style TargetType='{x:Type Path}'>
<Setter Property='Data'>
<Setter.Value>
<MultiBinding Converter='{StaticResource geoExclusionConverter}'>
<Binding ElementName='{0}' Path='Geometry'/>
<Binding ElementName='{1}' Path='Geometry'/>
</MultiBinding>
</Setter.Value>
</Setter>
</Style>
</Path.Style>
</Path>
</Canvas>
</ControlTemplate>
";
templateXml = templateXml.Replace("{0}", this.Name).Replace("{1}", this.ClippedArea);
//so on...
}
}
Notice that, it still depends on a converter class that must be defined in Application.Resources. It would be nice if this control doesn't depend on them, so there is no need to define the converter as resources. Any ideas are appreciated.
You can easily set the ControlTemplate using code by using System.Windows.Markup.XamlReader.Load(System.IO.Stream stream) method.
Below code shows how to change the Control template of Button :
string templateXml =
#"<ControlTemplate xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'>
<Canvas Background='Purple'>
<TextBlock Text='custom btn'/>
</Canvas>
</ControlTemplate>";
ControlTemplate template;
using (var stringReader = new System.IO.StringReader(templateXml))
using (XmlReader xmlReader = XmlReader.Create(stringReader))
{
template = (ControlTemplate)XamlReader.Load(xmlReader);
}
Btn1.Template = template;
You can use this approach for your scenario, and replace necessary values in the string.
But now if we try to apply converters, the above approach creates problems. So, we now go for pure-code approach, and construct all elements ourselves. This approach involves using FrameworkElementFactory class. For example, if we want to change ControlTemplate of Button and apply a converter to Text property of TextBlock like below :
<ControlTemplate TargetType="Button">
<Canvas Background='Red'>
<TextBlock>
<TextBlock.Text>
<Binding Path='Content' RelativeSource="{RelativeSource Mode=TemplatedParent}" >
<Binding.Converter>
<converter:ContentConverter/>
</Binding.Converter>
</Binding>
</TextBlock.Text>
</TextBlock>
</Canvas>
</ControlTemplate>
We will write the corresponding code as follows :
FrameworkElementFactory canvas = new FrameworkElementFactory(typeof(Canvas));
canvas.SetValue(Canvas.BackgroundProperty, new SolidColorBrush(Colors.Red));
FrameworkElementFactory tb = new FrameworkElementFactory(typeof(TextBlock));
Binding binding = new Binding("Content");
binding.RelativeSource = new RelativeSource(RelativeSourceMode.TemplatedParent);
binding.Converter = new Converters.ContentConverter();
tb.SetBinding(TextBlock.TextProperty, binding);
canvas.AppendChild(tb);
ControlTemplate ct = new ControlTemplate(typeof(Button));
ct.VisualTree = canvas;
Btn1.Template = ct;
Based on above approach, the control-template style which you have posted would be written as :
FrameworkElementFactory canvas = new FrameworkElementFactory(typeof(Canvas));
canvas.Name = "PlotArea";
FrameworkElementFactory path = new FrameworkElementFactory(typeof(Path));
{
path.SetValue(Path.StrokeThicknessProperty, 1.5);
path.SetValue(Path.StrokeProperty, new SolidColorBrush(Colors.Green));
path.SetValue(Path.OpacityProperty, 0.25);
MultiBinding binding = new MultiBinding();
// create your converter properly below
binding.Converter = new Converters.GeoConverter();
Binding bindingItem1 = new Binding();
bindingItem1.ElementName = "AreaPlus1SD";
bindingItem1.Path = new PropertyPath("Geometry");
Binding bindingItem2 = new Binding();
bindingItem2.ElementName = "AreaMedian";
bindingItem2.Path = new PropertyPath("Geometry");
binding.Bindings.Add(bindingItem1);
binding.Bindings.Add(bindingItem2);
path.SetBinding(Path.DataProperty, binding);
}
canvas.AppendChild(path);
ControlTemplate ct = new ControlTemplate(typeof(ChartingToolkit.AreaSeries));
ct.VisualTree = canvas;
AreaSeries1.Template = ct;
You can do it like this:
var customStyle = new Style(typeof (Button));
customStyle.Setters.Add(new Setter{Property = Button.BackgroundProperty, Value = new SolidColorBrush(){Color = Colors.Red}});
ButtonTest.Style = customStyle;

How to change the window.resource style setter?

<Window.Resources >
<Style x:Name="stylepropery" x:Key="BaseContentControlStyle" TargetType="{x:Type ContentControl}">
<Setter Property="Foreground" Value="{DynamicResource MyFillBrush}" />
</Style>
<Style TargetType="{x:Type Label}" BasedOn="{StaticResource BaseContentControlStyle}" />
<Style TargetType="{x:Type CheckBox}" BasedOn="{StaticResource BaseContentControlStyle}" />
</Window.Resources>
which is applying common font color to all the labels and textboxes now i want to change the color of font from code behind but some how its not applying
i just want to change setter propery value
Setter setter = new Setter(ContentControl.ForegroundProperty, dt.Rows[0]["value"]);
Style style = this.FindResource("BaseContentControlStyle") as Style;
style.Setters.Add(setter);
I have used this but not succedd
Try this code this may help you
Style style = new Style(typeof(ContentControl));
style.Setters.Add(new Setter(ContentControl.ForegroundProperty, Brushes.Green));
Resources["BaseContentControlStyle"] = style;
var converter = new System.Windows.Media.BrushConverter();
var brush = (Brush)converter.ConvertFromString(dt.Rows[0]["value"].ToString());
Style st = this.Resources["BaseContentControlStyle"] as Style;
Random r = new Random();
this.Resources["MyFillBrush"] = (new BrushConverter().ConvertFrom(dt.Rows[0]["value"].ToString()));
After lots of googling i have found the solution i am adding this solution for the future refrence users so they don't have to search many things :)

Validating WPF Visual Tree

I am trying to enforce that any new style added should have to meet some standards. Here is a simple example.
<Style TargetType="{x:Type CellValuePresenter}" BasedOn="{StaticResource {x:Type CellValuePresenter}}">
<Setter Property="Tag" Value="1"></Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderThickness="{TemplateBinding BorderThickness}" />
....
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Updated:
I am trying to validate the style from code behindthat if the style has ControlTemplate, its root should be Border with BorderThickness template binded.
Since Visual Tree is not created until the Control is rendered, I am creating the control at runtime based on target type and applying the style. And I even tried the following
Appropriate way to force loading of a WPF Visual
I can see the visual tree in WPF Tree Visualizer, but I cannot navigate fully using LogicalTreeHelper.GetChildren
I am just experimenting with ideas around:
private void ValidateStyle(Style fieldStyle_, Field field_)
{
if (fieldStyle_.TargetType == typeof(CellValuePresenter))
{
StringBuilder sb = new StringBuilder();
CellValuePresenter presenter = new CellValuePresenter();
presenter.Style = fieldStyle_;
if (fieldStyle_.Setters.Count > 0)
{
foreach (Setter setter in fieldStyle_.Setters)
{
if (setter.Property != null && setter.Property.PropertyType == typeof(ControlTemplate))
{
presenter.ApplyTemplate();
ValidateColumnStyle(0, presenter);
}
}
}
}
}
private void ValidateColumnStyle(int depth_, object obj_)
{
Debug.WriteLine(new string(' ', depth_) + obj_);
if (!(obj_ is DependencyObject))
{
return;
}
if (obj_ is UIElement)
{
Viewbox vb = new Viewbox() {Child = obj_ as UIElement};
vb.Measure(new Size(Double.PositiveInfinity, Double.PositiveInfinity));
vb.Arrange(new Rect(vb.DesiredSize));
}
foreach (object child in LogicalTreeHelper.GetChildren(obj_ as DependencyObject))
ValidateColumnStyle(depth_ + 1, child);
}
I am not able to navigate to Border element, any idea how to properly load the control.

ItemsControl: How To Use FindName within ItemsPanelTemplate to access Panel

<Style TargetType="{x:Type local:CustomItemsControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ScrollViewer>
<ItemsPresenter x:Name="PART_Presenter"/>
</ScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel x:Name="PART_StackPanel" IsItemsHost="True"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
Trying to access the StackPanel to set Events when children are changed.
[TemplatePartAttribute(Name = "PART_StackPanel", Type = typeof(StackPanel))]
[TemplatePartAttribute(Name = "PART_Presenter", Type = typeof(ItemsPresenter))]
public class CustomItemsControl: ItemsControl
{
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var presenter = (ItemsPresenter)this.Template.FindName("PART_Presenter", this);
var stackPanel = (StackPanel)this.ItemsPanel.FindName("PART_StackPanel",this);
}
}
Get Exception when I try to locate the StackPanel.
InvalidOperationException:
This operation is valid only on elements that have this template applied.
Please advise if there is a way to find a TemplatePart within an ItemsPanelTemplate. And when should I expect to know when the ItemsPanelTemplate is applied?
Another option is to call .ApplyTemplate() on the ItemsPresenter while still in the ItemControl's OnApplyTemplate method. Then the call to .FindName will succeed.
[TemplatePartAttribute(Name = "PART_StackPanel", Type = typeof(StackPanel))]
[TemplatePartAttribute(Name = "PART_Presenter", Type = typeof(ItemsPresenter))]
public class CustomItemsControl : ItemsControl
{
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
var presenter = (ItemsPresenter)this.Template.FindName("PART_Presenter", this);
presenter.ApplyTemplate();
var stackPanel = (StackPanel)this.ItemsPanel.FindName("PART_StackPanel", presenter);
}
}
Figured out that the Loaded event was the one to wait for on an ItemsPanelTemplate. I am able to find the StackPanel using the TemplatePart Name. Thanks to Rick for suggesting the StackPanel should be found within the Presenter.
protected override void OnInitialized(EventArgs e)
{
base.OnInitialized(e);
this.Loaded += new Accordion_Loaded;
}
void Accordion_Loaded(object sender, RoutedEventArgs e)
{
var presenter = (ItemsPresenter)this.Template.FindName("PART_Presenter", this);
var stackPanel = (StackPanel)this.ItemsPanel.FindName("PART_StackPanel", presenter);
}
The FindName method only finds names in a template that has been expanded and the ItemsPanel is expanded by the ItemsPresenter, not the ItemsControl. In your situation the "PART_StackPanel" will always be the child of the "PART_Presenter" so you can get a reference to it like this:
var stackPanel = (StackPanel)VisualTreeHelper.GetChild(presenter, 0);

Categories