I'm creating an onscreen keyboard for a touch screen app, where shift toggles upper and lower case buttons on the whole keyboard.
The code in the c# is working but I don't know how to change the content value and command parameter of the buttons based on on my custom property which changes on a bool value, in xaml.
<local:KeyboardButton Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="2" Command="{Binding AddText}" Content ="{Binding local:KeyboardButton.SelectedKey}" LowerCaseKey="`" UpperCasekey="¬"/>
This is what I have currently for each button in the XAML (ignore the Content, as I've been grasping at straws here), the idea is that the shift key will toggle the Content and CommandParameter between the LowerCaseKey and UpperCaseKey properties.
maybe you could achieve your goal with styles and triggers:
<Button Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="2" Command="{Binding AddText}" x:Name="AButton">
<Button.Resources>
<Style TargetType="Button">
<Setter Property="Content" Value="{Binding Path=LowerCaseKey, ElementName=AButton}" />
<Setter Property="CommandParameter" Value="{Binding Path=LowerCaseKey, ElementName=AButton}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsUpperCase}" Value="true">
<Setter Property="Content" Value="{Binding Path=UpperCasekey, ElementName=AButton}" />
<Setter Property="CommandParameter" Value="{Binding Path=UpperCasekey, ElementName=AButton}" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Resources>
</Button>
Custom Control:
using System.Windows;
using System.Windows.Controls;
namespace Test
{
public class KeyboardButton : Button
{
public static readonly DependencyProperty SelectedKeyProperty = DependencyProperty.Register("SelectedKey", typeof(string),
typeof(KeyboardButton), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.AffectsArrange));
public static readonly DependencyProperty IsUpperCaseProperty = DependencyProperty.Register("IsUpperCase", typeof(bool),
typeof(KeyboardButton), new FrameworkPropertyMetadata(false));
static KeyboardButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(KeyboardButton), new FrameworkPropertyMetadata(typeof(KeyboardButton)));
}
public string SelectedKey
{
get { return (string)GetValue(SelectedKeyProperty); }
set { SetValue(SelectedKeyProperty, value); }
}
public string LowerCaseKey
{
get;
set;
}
public string UpperCaseKey
{
get;
set;
}
public bool IsUpperCase
{
get { return (bool)GetValue(IsUpperCaseProperty); }
set { SetValue(IsUpperCaseProperty, value); }
}
}
}
Themes\Generic.xaml (file Generic.xaml in the Themes folder)
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Test">
<Style TargetType="{x:Type local:KeyboardButton}" BasedOn="{StaticResource {x:Type Button}}">
<Setter Property="Content" Value="{Binding LowerCaseKey, Mode=OneTime, RelativeSource={RelativeSource Self}}"/>
<Style.Triggers>
<Trigger Property="IsUpperCase" Value="true">
<Setter Property="Content" Value="{Binding UpperCaseKey, Mode=OneTime, RelativeSource={RelativeSource Self}}"/>
</Trigger>
</Style.Triggers>
</Style>
</ResourceDictionary>
Don't forget this in AssemblyInfo.cs:
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
Related
The objective is to swap the control at runtime based on a property in my ViewModel, and have the displayed control be have a binding that updates the properties in the ViewModel. I started by creating the following Style in the View.xaml:
<UserControl.Resources>
<Style x:Key="DisplayTextOrButton" TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding TextNotButton}" Value="True">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBox Content="{Binding SomeText}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding TextNotButton}" Value="False">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Button Content="{Binding ButtonText}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
Note that the bindings LabelText and ButtonText are the bindings to the properties in the ViewModel.
Then further on in the View.xaml I have the following:
<ContentControl Content="{Binding TextNotButton}"
Style="{StaticResource DisplayTextOrButton}">
</ContentControl>
Finally, the ViewModel.cs has the following properties:
private bool textNotButton;
public bool TextNotButton
{
get => this.textNotButton;
set
{
this.textNoButton = value;
this.OnPropertyChanged("TextNotButton");
}
}
private string someText;
public string SomeText
{
get => this.someText;
set
{
this.someText = value;
this.OnPropertyChanged("SomeText");
}
}
private string buttonText;
public string ButtonText
{
get => this.buttonText;
set
{
this.buttonText = value;
this.OnPropertyChanged("ButtonText");
}
}
The style works well for swapping between the label and the button, but changing the text in the TextBox does not update the property in the ViewModel, and the Button's text is empty (I imagine because the binding hasn't worked)
I believe this is because the style is a static resource so the bindings SomeText and ButtonText in the style aren't actually the bindings in the ViewModel, but I'm not sure how to pass the reference of the other properties into the style. Or even if that's a thing. I'm pretty new to XAML so not sure on how to handle this
I'm trying to create a re-usable textblock user control in WPF. The basic idea is as follows:
User does not directly specify the content of the textblock
There are three dependancy properties in my user control called IsToggled, ToggleTrueText, and ToggleFalseText.
The control will display ToggleTrueText if IsToggled is true; or display ToggleFalseText if IsToggled is false.
When IsToggled changes during runtime, the text automatically changes to either ToggleTrueText or ToggleFalseText
I started by adding a PropertyChangedCallback to the IsToggled DP:
Code-behind of the UserControl:
public static readonly DependencyProperty IsToggledProperty =
DependencyProperty.Register("IsToggled", typeof(bool),
typeof(TagToggle), new PropertyMetadata(new
PropertyChangedCallback(OnToggleStateChanged)));
public bool IsToggled
{
get { return (bool)GetValue(IsToggledProperty); }
set { SetValue(IsToggledProperty, value); }
}
//ToggleTrueText and ToggleFalseText are declared similarly to IsToggled
...
private static void OnToggleStateChanged(DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
...
}
Xaml of the user control:
<Grid x:Name="LayoutRoot">
<TextBlock x:Name="TheTextBlock" Text="{Binding WhatDoIBindTo}"/>
</Grid>
However, I'm not sure what would be the best way to ensure that TheTextBlock updates its text whenever IsToggled changes during runtime.
Try this:
private static void OnToggleStateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
TagToggle ctrl = d as TagToggle;
if (ctrl != null)
{
TheTextBlock.Text = ctrl.IsToggled ? ToggleTrueText. : ToggleFalseText;
}
}
If you want to bind the Text property of the TextBlock you need to make sure that you are binding to properties of the UserControl. You could do this by setting the DataContext property of the TextBlock:
<TextBlock x:Name="TheTextBlock" DataContext="{Binding RelativeSource={RelativeSource AncestorType=UserControl}}">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Text" Value="{Binding ToggleTrueText}" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsToggled}" Value="False">
<Setter Property="Text" Value="{Binding ToggleFalseText}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
You can used trigger for this
Please check below code
<TextBlock x:Name="TheTextBlock">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsToggled}" Value="True">
<Setter Property="Text" Value="{Binding ToggleTrueText}"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsToggled}" Value="False">
<Setter Property="Text" Value="{Binding ToggleFalseText}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
I have the following custom control:
public class AnimatedButton : Button
{
public enum ButtonStates
{
None,
Busy
}
public ButtonStates State
{
get { return (ButtonStates)GetValue(StateProperty); }
set { SetValue(StateProperty, value); }
}
// Using a DependencyProperty as the backing store for State. This enables animation, styling, binding, etc...
public static readonly DependencyProperty StateProperty =
DependencyProperty.Register("State", typeof(ButtonStates), typeof(AnimatedButton), new PropertyMetadata(ButtonStates.None));
public ImageSource ImageDefault
{
get { return (ImageSource)GetValue(ImageDefaultProperty); }
set { SetValue(ImageDefaultProperty, value); }
}
// Using a DependencyProperty as the backing store for ImageDefault. This enables animation, styling, binding, etc...
public static readonly DependencyProperty ImageDefaultProperty =
DependencyProperty.Register("ImageDefault", typeof(ImageSource), typeof(AnimatedButton), new PropertyMetadata(null));
public ImageSource ImageBusy
{
get { return (ImageSource)GetValue(ImageBusyProperty); }
set { SetValue(ImageBusyProperty, value); }
}
...
}
My aim here is to display the appropriate image source based on the current button state. For example, if the ButtonState is None, then display the default image, otherwise display the Busy image, pretty straightforward. Here is the style:
<Style TargetType="{x:Type controls:AnimatedButton}">
...
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:AnimatedButton}">
<Border>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image x:Name="img"/>
<TextBlock Text="{TemplateBinding Content}"
Grid.Column="1"/>
</Grid>
</Border>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding State}" Value="None">
<Setter TargetName="img" Property="Source" Value="{Binding ImageDefault}"/>
</DataTrigger>
<DataTrigger Binding="{Binding State}" Value="Busy">
<Setter TargetName="img" Property="Source" Value="{Binding ImageBusy}"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The problem is in the DataTrigger, it isn't picking up the dependency property State. After adding the control onto a view, I am receiving the following error in the Output:
System.Windows.Data Error: 40 : BindingExpression path error: 'State' property not found on 'object' ''WorkspaceViewModel' (HashCode=56037929)'. BindingExpression:Path=State; DataItem='WorkspaceViewModel' (HashCode=56037929); target element is 'AnimatedButton' (Name=''); target property is 'NoTarget' (type 'Object')
Reading that error message, it appears as though it's looking for the State property on the WorkspaceViewModel as opposed to the control that the dependency property belongs to. Why is this?
The Bindings in the DataTriggers (correctly) expect the State property to be in the DataContext of the control. But you want to trigger on the value of a property of the control itself.
You should therefore use Triggers instead of DataTriggers:
<ControlTemplate.Triggers>
<Trigger Property="State" Value="None">
<Setter TargetName="img" Property="Source"
Value="{Binding ImageDefault, RelativeSource={RelativeSource TemplatedParent}}"/>
</Trigger>
<Trigger Property="State" Value="Busy">
<Setter TargetName="img" Property="Source"
Value="{Binding ImageBusy, RelativeSource={RelativeSource TemplatedParent}}"/>
</Trigger>
</ControlTemplate.Triggers>
I am creating an animation control and where I am trying to use data triggers. The issue is the dp property which I created is not getting changed/called when the trigger is fired. Here is the summary of behaviour I noticed.
1) The code behind of never gets called.
2) Property appears in XAML intellisense but the changes given in XAML never gets applied (design/runtime). But if I replace 'IsSpinning' in "public static readonly DependencyProperty IsSpinningProperty = DependencyProperty.Register("IsSpinning", typeof(bool), typeof(ProgressWaitSpinner), new UIPropertyMetadata(false));" to something else( say 'xyz') it starts working for property assignment, but throws runtime exception if styles are enabled.
3) When running the sample, The rectangle should be hidden instead of showing as Chocolate color, which is not happening.
4) Setter for changing color is working, which is from the user control, however the setter property on newly created property is not working.
I created a simplified sample here which shows the problem. Anyone got a clue what is going on please?
UserControl XAML:
<UserControl x:Class="CustomControls.ProgressWaitSpinner"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:CustomControls"
Height="191" Width="191">
<Grid x:Name="LayoutRoot">
<Label Height="32" Name="label1" VerticalAlignment="Top" />
</Grid>
</UserControl>
UserControl Code:
using System.Windows;
using System.Windows.Controls;
namespace CustomControls
{
public partial class ProgressWaitSpinner : UserControl
{
public ProgressWaitSpinner(){InitializeComponent();}
public bool IsSpinning
{
get
{
return (bool)GetValue(IsSpinningProperty);
}
set
{
if (value == true)
{
this.Visibility = System.Windows.Visibility.Visible;
}
else
{
this.Visibility = System.Windows.Visibility.Hidden;
}
SetValue(IsSpinningProperty, value);
}
}
public static readonly DependencyProperty IsSpinningProperty = DependencyProperty.Register("IsSpinning", typeof(bool), typeof(ProgressWaitSpinner), new UIPropertyMetadata(false));
}
}
MainWindow XAML:
<Window x:Class="WPFSpinnerWait.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:usrctrl="clr-namespace:CustomControls"
Title="MainWindow" Height="208" Width="228">
<Grid>
<usrctrl:ProgressWaitSpinner Height="40" x:Name="WaitSpinner" Margin="110,103,0,0" HorizontalAlignment="Left" Width="84" VerticalAlignment="Top">
<usrctrl:ProgressWaitSpinner.Style>
<Style>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=label1, Path=Content}" Value="NotStarted"></Condition>
</MultiDataTrigger.Conditions>
<Setter Property="usrctrl:ProgressWaitSpinner.Background" Value="Red" />
<Setter Property="usrctrl:ProgressWaitSpinner.IsSpinning" Value="false"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=label1, Path=Content}" Value="Running"></Condition>
</MultiDataTrigger.Conditions>
<Setter Property="usrctrl:ProgressWaitSpinner.Background" Value="Chocolate" />
<Setter Property="usrctrl:ProgressWaitSpinner.IsSpinning" Value="true" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
</usrctrl:ProgressWaitSpinner.Style>
</usrctrl:ProgressWaitSpinner>
<Button Content="NotStarted" Height="28" HorizontalAlignment="Left" Margin="38,22,0,0" Name="checkBox1" VerticalAlignment="Top" Width="136" Click="checkBox1_Checked" />
<Button Content="Running" Height="30" HorizontalAlignment="Left" Margin="38,56,0,0" Name="checkBox2" VerticalAlignment="Top" Width="136" Click="checkBox1_Checked" />
<Label Content="NotStarted" DataContext="usrctrl:ProgressWaitSpinner" Height="25" HorizontalAlignment="Left" Margin="38,92,0,0" Name="label1" VerticalAlignment="Top" Width="114" />
</Grid>
</Window>
MainWindow Code:
using System.Windows;
using System.Windows.Controls;
namespace WPFSpinnerWait
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void checkBox1_Checked(object sender, RoutedEventArgs e)
{
label1.Content = ((Button)sender).Content.ToString();
}
}
}
The code behind won't get called, DependancyProperties do not use the backing property when the property is change/used in Xaml, thay are only there for use in code behind as a helper, thay have no use in Xaml bindings
You can use the PropertyChanged event of the DependancyProperty instead
public bool IsSpinning
{
get { return (bool)GetValue(IsSpinningProperty); }
set { SetValue(IsSpinningProperty, value); }
}
// Using a DependencyProperty as the backing store for IsSpinning. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsSpinningProperty =
DependencyProperty.Register("IsSpinning", typeof(bool), typeof(ProgressWaitSpinner), new PropertyMetadata(false, OnIsSpinningChanged));
private static void OnIsSpinningChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (((bool)e.NewValue) == true)
{
(d as ProgressWaitSpinner).Visibility = System.Windows.Visibility.Visible;
}
else
{
(d as ProgressWaitSpinner).Visibility = System.Windows.Visibility.Hidden;
}
}
Edit:
For your second question, Try adding the TargetType for your Style so you can access the properties directly
<Style TargetType="{x:Type usrctrl:ProgressWaitSpinner}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=label1, Path=Content}" Value="NotStarted"></Condition>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="Red" />
<Setter Property="IsSpinning" Value="false"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding ElementName=label1, Path=Content}" Value="Running"></Condition>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="Chocolate" />
<Setter Property="IsSpinning" Value="true" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
I have different ControlTemplates for one Canvas:
<Application.Resources>
<ControlTemplate x:Key="Control1" />
<ControlTemplate x:Key="Control2" />
</Application.Resources>
I want to change one of them by my viewmodel property like this:
private string _template = "Control1";
public string Template
{
get
{
return _template;
}
set
{
if (!string.IsNullOrEmpty(value))
{
_template = value;
OnPropertyChanged("Template");
}
}
}
And finally use it in my view:
<UserControl Template="{StaticResource {Binding Template}}" />
But it doesn't work, how i can fix it?
Thanks
You can try using DataTriggers
<UserControl>
<UserControl.Style>
<Style TargetType="{x:Type UserControl}">
<Setter Property="ControlTemplate" value="{StaticResource Control1}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Template}" Value ="Control1">
<Setter Property="ControlTemplate" value="{StaticResource Control1}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Template}" Value ="Control2">
<Setter Property="ControlTemplate" value="{StaticResource Control2}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</UserControl.Style>
</UserControl>