I want to achieve an effect, where the ListBoxItem gets selected (highlighted) for a few seconds and after that it gets deselected. Trying to achieve a simple control highlight with fade effects but in order to make things work, I need a property to be changed accordingly.
I have a IsSelected property bound to my view model property:
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</Style>
My property looks like so:
public bool IsSelected
{
get => _isSelected;
set
{
// Update value
_isSelected = value;
// Raise property changed
OnPropertyChanged(nameof(IsSelected));
}
}
I have tried using a delayed binding:
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<ControlTemplate.Triggers>
<!-- Deselect after 5 seconds -->
<DataTrigger Binding="{Binding IsSelected, Delay=5000}" Value="True">
<Setter Property="IsSelected" Value="False" />
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
I have also tried using storyboards:
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<ControlTemplate.Triggers>
<!-- Deselect after 5 seconds -->
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard>
<Storyboard>
<BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsSelected">
<DiscreteBooleanKeyFrame KeyTime="00:00:05" Value="False" />
</BooleanAnimationUsingKeyFrames>
</Storyboard>
</BeginStoryboard>
</DataTrigger.EnterActions>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Unfortunately, none of the above approach seem to update my IsSelected property and the ListBoxItem remains selected and highlighted.
I want this to be done in XAML (or extensions), MVVM style, no code behind and no wasteful timers - is this possible? If so, how can I properly deselect a ListBoxItem with a delay?
Here is an example of an attached behaviour that should do what you want, more or less:
public class DeselectBehavior
{
public static bool GetIsEnabled(ListBox listBox)
{
return (bool)listBox.GetValue(IsEnabledProperty);
}
public static void SetIsEnabled(ListBox listBox, bool value)
{
listBox.SetValue(IsEnabledProperty, value);
}
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached(
"IsEnabled",
typeof(bool),
typeof(DeselectBehavior),
new UIPropertyMetadata(false, OnChanged));
private static void OnChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ListBox listBox = d as ListBox;
if((bool)e.NewValue)
{
listBox.AddHandler(ListBoxItem.SelectedEvent, (RoutedEventHandler)OnListBoxItemSelected, true);
}
else
{
listBox.RemoveHandler(ListBoxItem.SelectedEvent, (RoutedEventHandler)OnListBoxItemSelected);
}
}
private static async void OnListBoxItemSelected(object sender, RoutedEventArgs e)
{
await Task.Delay(2000);
ListBoxItem listBoxItem = e.OriginalSource as ListBoxItem;
if (listBoxItem != null)
listBoxItem.IsSelected = false;
}
}
XAML:
<ListBox ... local:DeselectBehavior.IsEnabled="True">
...
Related
I have some problem in changing Ellipse Fill value based on data binding a boolean.
true = Lime colour
false = Red colour
My code does not prompt out any error but also does not show fill value colours.
WPF XAML code:
<Ellipse x:Name="damageSpoolSlot1" HorizontalAlignment="Left" Height="45" Stroke="Black" VerticalAlignment="Top" Width="70" Grid.Column="1" Margin="203.4,377.2,0,0" Grid.Row="2">
<Ellipse.Style>
<Style TargetType="{x:Type Ellipse}">
<Style.Triggers>
<DataTrigger Binding="{Binding DamageSpoolSlot1PresenceSensorOn}" Value="false">
<Setter Property="Fill" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding DamageSpoolSlot1PresenceSensorOn}" Value="true">
<Setter Property="Fill" Value="Lime"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
Code-Behind:
Binding myBinding = new Binding("DamageSpoolSlot1PresenceSensorOn")
{
Source = MyBinding.Instance
};
BindingOperations.SetBinding(damageSpoolSlot1, Ellipse.FillProperty, myBinding);
My data binding code:
public class MyBinding : INotifyPropertyChanged
{
private static volatile MyBinding instance;
private static object syncRoot = new Object();
public static MyBinding Instance
{
get
{
if (instance == null)
{
lock (syncRoot)
{
if (instance == null)
instance = new MyBinding();
}
}
return instance;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private bool _DamageSpoolSlot1PresenceSensorOn = false;
public bool DamageSpoolSlot1PresenceSensorOn
{
get { return _DamageSpoolSlot1PresenceSensorOn; }
set
{
if (value != _DamageSpoolSlot1PresenceSensorOn)
{
_DamageSpoolSlot1PresenceSensorOn = value;
OnPropertyChanged("DamageSpoolSlot1PresenceSensorOn");
}
}
}
}
The data triggers in your style bind to the DataContext of the Ellipse. This means they expect an object that has a property DamageSpoolSlot1PresenceSensorOn. However, your binding fails in two ways.
The binding already binds to the DamageSpoolSlot1PresenceSensorOn of the MyBinding.Instance, so the data triggers will try to bind to another sub property DamageSpoolSlot1PresenceSensorOn that simply does not exist, the DataContext already is this property.
The binding binds the Fill property, which expects a Brush, but the data triggers bind to the DataContext of the Ellipse, not the Fill property.
Change the binding like this, so it will deliver the Instace to the DataContext property.
Binding myBinding = new Binding()
{
Source = MyBinding.Instance
};
BindingOperations.SetBinding(damageSpoolSlot1, Ellipse.DataContextProperty, myBinding);
Another simpler option is to create the binding to the MyBinding.Instance directly in XAML. Bind the DataContext property using a binding that specifies the static MyBinding.Instance as Source with the x:Static markup extension.
<Ellipse x:Name="damageSpoolSlot1" HorizontalAlignment="Left" Height="45" Stroke="Black" VerticalAlignment="Top" Width="70" Margin="203.4,377.2,0,0"
DataContext="{Binding Source={x:Static local:MyBinding.Instance}}">
<Ellipse.Style>
<Style TargetType="{x:Type Ellipse}">
<Setter Property="Fill" Value="Red"/>
<Style.Triggers>
<DataTrigger Binding="{Binding DamageSpoolSlot1PresenceSensorOn}" Value="True">
<Setter Property="Fill" Value="Lime"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
As #Clemens pointed out, you could also simply set the DataContext in code-behind.
damageSpoolSlot1.DataContext = MyBinding.Instance;
I have dynamic Datagrid where columns creating programmatically and assigning items. After update I'm getting binding errors and I don't know how avoid them,
It seems that the styles are binded to Items in Visual tree and when updating items the triggers running on orphan data
<local:PerBalanceReportDataGrid Background="Transparent" RowBackground="Transparent" Panel.ZIndex="1000"
FrozenColumnCount="1"
ScrollViewer.CanContentScroll="True"
HorizontalScrollBarVisibility="Visible"
ScrollViewer.ScrollChanged="PerBalanceReportDataGrid_ScrollChanged"
Grid.Row="1"
ColumnWidth="150"
x:Name="PerBalanceReportDataGrid" >
<local:PerBalanceReportDataGrid.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Foreground" Value="{Binding RelativeSource={RelativeSource Mode=Self}, Converter={StaticResource textToColorConverter}}"/>
<Setter Property="FontWeight" Value="{Binding RelativeSource={RelativeSource Mode=Self}, Converter={StaticResource textToBoldConverter}}"/>
</Style>
</local:PerBalanceReportDataGrid.CellStyle>
<local:PerBalanceReportDataGrid.RowStyle>
<Style TargetType="DataGridRow">
<Setter Property="Background" Value="Transparent"/>
</Style>
</local:PerBalanceReportDataGrid.RowStyle>
<local:PerBalanceReportDataGrid.ColumnHeaderStyle>
<Style TargetType="DataGridColumnHeader">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="SeparatorVisibility" Value="Hidden" />
</Style>
</local:PerBalanceReportDataGrid.ColumnHeaderStyle>
</local:PerBalanceReportDataGrid>
public class ExtendedDataGrid<TDynamicRowType, TCellDataType> : DataGrid where TDynamicRowType : DynamicDataRow<TCellDataType>
{
public static readonly DependencyProperty DataTableProperty =
DependencyProperty.Register(
"DataTable",
typeof(DataTable<TDynamicRowType, TCellDataType>),
typeof(ExtendedDataGrid<TDynamicRowType, TCellDataType>),
new PropertyMetadata(null, OnDataTablePropertyChanged));
public DataTable<TDynamicRowType, TCellDataType> DataTable
{
get { return (DataTable<TDynamicRowType, TCellDataType>)GetValue(DataTableProperty); }
set { SetValue(DataTableProperty, value); }
}
private static void OnDataTablePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (d is ExtendedDataGrid<TDynamicRowType, TCellDataType> grid)
grid.Init();
}
private void Init()
{
if (DataTable == null)
return;
ItemsSource = null;
Columns.Clear();
SetValue(DataGridReadOnlyBehavior<TDynamicRowType>.PredicateProperty, DataTable.IsReadOnlyPredicate);
Columns.AddRange(DataTable.Columns.Select(FromInfo));
ItemsSource = DataTable.Rows;
ICollectionView view = CollectionViewSource.GetDefaultView(ItemsSource);
view.GroupDescriptions.AddRange(
DataTable.GroupByProperties.Select(x => new PropertyGroupDescription(x)));
ExtendedColumn<TCellDataType> FromInfo(ColumnInfo info) => new (info);
}
}
public class PerBalanceReportDataGrid : ExtendedDataGrid<PerBalanceReportDataRow, string>
{
static PerBalanceReportDataGrid() =>
DefaultStyleKeyProperty.OverrideMetadata(
typeof(PerBalanceReportDataGrid),
new FrameworkPropertyMetadata(typeof(PerBalanceReportDataGrid)));
}
After I'm updating the table I'm getting the following errors
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='System.Windows.Controls.DataGridRow', AncestorLevel='1''. BindingExpression:Path=Foreground; DataItem=null; target element is 'DataGridCell' (Name=''); target property is 'Foreground' (type 'Brush')
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 a PCL and I am using MVVM pattern.
I am building a togglebutton, that toggles when the related property in the viewmodel is activated, with behaviors and triggers.
This is my behavior
public class ToggleBehavior : Behavior<View>
{
TapGestureRecognizer tapRecognizer;
public static readonly BindableProperty IsToggledProperty = BindableProperty.Create<ToggleBehavior, bool>(tb => tb.IsToggled, false);
public bool IsToggled
{
set { SetValue(IsToggledProperty, value); }
get { return (bool)GetValue(IsToggledProperty); }
}
}
This is how I consume my behavior
<Button Text="AUTO" Style="{StaticResource SmallEllipseButton}" BackgroundColor="{StaticResource BackgroundColor}" cm:Message.Attach="Automatic($dataContext)">
<Button.Behaviors>
<local:ToggleBehavior x:Name="autoToggleBehavior" IsToggled="{Binding CurrentMode,Converter={StaticResource modeToBooleanConverter}, ConverterParameter=AUTO}"/>
</Button.Behaviors>
<Button.Triggers>
<DataTrigger TargetType="Button" Binding="{Binding Source={x:Reference autoToggleBehavior},Path=IsToggled}" Value="False" >
<Setter Property="BackgroundColor" Value="White"/>
<Setter Property="BorderColor" Value="White"/>
<Setter Property="TextColor" Value="Gray"/>
</DataTrigger>
<DataTrigger TargetType="Button" Binding="{Binding Source={x:Reference autoToggleBehavior},Path=IsToggled}" Value="True" >
<Setter Property="BackgroundColor" Value="{StaticResource BackgroundColor}"/>
<Setter Property="TextColor" Value="White"/>
</DataTrigger>
</Button.Triggers>
</Button>
The problem is that the property IsToggled is not binded correctly in this IsToggled="{Binding CurrentMode,Converter={StaticResource modeToBooleanConverter}, ConverterParameter=AUTO}"/>
If I set it statically to true or false it works.
The problem I think is that I can't bind dinamically this property.
I use the same binding with the same converter in the same page for an IsVisible property and it works.
If I put a breakpoint in the converter, the application doesn't break in it, but for the IsVisible property breaks.
Using a Button might not be the best option as it already has a tap handler and assigning one to it would still not trigger the event. I don't know if this helps but I changed the control to a Label to get the below to work. Of course if the Button is bound to a command that modifies the view model then you don't need the tap handler in the behaviour at all.
XAML:
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:local="clr-namespace:BehTest" x:Class="BehTest.BehTestPage">
<Label Text="AUTO" HorizontalOptions="Center" VerticalOptions="Center">
<Label.Behaviors>
<local:ToggleBehavior x:Name="autoToggleBehavior" IsToggled="{Binding Toggled, Mode=TwoWay}"/>
</Label.Behaviors>
<Label.Triggers>
<DataTrigger TargetType="Label" Binding="{Binding Source={x:Reference autoToggleBehavior},Path=IsToggled}" Value="False" >
<Setter Property="BackgroundColor" Value="White"/>
<Setter Property="TextColor" Value="Gray"/>
</DataTrigger>
<DataTrigger TargetType="Label" Binding="{Binding Source={x:Reference autoToggleBehavior},Path=IsToggled}" Value="True" >
<Setter Property="BackgroundColor" Value="Blue"/>
<Setter Property="TextColor" Value="White"/>
</DataTrigger>
</Label.Triggers>
</Label>
</ContentPage>
Behaviour:
public class ToggleBehavior : Behavior<View>
{
readonly TapGestureRecognizer tapRecognizer;
public ToggleBehavior()
{
tapRecognizer = new TapGestureRecognizer
{
Command = new Command(() => this.IsToggled = !this.IsToggled)
};
}
public static readonly BindableProperty IsToggledProperty = BindableProperty.Create<ToggleBehavior, bool>(tb => tb.IsToggled, false);
public bool IsToggled
{
set { SetValue(IsToggledProperty, value); }
get { return (bool)GetValue(IsToggledProperty); }
}
protected override void OnAttachedTo(View bindable)
{
base.OnAttachedTo(bindable);
bindable.GestureRecognizers.Add(this.tapRecognizer);
}
protected override void OnDetachingFrom(View bindable)
{
base.OnDetachingFrom(bindable);
bindable.GestureRecognizers.Remove(this.tapRecognizer);
}
protected override void OnAttachedTo(BindableObject bindable)
{
base.OnAttachedTo(bindable);
this.BindingContext = bindable.BindingContext;
bindable.BindingContextChanged += Bindable_BindingContextChanged;
}
protected override void OnDetachingFrom(BindableObject bindable)
{
base.OnDetachingFrom(bindable);
this.BindingContext = null;
bindable.BindingContextChanged -= Bindable_BindingContextChanged;
}
void Bindable_BindingContextChanged(object sender, EventArgs e)
{
var bobject = sender as BindableObject;
this.BindingContext = bobject?.BindingContext;
}
}
We added a ToggleButton into our NuGet!
Its free to use.
xmlns:aw="clr-namespace:AscendantWare.Xamarin.Essentials.Controls"
<aw:AWToggleButton TextColor="White" ToggleBackgroundColor="DarkGoldenrod" Margin="0" VerticalOptions="FillAndExpand" HorizontalOptions="Fill" CornerRadius="15" IsToggled="{Binding IsNoteEnabled, Mode=TwoWay}" Command="{Binding MyCommand}"/>
More Informations in our online documentation here!
I'm getting the following Binding errors on my code and I don't know how to troubleshoot them. The bindings were generated by VS. I've tried adding presentation.tracesources (which is in the code below) but I get the same output as before.
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='ClimateSolutions.SuperTB', AncestorLevel='1''. BindingExpression:Path=myName; DataItem=null; target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='ClimateSolutions.SuperTB', AncestorLevel='1''. BindingExpression:Path=isRequired; DataItem=null; target element is 'SuperTB' (Name='email'); target property is 'NoTarget' (type 'Object')
Here's my XAML:
<TextBox x:Class="ClimateSolutions.SuperTB"
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" Height="53" Width="296" FontSize="32"
xmlns:local="clr-namespace:ClimateSolutions"
xmlns:diagnostics="clr-namespace:System.Diagnostics;assembly=WindowsBase"
HorizontalAlignment="Left" Name="Blarg">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="Text" Value="">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<TextBlock Foreground="Gray" FontSize="24" Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=local:SuperTB, AncestorLevel=1}, Path=myName, diagnostics:PresentationTraceSources.TraceLevel=High}">
</TextBlock>
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</Trigger>
<DataTrigger Binding="{Binding Path=isRequired, RelativeSource={RelativeSource FindAncestor, AncestorType=local:SuperTB, AncestorLevel=1}}" Value="False">
<Setter Property="Text" Value="100" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
and here's the CS for SuperTB:
namespace ClimateSolutions
{
/// <summary>
/// Interaction logic for SuperTB.xaml
/// </summary>
public partial class SuperTB : TextBox, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String Property)
{
var anEvent = this.PropertyChanged;
if (anEvent != null)
{
anEvent(this, new PropertyChangedEventArgs(Property));
}
}
private String MyName = "Unicorns!";
private static DependencyProperty myNameProperty = DependencyProperty.Register("myName", typeof(String), typeof(SuperTB));
public String myName
{
get { return MyName; }
set { MyName = value; NotifyPropertyChanged("myName"); }
}
DependencyProperty isRequiredProperty = DependencyProperty.Register("isRequired", typeof(Boolean), typeof(SuperTB));
public Boolean isRequired
{
get { return (Boolean)GetValue(isRequiredProperty); }
set { SetValue(isRequiredProperty, value); }
}
public SuperTB()
{
InitializeComponent();
myName = "Unicorns!";
}
}
}
EDIT : I have updated the code according to your comment. To summarize, since this is a custom control, you are less dependant on the MVVM pattern to build your component logic (and thus use code behind in you component) as soon as your componennt itself meets this needs (to be sort, make its properties to be as much bindable as you can). For example, in the updated code, you can now bind the default property, but you can also imagine exposing properties to set the foreground colors used to diaplay control name when there is no value, and so forth.
I tried several things with you original code (included solution provided by J cooper) and nothing seemed to work. It seems that there is a lot of issues with your code.
I managed to approach a solution by making your textbox a custom control.
Here is the Generic.xaml (the visual definition of your control) :
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Question_6514447">
<Style TargetType="{x:Type local:SuperTB2}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:SuperTB2}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<TextBox x:Name="PART_Input">
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsRequired}" Value="False">
<Setter Property="Text" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=DefaultTextValue}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
And here is the code behind of the control :
[TemplatePart(Name = "PART_Input")]
public class SuperTB2 : Control
{
private TextBox PART_Input;
static SuperTB2()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(SuperTB2), new FrameworkPropertyMetadata(typeof(SuperTB2)));
}
public SuperTB2()
{
Loaded += SuperTb2Loaded;
}
public override void OnApplyTemplate()
{
PART_Input = GetTemplateChild("PART_Input") as TextBox;
if (PART_Input != null)
{
PART_Input.GotFocus += PartInputGotFocus;
PART_Input.LostFocus += PartInputLostFocus;
}
}
void PartInputLostFocus(object sender, RoutedEventArgs e)
{
if (PART_Input.Text == string.Empty)
{
PART_Input.Text = Name;
PART_Input.Foreground = new SolidColorBrush(Colors.Gray);
}
}
void PartInputGotFocus(object sender, RoutedEventArgs e)
{
if (PART_Input.Text.Equals(Name))
{
PART_Input.Text = string.Empty;
PART_Input.Foreground = new SolidColorBrush(Colors.Black);
}
}
void SuperTb2Loaded(object sender, RoutedEventArgs e)
{
if (PART_Input.Text == string.Empty)
{
PART_Input.Text = Name;
PART_Input.Foreground = new SolidColorBrush(Colors.Gray);
}
}
private static DependencyProperty myNameProperty =
DependencyProperty.Register("MyName", typeof(string), typeof(SuperTB2), new PropertyMetadata("Unicorns !", NameChanged));
private static void NameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public string MyName
{
get { return (string)GetValue(myNameProperty); }
set { SetValue(myNameProperty, value); }
}
DependencyProperty isRequiredProperty =
DependencyProperty.Register("IsRequired", typeof(bool), typeof(SuperTB2), new PropertyMetadata(false, IsReqChanged));
private static void IsReqChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
public bool IsRequired
{
get { return (bool)GetValue(isRequiredProperty); }
set { SetValue(isRequiredProperty, value); }
}
public string DefaultTextValue
{
get { return (string)GetValue(DefaultTextValueProperty); }
set { SetValue(DefaultTextValueProperty, value); }
}
// Using a DependencyProperty as the backing store for DefaultTextValue. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DefaultTextValueProperty =
DependencyProperty.Register("DefaultTextValue", typeof(string), typeof(SuperTB2), new UIPropertyMetadata("100"));
}
And an example of use of the component :
<Grid>
<StackPanel>
<Question_6514447:SuperTB2 x:Name="FirstName" IsRequired="true" DefaultTextValue="200"/>
</StackPanel>
</Grid>
With this updated code, I think you can acheive almost all the behaviors your needed !
Hope this will help !
Do not use relative source in your binding expressions. Relative source is used to access elements higher in the element tree. It seems as though you were using it in terms of object inheritance.
<Trigger Property="Text" Value="">
<Setter Property="Background">
<Setter.Value>
<VisualBrush Stretch="None">
<VisualBrush.Visual>
<TextBlock Foreground="Gray" FontSize="24" Text="{Binding Path=myName, diagnostics:PresentationTraceSources.TraceLevel=High}">
</TextBlock>
</VisualBrush.Visual>
</VisualBrush>
</Setter.Value>
</Setter>
</Trigger>
<DataTrigger Binding="{Binding Path=isRequired}" Value="False">
<Setter Property="Text" Value="100" />
</DataTrigger>