How to get a string from c# code to XAML (WPF) - c#

I am trying to get a string from my C# code over to my XAML, but I can't seem to find a way to do it
My C# code
public string demoColour= "#FFFFFF";
My XAML Code
...
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" TargetName="Bd" Value="{ NOT SURE WHAT GOES HERE :( }"/>
</Trigger>
...

As Clemes said, you should take a look at data binding.
Data binding is one really important thing in WPF.
But here is one Solution, which works fine:
Make a new Class called ViewModel
Add a Property to that Class like public string MyColor { get; set; } = "#FFFFFF";
Set the DataContext in your XAML:
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
Bind your Property to your XAML whereever you want. For Example:
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Background" Value="{Binding MyColor}"/>
</Style>
</Window.Resources>

Related

Binding int variable as value in style

I am trying to find how to bind an int variable as my value in style. Here is my simple xaml style.
<Style TargetType="Button" x:Key="ButtonMenu">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="FontSize" Value="14"/>
<Setter Property="Foreground" Value="White"/>
</Style>
In the fourth row, I wanna change the value from 14 to my variable called SizeVar bud I can't find how to do it. I really tried googling but can't find anything that works for me. I know that I should do it somehow through binding but not exactly how.
I tried using different binding options but couldn't find one that works for me. My English is also not perfect so maybe it is possible that I misunderstood something.
From your description I interpreted the following:
You have a XAML-file where in the Resources you want to contain a style called "ButtonMenu".
The variable that binds to the FontSize is called "SizeVar" and is contained in the DataContext, which is set.
With this, I tested it out and the following works.
In the View (XAML):
...
<Window.DataContext>
<viewmodels:MainViewModel/>
</Window.DataContext>
<Window.Resources>
<Style TargetType="Button" x:Key="ButtonMenu">
<Setter Property="FontSize" Value="{Binding SizeVar}"/>
</Style>
</Window.Resources>
<Grid>
<Button
Content="Test"
Style="{DynamicResource ButtonMenu}"/>
</Grid>
...
In the associated ViewModel (C#):
private int _sizeVar;
public int SizeVar
{
get => _sizeVar;
set
{
_sizeVar = value;
OnPropertyChanged();
}
}
public MainViewModel()
{
SizeVar = 14;
}

Why are root level attributes ignored for custom classes in XAML?

Consider the following simple XAML:
<TextBlock x:Class="MyTextBlock"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Foreground="Red">
</TextBlock>
And the associated C# code:
public partial class MyTextBlock : TextBlock
{
public MyTextBlock()
{
}
}
Why does the Foreground="Red" part not work when I then do <MyTextBlock Text="Foo"/> in my application? I know there are plenty of other ways to do it in code, but I have trouble understanding what is happening exactly here.
I have also tried the following XAML:
<TextBlock x:Class="MyTextBlock"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Red"/>
</Style>
</TextBlock.Style>
</TextBlock>
And finally this one:
<TextBlock x:Class="MyTextBlock"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<TextBlock.Resources>
<Style x:Key="MyTextBlockStyle" TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Red"/>
</Style>
</TextBlock.Resources>
<TextBlock.Style>
<DynamicResource ResourceKey="MyTextBlockStyle"/>
</TextBlock.Style>
</TextBlock>
Again, neither of these appear to apply the style. Why is that? Is there a way to declare some kind of default style for the root element in XAML?
Why does the Foreground="Red" part not work when I then do <MyTextBlock Text="Foo"/> in my application?
It works if you call InitializeComponent():
public partial class MyTextBlock : TextBlock
{
public MyTextBlock()
{
InitializeComponent();
}
}
Local values take precedence over Style setters.

Create A Custom Event That Can Be Triggered From Code Behind

I'm breaking my pick on something I would have thought to be pretty simple. We have an application which uses some advanced color animations on a panel or a border control to attract the users attention when specific conditions occur. Usually those conditions are identified by communicating with an external device and could occur at any time. So I want to be able to define a trigger for a border or stackpanel like I might for it's standard IsEnabled or IsFocused properties but one that I can trigger arbitrarily form the Main Window's code-behind (IsMyArbitraryCondition). I imagine it would look like this:
<Border x:Name="Fancy_Border">
<Border.Style>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="5"/>
<Setter Property="BorderThickness" Value="5"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Style.Triggers>
<Trigger Property="IsMyArbitraryCondition" Value="true">
<Trigger.EnterActions>
<BeginStoryboard x:Name="Board" Storyboard="{StaticResource ComplicatedAnimation}"/>
</Trigger.EnterActions>
</Trigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
Then in the code-behind for the main window:
Fancy_Border.IsMyArbitraryCondition = true;
// ComplicatedAnimation occurs
I've seen attached properties, dependency properties, etc suggested but my level of knowledge about how to us WPF has been insufficient so far to use those examples successfully. It seems like there must be a relatively simple way that I can still define my complex trigger behavior with the convenience of an XAML style but have that style trigger fired by something (anything!) other than the built in properties (IsMouseOver, IsPressed, IsVisible, etc etc).
EDIT (Using UserControl And DataTrigger)
I have a complete and working example provided below. The only remaining issue is that I can't put named components inside the content of my extended border control. The compiler returns:
Cannot set Name attribute value 'Example' on element 'TextBlock'. 'TextBlock' is under the scope of element 'HighlightBorder', which already had a name registered when it was defined in another scope.
This is an issue as I'm not developing using MVVM and seek to directly manipulated some individual controls that are within the HighlightBorder. Any workarounds you know of for that would be very helpful!
HighlightBorder.cs
using System;
using System.Windows;
using System.Windows.Controls;
namespace App
{
public partial class HighlightBorder : Border
{
public HighlightBorder()
{
InitializeComponent();
}
public bool IsActiveHighlight
{
get { return (bool) GetValue(ActiveHighlightProperty); }
set { SetValue(ActiveHighlightProperty, value); }
}
public static readonly DependencyProperty ActiveHighlightProperty =
DependencyProperty.Register("IsActiveHighlight", typeof(bool), typeof(HighlightBorder), new UIPropertyMetadata(null));
}
}
HighlightBorder.xaml
<Border x:Class="App.HighlightBorder"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Border.Style>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="5"/>
<Setter Property="BorderThickness" Value="5"/>
<Setter Property="BorderBrush" Value="Transparent"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsActiveHighlight, RelativeSource={RelativeSource Mode=Self}}" Value="true">
<DataTrigger.EnterActions>
<BeginStoryboard x:Name="EnabledPulse" Storyboard="{DynamicResource PulseAccent}"/>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="EnabledPulse"/>
<BeginStoryboard x:Name="EnabledClear" Storyboard="{DynamicResource ClearAccent}"/>
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
MainWindow.xaml
<local:HighlightBorder x:Name="Grouped_Controls">
<TextBlock> Example Content </TextBlock>
</local:HighlightBorder>
MainWindow.cs
Grouped_Controls.IsActiveHighlight = false; // Or true

Populate WPF combo box items in XAML style based on the state of another control

I think I'm close, but I'm missing something here.
I'm really trying to stretch my XAML knowledge and unclutter my code behind in my WPF projects.
I have a situation where I'd like to populate the items of a combobox based on the state of another control.
I have a style where I'm checking the value of a checkbox:
<Style
x:Key="{x:Type ComboBox}"
TargetType="{x:Type ComboBox}">
<Style.Triggers>
<DataTrigger
Binding="{Binding ElementName=SomeCheckBox, Path=IsChecked}"
Value="True">
Which seems to allow me to trigger on the value of the box.
I'm trying to figure out how to set up my setter now.
It seems that there is no way to access the items property ...
Here is what I've been messing with:
<Setter
Property="Items">
<Setter.Value>
<ComboBoxItem Content="SomeValue" />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
but it's pretty clear that isn't going to work.
Is there a way to do this using the setter?
Do I need to come up with some sort of list or lists that I stow in the window resources and use as an item source?
And how would I make that list?
based on the state of another control
I would do it in the code behind on the VM after binding the check box IsChecked to a property while also binding the ComboBox's to a list which changes based on the value of IsChecked.
Here is a quick example:
public bool IsOperationChecked // Bound to the checkbox
{
get { return _IsOperationChecked; }
set {
_IsOperationChecked= value;
OnPropertyChanged("IsOperationChecked");
Names = (value) ? new List<string>() {"alpha", "beta"} :
new List<string>() {"Bill", "Frank"};
}
}
public List<string> Names // Bound to the Combobox
{
get { return _Names; }
set { _Names = value; OnPropertyChanged("Names"); }
}
Use an array declared in your Resources:
<Window.Resources>
<x:Array Type="sys:String"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
x:Key="MyArray">
<sys:String>One</sys:String>
<sys:String>Two</sys:String>
</x:Array>
</Window.Resources>
...
<Style x:Key="{x:Type ComboBox}"
TargetType="{x:Type ComboBox}">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=SomeCheckBox, Path=IsChecked}" Value="True">
<Setter Property="ItemsSource" Value="{StaticResource MyArray}" />
</DataTrigger>
</Style.Triggers>
</Style>

How to make a WPF resource be recalculated when a trigger's run?

In short: I've got a Style. It uses TemplateBinding a fair bit to make it parametrized instead of repeating myself over and over again. However, when a trigger for that style gets used and a resource gets used in a setter in that trigger, it just doesn't show up! Not even the default value gets shown. Here's a small program that replicates this issue:
TestDictionary.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:lcl="clr-namespace:MyNamespace">
<Style TargetType="Button" x:Key="BtnTest">
<Style.Resources>
<Label Content="{TemplateBinding lcl:TestClass.String}" x:Key="innerLabel"/>
</Style.Resources>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="Content" Value="{DynamicResource innerLabel}"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
MainWindow.xaml
<Window x:Class="MyNamespace.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:lcl="clr-namespace:MyNamespace"
Title="Test" Width="500" Height="350">
<Window.Resources>
<ResourceDictionary Source="TestDictionary.xaml"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Button Content="Enable/Disable" Click="Click"/>
<Button Grid.Column="1" x:Name="btn" Style="{DynamicResource BtnTest}" lcl:TestClass.String="TESTING"/>
</Grid>
</Window>
MainWindow.xaml.cs
using System.Windows;
namespace MyNamespace
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Click(object sender, RoutedEventArgs e)
{
btn.IsEnabled = !btn.IsEnabled;
}
}
public class TestClass
{
public static string GetString(DependencyObject obj)
{
return (string)obj.GetValue(StringProperty);
}
public static void SetString(DependencyObject obj, string value)
{
obj.SetValue(StringProperty, value);
}
public static readonly DependencyProperty StringProperty =
DependencyProperty.RegisterAttached("String", typeof(string), typeof(TestClass), new PropertyMetadata("Default!"));
}
}
Instead of using a TemplateBinding, I also tried this:
{Binding Path=lcl:TestClass.String, RelativeSource={RelativeSource AncestorType={x:Type Button}}}
It still didn't work.
I know I'm probably doing something wrong, but the question is: what is it?
All you really need to make this work is to use RelativeSource in your binding. Since you are setting the attached property on the Button, in your style trigger, you can just bind to the attached property on self:
<Style TargetType="Button" x:Key="BtnTest">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="Content"
Value="{Binding Path=(lcl:TestClass.String), RelativeSource={RelativeSource Self}}"/>
</Trigger>
</Style.Triggers>
</Style>
One cool thing about using your approach, since Button is a ContentControl, you're attached property can be any object, not just strings.
And to clarify what went wrong in your previous approach -
As others have said, TemplateBinding only works in ControlTemplates. It also only works when the DependencyProperty is defined on the class you are creating the template for (so you can never do a TemplateBinding to Grid.Row for example)
When binding to an attached property, the whole thing needs to be in parentheses, otherwise WPF will try to bind to a property of a property. Otherwise your RelativeSource binding was close!
I think if you want to have a Label inside the Button as the content, it may work (I didn't test that), but it doesn't seem like the best idea, as your Button can host any object you want.
EDIT for more complex example
So, if you need to display more than one dynamic property, I would recommend using a DataTemplate:
<Style TargetType="Button" x:Key="BtnTest">
<Style.Triggers>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Label Content="{Binding Path=(lcl:TestClass.String), RelativeSource={RelativeSource AncestorType={x:Type Button}}}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
Also, I want to point out that a DataTemplateSelector might be more applicable if you have multiple different criteria for changing the look of the content.
Now I see the details. What you should write before relative source is like:
Binding Path=(lcl:TestClass.String)
Do not forget to add parenthesis.
Your example does not work because TemplateBinding only works in a ControlTemplate. To achieve something akin to a TemplateBinding in Resources you need to do other stuff. Here's an example.
In order for TemplateBinding to work, you need to fix the code a little bit (this is just an example with no resources):
<Style x:Key="BtnTest" TargetType="{x:Type Button}">
<Setter Property="MinHeight" Value="100" />
<Setter Property="MinWidth" Value="200" />
<Setter Property="BorderThickness" Value="2" />
<Setter Property="BorderBrush" Value="Blue" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2" Background="{TemplateBinding Background}">
<ContentPresenter RecognizesAccessKey="True" Content="{TemplateBinding lcl:TestClass.String}" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" Value="0.5" />
</Trigger>
</Style.Triggers>
</Style>
Useful links about this topic: Here, and here too.
EDIT:
You can also use the application settings instead of TestClass. Open "Project -> Properties: MyNamespace... -> Settings" and add your settings:
Name--------Type--------Scope--------Value
LabelText---string--------User----------Default
Set the your value for the LabelText in code. For example:
public MainWindow()
{
InitializeComponent();
MyNamespace.Properties.Settings.Default.LabelText = "Testing";
}
And use this ResourceDictionary:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:properties="clr-namespace:MyNamespace.Properties"
xmlns:lcl="clr-namespace:MyNamespace">
<Style TargetType="Button" x:Key="BtnTest">
<Style.Resources>
<Label x:Key="innerLabel" Content="{Binding Source={x:Static properties:Settings.Default}, Path=LabelText, Mode=TwoWay}" />
</Style.Resources>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="True">
<Setter Property="Content" Value="{DynamicResource innerLabel}"/>
</Trigger>
</Style.Triggers>
</Style>

Categories

Resources