I have the same question. The advantage of a component-like mechanism is that the designer can add it in Blend, configure it in the designer with the Properties editor, and use data binding. What do you think of the solution below? It works.
public class TimerComponent : FrameworkElement
{
public Timer Timer { get; protected set; }
public TimerComponent()
{
if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
{
Visibility = Visibility.Collapsed;
Timer = new Timer(OnTimerTick, null, Timeout.Infinite, Timeout.Infinite);
}
}
void OnTimerTick(object ignore)
{
Dispatcher.BeginInvoke(new Action(RaiseTickEvent));
}
#region DueTime Dependency Property
public int DueTime
{
get { return (int)GetValue(DueTimeProperty); }
set { SetValue(DueTimeProperty, value); }
}
public static readonly DependencyProperty DueTimeProperty =
DependencyProperty.Register("DueTime", typeof(int), typeof(TimerComponent), new UIPropertyMetadata(new PropertyChangedCallback(OnDueTimeChanged)));
static void OnDueTimeChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var target = obj as TimerComponent;
if (target.Timer != null)
{
var newDueTime = (int)e.NewValue;
target.Timer.Change(newDueTime, target.Period);
}
}
#endregion
#region Period Dependency Property
public int Period
{
get { return (int)GetValue(PeriodProperty); }
set { SetValue(PeriodProperty, value); }
}
public static readonly DependencyProperty PeriodProperty =
DependencyProperty.Register("Period", typeof(int), typeof(TimerComponent), new UIPropertyMetadata(new PropertyChangedCallback(OnPeriodChanged)));
static void OnPeriodChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
var target = obj as TimerComponent;
if (target.Timer != null)
{
var newPeriod = (int)e.NewValue;
target.Timer.Change(target.DueTime, newPeriod);
}
}
#endregion
#region Tick Routed Event
public static readonly RoutedEvent TickEvent = EventManager.RegisterRoutedEvent(
"Tick", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(TimerComponent));
public event RoutedEventHandler Tick
{
add { AddHandler(TickEvent, value); }
remove { RemoveHandler(TickEvent, value); }
}
private void RaiseTickEvent()
{
RoutedEventArgs newEventArgs = new RoutedEventArgs(TimerComponent.TickEvent);
RaiseEvent(newEventArgs);
}
#endregion
}
And is used as follows.
<StackPanel>
<lib:TimerComponent Period="{Binding ElementName=textBox1, Path=Text}" Tick="OnTimerTick" />
<TextBox x:Name="textBox1" Text="1000" />
<Label x:Name="label1" />
</StackPanel>