Here is a simple little behavior for updating a TextBox's Text property in Silverlight when the text changes instead of when it loses focus.
Quick and Easy. Gotta Love Behaviors.
Jacob
<ItemsControl
Margin="0 130 0 0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Opacity="0.0"
ItemsSource="{Binding Items}">
<i:Interaction.Triggers>
<local:BooleanPropertyTrigger
Binding="{Binding FinishedLoading}"
TriggerValue="True">
<local:StoryboardAction>
<Storyboard>
<DoubleAnimation
To="1.0"
Duration="00:00:0.7"
Storyboard.TargetProperty="Opacity" />
</Storyboard>
</local:StoryboardAction>
</local:BooleanPropertyTrigger>
<local:BooleanPropertyTrigger
Binding="{Binding FinishedLoading}"
TriggerValue="False">
<local:StoryboardAction>
<Storyboard>
<DoubleAnimation
To="0.0"
Duration="00:00:0.4"
Storyboard.TargetProperty="Opacity" />
</Storyboard>
</local:StoryboardAction>
</local:BooleanPropertyTrigger>
</i:Interaction.Triggers>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border
Height="30"
Width="200"
Margin="0 2"
BorderBrush="Plum"
BorderThickness="3"
CornerRadius="5">
<TextBlock
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="5 0 0 0"
Text="{Binding Name}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl
Margin="0 130 0 0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Opacity="0.0"
ItemsSource="{Binding Items}">
<i:Interaction.Behaviors>
<local:FadeyBehavior
Binding="{Binding FinishedLoading}" />
</i:Interaction.Behaviors>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border
Height="30"
Width="200"
Margin="0 2"
BorderBrush="Plum"
BorderThickness="3"
CornerRadius="5">
<TextBlock
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="5 0 0 0"
Text="{Binding Name}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
using System.Windows;
using System.Windows.Data;
using System.Windows.Interactivity;
/// <summary>
/// A behavior for fading an element based on whether a bound property is true or false.
/// </summary>
public class FadeyBehavior : Behavior<FrameworkElement>
{
/// <summary>
/// The <see cref="Binding" /> dependency property's name.
/// </summary>
public const string BindingPropertyName = "Binding";
/// <summary>
/// Gets or sets the value of the <see cref="Binding" />
/// property. This is a dependency property.
/// </summary>
public object Binding
{
get
{
return (object)GetValue(BindingProperty);
}
set
{
SetValue(BindingProperty, value);
}
}
/// <summary>
/// Identifies the <see cref="Binding" /> dependency property.
/// </summary>
public static readonly DependencyProperty BindingProperty = DependencyProperty.Register(
BindingPropertyName,
typeof(object),
typeof(FadeyBehavior),
new PropertyMetadata(null));
protected override void OnAttached()
{
if (AssociatedObject.Opacity != 0.0)
AssociatedObject.Opacity = 0.0;
// Create our fade in/out triggers
var triggerIn = new FadeInTrigger();
var triggerOut = new FadeOutTrigger();
// Bind the Binding property in this behavior to the underlying triggers.
var b = new Binding("Binding") { Source = this };
BindingOperations.SetBinding(triggerIn, BooleanPropertyTrigger.BindingProperty, b);
BindingOperations.SetBinding(triggerOut, BooleanPropertyTrigger.BindingProperty, b);
// Add our triggers to the associated object.
var currTriggers = Interaction.GetTriggers(AssociatedObject);
currTriggers.Add(triggerIn);
currTriggers.Add(triggerOut);
base.OnAttached();
}
protected override void OnDetaching()
{
base.OnDetaching();
}
}
<ItemsControl
Margin="0 130 0 0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Opacity="0.0"
ItemsSource="{Binding Items}">
<i:Interaction.Triggers>
<local:BooleanPropertyTrigger
Binding="{Binding FinishedLoading}"
TriggerValue="True">
<local:StoryboardAction>
<Storyboard>
<DoubleAnimation
To="1.0"
Duration="00:00:0.7"
Storyboard.TargetProperty="Opacity" />
</Storyboard>
</local:StoryboardAction>
</local:BooleanPropertyTrigger>
<local:BooleanPropertyTrigger
Binding="{Binding FinishedLoading}"
TriggerValue="False">
<local:StoryboardAction>
<Storyboard>
<DoubleAnimation
To="0.0"
Duration="00:00:0.4"
Storyboard.TargetProperty="Opacity" />
</Storyboard>
</local:StoryboardAction>
</local:BooleanPropertyTrigger>
</i:Interaction.Triggers>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border
Height="30"
Width="200"
Margin="0 2"
BorderBrush="Plum"
BorderThickness="3"
CornerRadius="5">
<TextBlock
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="5 0 0 0"
Text="{Binding Name}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
using System;
using System.Windows;
using System.Windows.Interactivity;
using System.Windows.Markup;
using System.Windows.Media.Animation;
[ContentProperty("Story")]
public class StoryboardAction : TriggerAction<FrameworkElement>
{
/// <summary>
/// Holder for a target setting state.
/// </summary>
private bool hasTargetSet = false;
/// <summary>
/// Gets or sets the storyboard for this action.
/// </summary>
/// <value>The storyboard to run when invoked.</value>
public Storyboard Story { get; set; }
/// <summary>
/// The <see cref="Target" /> dependency property's name.
/// </summary>
public const string TargetPropertyName = "Target";
/// <summary>
/// Gets or sets the value of the <see cref="Target" />
/// property. This is a dependency property.
/// You can leave this un-set if you want to use the Trigger's
/// AssociatedObject as the Target.
/// </summary>
public DependencyObject Target
{
get
{
return (DependencyObject)GetValue(TargetProperty);
}
set
{
SetValue(TargetProperty, value);
}
}
/// <summary>
/// Identifies the <see cref="Target" /> dependency property.
/// </summary>
public static readonly DependencyProperty TargetProperty = DependencyProperty.Register(
TargetPropertyName,
typeof(DependencyObject),
typeof(StoryboardAction),
new PropertyMetadata(null));
/// <summary>
/// Invokes the action.
/// </summary>
/// <param name="parameter">The parameter to the action. If the action does not require a parameter, the parameter may be set to a null reference.</param>
protected override void Invoke(object parameter)
{
if (this.Story == null)
return;
if(!hasTargetSet)
{
// Fall back to the associated object if no target defined.
var target = this.Target ?? AssociatedObject;
if (target != null)
{
Storyboard.SetTarget(this.Story, target);
hasTargetSet = true;
}
}
// Stop any previously ran storyboards.
if (this.Story.GetCurrentState() != ClockState.Stopped)
this.Story.Stop();
this.Story.Begin();
}
}
using System;
using System.Windows;
using System.Windows.Media.Animation;
public class FadeOutAction : FadeAction
{
public FadeOutAction()
: base(.2, 0.0)
{ }
}
public class FadeInAction : FadeAction
{
public FadeInAction()
: base(.6, 1.0)
{ }
}
public class FadeAction : StoryboardAction
{
public FadeAction(double durationSeconds = .6, double fadeTo = 0.0)
{
var anim = new DoubleAnimation { Duration = new Duration(TimeSpan.FromSeconds(durationSeconds)), To = fadeTo };
Storyboard.SetTargetProperty(anim, new PropertyPath("Opacity"));
this.Story = new Storyboard();
this.Story.Children.Add(anim);
}
}
<ItemsControl
Margin="0 130 0 0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Opacity="0.0"
ItemsSource="{Binding Items}">
<i:Interaction.Triggers>
<local:BooleanPropertyTrigger
Binding="{Binding FinishedLoading}"
TriggerValue="True">
<local:FadeInAction />
</local:BooleanPropertyTrigger>
<local:BooleanPropertyTrigger
Binding="{Binding FinishedLoading}"
TriggerValue="False">
<local:FadeOutAction />
</local:BooleanPropertyTrigger>
</i:Interaction.Triggers>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border
Height="30"
Width="200"
Margin="0 2"
BorderBrush="Plum"
BorderThickness="3"
CornerRadius="5">
<TextBlock
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="5 0 0 0"
Text="{Binding Name}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl
Margin="0 130 0 0"
HorizontalAlignment="Left"
VerticalAlignment="Top"
Opacity="0.0"
ItemsSource="{Binding Items}">
<i:Interaction.Triggers>
<local:BooleanPropertyTrigger
Binding="{Binding FinishedLoading}"
TriggerValue="True">
<local:StoryboardAction>
<Storyboard>
<DoubleAnimation
To="1.0"
Duration="00:00:0.7"
Storyboard.TargetProperty="Opacity" />
</Storyboard>
</local:StoryboardAction>
</local:BooleanPropertyTrigger>
</i:Interaction.Triggers>
</ItemsControl>
/// <summary>
/// A base property changed trigger that
/// fires whenever the bound property changes.
/// </summary>
public class PropertyChangedTrigger : TriggerBase<FrameworkElement>
{
/// <summary>
/// The <see cref="Binding" /> dependency property's name.
/// </summary>
public const string BindingPropertyName = "Binding";
/// <summary>
/// Gets or sets the value of the <see cref="Binding" />
/// property. This is a dependency property.
/// </summary>
public object Binding
{
get
{
return (object)GetValue(BindingProperty);
}
set
{
SetValue(BindingProperty, value);
}
}
/// <summary>
/// Identifies the <see cref="Binding" /> dependency property.
/// </summary>
public static readonly DependencyProperty BindingProperty =
DependencyProperty.Register(
BindingPropertyName,
typeof(object),
typeof(PropertyChangedTrigger),
new PropertyMetadata(null,
new PropertyChangedCallback(Binding_ValueChanged)));
/// <summary>
/// Called after the trigger is attached to an AssociatedObject.
/// </summary>
protected override void OnAttached()
{
base.OnAttached();
}
/// <summary>
/// Called when the trigger is being detached
/// from its AssociatedObject,
/// but before it has actually occurred.
/// </summary>
protected override void OnDetaching()
{
base.OnDetaching();
}
/// <summary>
/// Occurs when Binding's value changes.
/// </summary>
/// <param name="obj">The obj on which the binding changed.</param>
/// <param name="args">The <see cref="System.Windows.DependencyPropertyChangedEventArgs"/> instance containing the event data.</param>
private static void Binding_ValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var trig = obj as PropertyChangedTrigger;
if (trig != null && trig.ShouldTriggerFire(args.NewValue))
{
trig.OnPropertyTrigger(args.NewValue);
}
}
/// <summary>
/// Does the change logic test. By default, it will always fire on value change.
/// </summary>
/// <param name="newValue">The new value.</param>
/// <returns>True if the trigger should fire, otherwise false.</returns>
protected virtual bool ShouldTriggerFire(object newValue)
{
return true;
}
/// <summary>
/// Called when [property trigger].
/// </summary>
/// <param name="value">The value of the property.</param>
protected virtual void OnPropertyTrigger(object value)
{
base.InvokeActions(value);
}
}
/// <summary>
/// A base class for property triggers that must be equal to fire.
/// </summary>
/// <typeparam name="TValue">The type of the trigger value.</typeparam>
/// <summary>
/// A base class for property triggers that must be equal to fire.
/// </summary>
/// <typeparam name="TValue">The type of the trigger value.</typeparam>
public class EqualsPropertyTrigger<TValue> : PropertyChangedTrigger
{
/// <summary>
/// Gets or sets the trigger value to match the property value for.
/// </summary>
/// <value>The trigger value.</value>
public TValue TriggerValue { get; set; }
/// <summary>
/// Logic to check whether the trigger should fire.
/// </summary>
/// <param name="newValue">The new value.</param>
/// <returns>True if the trigger should fire, otherwise false.</returns>
protected override bool ShouldTriggerFire(object newValue)
{
if (newValue == null)
return this.TriggerValue == null;
return newValue.Equals(this.TriggerValue);
}
}
/// <summary>
/// A generic object property trigger
/// </summary>
public class PropertyTrigger : EqualsPropertyTrigger<object>
{ }
/// <summary>
/// A Boolean value property trigger
/// </summary>
public class BooleanPropertyTrigger : EqualsPropertyTrigger<bool>
{ }
/// <summary>
/// A string property value trigger
/// </summary>
public class StringPropertyTrigger : EqualsPropertyTrigger<string>
{ }
<clr:ClearableTextBox
Text="{Binding SomeText, Mode=TwoWay}"
ClearButtonContent="Clear"
VerticalAlignment="Top"/>
<!-- Put inside your new UserControl -->
<Grid x:Name="LayoutRoot">
<TextBox
MinWidth="50"
Text="{Binding ElementName=uiThis, Path=Text, Mode=TwoWay}"
x:Name="editBox"/>
<Button
MinWidth="30"
HorizontalAlignment="Right"
VerticalAlignment="Center"
Click="clrBtn_Click"
x:Name="clrBtn">
<Button.Template>
<ControlTemplate>
<TextBlock
FontSize="25"
Margin="0 0 20 0"
Foreground="Gray"
FontWeight="Bold"
Text="X"/>
</ControlTemplate>
</Button.Template>
</Button>
</Grid>
// Put inside your code behind for your new UserControl.
public partial class ClearableTextBox : UserControl, INotifyPropertyChanged
{
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
// Using a DependencyProperty as the backing store for EditText. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(ClearableTextBox), new PropertyMetadata(string.Empty, HandleTextChanged));
public ClearableTextBox()
{
InitializeComponent();
}
private static void HandleTextChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
var box = obj as ClearableTextBox;
if (null != box)
box.OnPropertyChanged("Text");
}
private void clrBtn_Click(object sender, RoutedEventArgs e)
{
Text = string.Empty;
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string name)
{
if (null != PropertyChanged)
PropertyChanged(null, new PropertyChangedEventArgs(name));
}
#endregion
}