Using a Dependency Property in a Data Trigger - c#

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>

Related

Image Visibility DataTrigger Set Default to False

I have an image that need to be showed based on condition, is that attachment file or not. The problem is, I've set trigger that set the value of the condition, but seems like the condition isn't work and the value always set to true.
<Image
Width="30"
Height="30"
Source="Resources/Images/chat_file_attach.png">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding AttachStat}" Value="False">
<Setter Property="Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding AttachStat}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
The question is. Is there any way to make the default value to false? I've set it to true on the C# looping data, whenever the condition is attachment included.
Take a look in your output window and search for:
System.Windows.Data Warning: 40 : BindingExpression path error
I think the AttachStat property is not available in the image's DataContext.
Use a single DataTrigger and make sure that the DataContext of the Image has a public AttachStat property:
<Image x:Name="img" Width="30" Height="30" Source="Resources/Images/chat_file_attach.png">
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Visibility" Value="Collapsed" />
<Style.Triggers>
<DataTrigger Binding="{Binding AttachStat}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</Image.Style>
</Image>
img.DataContext = new YourClass();
...
public class YourClass : INotifyPropertyChanged
{
private bool _attachStat;
public bool AttachStat
{
get
{
return _attachStat;
}
set
{
_attachStat = value;
NotifyPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
You can use a BoolToVisibilityConverter to convert a Boolean to and from a Visibility value.
In the resource section:
<BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter" />
And the image:
<Image Width="30"
Height="30"
Source="Resources/Images/chat_file_attach.png"
Visibility="{Binding AttachStat,
Converter={StaticResource BoolToVisibilityConverter}}" />

WPF Set property of element in Style (Visual.Brush)

How can I set the Content property of the Label (L_Watermark)?
I tried different ways but nothing work.
Normally to set a property of an element about his name but here it doesn´t work.
I want do do it in the CodeBehind or with Databinding to a dependency property
The XAML of my UserControl:
<StackPanel Orientation="Horizontal" Width="155">
<TextBox x:Name="TB_Date" LostFocus="TB_Date_LostFocus" KeyDown="TB_Date_KeyDown" BorderThickness="0" VerticalContentAlignment="Center" Width="125" Height="24">
<TextBox.Style>
<Style TargetType="TextBox" xmlns:sys="clr-namespace:System;assembly=mscorlib">
<Style.Resources>
<VisualBrush x:Name="VisuBrush" x:Key="CueBannerBrush" AlignmentX="Left" AlignmentY="Center" Stretch="None">
<VisualBrush.Visual>
<Label x:Name="L_Watermark" Content="{Binding Watermark, UpdateSourceTrigger=PropertyChanged, Mode=OneWay, FallbackValue=Date}" Foreground="LightGray" />
</VisualBrush.Visual>
</VisualBrush>
</Style.Resources>
<Style.Triggers>
<Trigger Property="Text" Value="{x:Static sys:String.Empty}">
<Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
</Trigger>
<Trigger Property="Text" Value="{x:Null}">
<Setter Property="Background" Value="{StaticResource CueBannerBrush}" />
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="True">
<Setter Property="Background" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
<DatePicker Name="DP_Date" Focusable="False" BorderBrush="{x:Null}" SelectedDateChanged="DP_Date_SelectedDateChanged" Width="30" FirstDayOfWeek="Monday" Height="24"/>
</StackPanel>
Here is the eventhandler in which i want to set the Content property of the L_Watermark label
private void Initialize(object sender, EventArgs e)
{
if (HasStartTime)
{
TB_Date.Text = DateTime.Now.ToLongDateString();
}
Debug.WriteLine(Watermark);
}
Like the Text in TB_Date i tried to to it with L_Watermark.Content = Watermark
I also tried to do it with Databinding like:
<Label x:Name="L_Watermark" Content="{Binding Watermark, UpdateSourceTrigger=PropertyChanged, Mode=OneWay, FallbackValue=Date}" Foreground="LightGray" />
I also have two dependency properties in my UserControl:
public bool HasStartTime
{
get { return (bool)GetValue(HasStartTimeProperty); }
set
{
SetValue(HasStartTimeProperty, value);
OnPropertyChanged();
}
}
// Using a DependencyProperty as the backing store for HasStartTime. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HasStartTimeProperty =
DependencyProperty.Register("HasStartTime", typeof(bool), typeof(AdvancedDatePicker), new PropertyMetadata(false));
public string Watermark
{
get { return (string)GetValue(WatermarkProperty); }
set
{
SetValue(WatermarkProperty, value);
OnPropertyChanged();
}
}
// Using a DependencyProperty as the backing store for Watermark. This enables animation, styling, binding, etc...
public static readonly DependencyProperty WatermarkProperty =
DependencyProperty.Register("Watermark", typeof(string), typeof(AdvancedDatePicker), new PropertyMetadata("Datum"));

Creating a self-updating Textblock user control in WPF

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>

Setting the content property dynamically using a custom property XAML WPF

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)
)]

Writing a custom control in Windows 8 Metro

I'm writing my first custom user control against WinRT, and I've run into a problem.
I would like to expose an image, PART_NwBadge and it's visibility as Dependency Properties in my control. Then I would like to supply default values through setters in the style. This part is not working. Instead, the default value from the DependencyProperty (in BadgedButton.cs) is being applied.
Is it even possible to do what I've described? Or should I be setting the default values in the C# code? If I do need to set the values in the C# code, would someone comment on how to load image resources in code? After a great deal of searching I've yet to find a solution that works.
Finally, since this is my first serious attempt at writing a custom control, please suggest any improvements I could make, even if they are not directly related to the problem.
Windows 8 Consumer Preview
C#/WinRT/Metro
Visual Studio 11 Beta
Themes/Generic.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:l="using:InkSdkTestApplication.Controls">
<Style TargetType="l:BadgedButton">
<Setter Property="Width" Value="36"/>
<Setter Property="Height" Value="36"/>
<Setter Property="Background" Value="#1C1C1C"/>
<Setter Property="BorderBrush" Value="White"/>
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="NwBadge">
<Setter.Value>
<Image Width="16" Height="16" Source="../Assets/mouse_16x16.png"/>
</Setter.Value>
</Setter>
<Setter Property="NwBadgeVisibility" Value="Collapsed"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="l:BadgedButton">
<Border x:Name="PART_Border"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<ContentPresenter x:Name="PART_Content"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Content="{TemplateBinding Content}"/>
<Image x:Name="PART_NwBadge"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Width="16" Height="16"
Visibility="{TemplateBinding NwBadgeVisibility}"
Source="{TemplateBinding NwBadge}"/>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Controls/BadgedButton.cs
namespace InkSdkTestApplication.Controls
{
public sealed class BadgedButton : Control
{
#region // Dependency Properties
public static DependencyProperty ContentProperty =
DependencyProperty.Register(
"Content",
typeof(FrameworkElement),
typeof(BadgedButton),
new PropertyMetadata(null));
public static DependencyProperty NwBadgeProperty =
DependencyProperty.Register(
"NwBadge",
typeof(Image),
typeof(BadgedButton),
new PropertyMetadata(null));
public static DependencyProperty NwBadgeVisibilityProperty =
DependencyProperty.Register(
"NwBadgeVisibility",
typeof(Visibility),
typeof(BadgedButton),
new PropertyMetadata(Visibility.Visible));
#endregion
#region // Public Properties
public FrameworkElement Content
{
get { return (FrameworkElement)GetValue(ContentProperty); }
set { SetValue(ContentProperty, value); }
}
public Image NwBadge
{
get { return (Image)GetValue(NwBadgeProperty); }
set { SetValue(NwBadgeProperty, value); }
}
public Visibility NwBadgeVisibility
{
get { return (Visibility)GetValue(NwBadgeVisibilityProperty); }
set { SetValue(NwBadgeVisibilityProperty, value); }
}
#endregion
public BadgedButton()
{
this.DefaultStyleKey = typeof(BadgedButton);
}
}
}
http://timheuer.com/blog/archive/2012/03/07/creating-custom-controls-for-metro-style-apps.aspx

Categories

Resources