可能有多种方法可以做到这一点,您的实际应用中的其他因素可能会影响我在下面概述的方法是否适合您的应用。
指示状态变化
首先,您需要通过某种方式来提醒 UI Sample 的状态变化,它会在范围内一段时间,然后会超出范围。您可以将此状态作为Sample 类型中的属性公开。您将通过实现INotifyPropertyChanged 接口来通知 UI。这是您的班级在实现 INotifyPropertyChanged 后的样子:-
public class TimedSample : INotifyPropertyChanged
{
private string _Value;
public string Value
{
get { return _Value; }
set
{
_Value = value;
NotifyPropertyChanged("Value");
}
}
private DateTime _Begin;
public DateTime Begin
{
get { return _Begin; }
set
{
_Begin = value;
NotifyPropertyChanged("Begin");
}
}
private DateTime _End;
public DateTime End
{
get { return _End; }
set
{
_End = value;
NotifyPropertyChanged("End");
}
}
private bool _NowInRange;
public bool NowInRange
{
get { return _NowInRange; }
private set
{
_NowInRange = value;
NotifyPropertyChanged("NowInRange");
}
}
private void NotifyPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
public event PropertyChangedEventHandler PropertyChanged;
}
当当前时间在Begin 和End 范围内时,TimeSample 内部的某些代码会使NowInRange 属性的值变为真。 (我会回到那个)。
将布尔值转换为画笔
下一个问题是您要更改项目的颜色。因此,我们想将TextBlock 的Foreground 属性绑定到TimedSample 的NowInRange 属性。所以我们需要一个IValueConverter:-
public class BoolToBrushConverter : IValueConverter
{
public Brush FalseBrush { get; set; }
public Brush TrueBrush { get; set; }
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return FalseBrush;
else
return (bool)value ? TrueBrush : FalseBrush;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException("This converter only works for one way binding");
}
}
一些 XAML 组合起来
现在我们只需要将这个转换器放在一个资源字典中,我们就可以将它全部连接起来。
下面的 Xaml 假定将 TimedSample 对象列表分配给 Usercontrol 的 DataContext 属性。
<UserControl x:Class="SilverlightApplication1.ListBoxStuff"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SilverlightApplication1"
>
<Grid x:Name="LayoutRoot" Background="White">
<Grid.Resources>
<local:BoolToBrushConverter x:Key="BoolToYellowAndRed" TrueBrush="Yellow" FalseBrush="Red" />
</Grid.Resources>
<ListBox ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Value}"
Foreground="{Binding NowInRange, Converter={StaticResource BoolToYellowAndRed}}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</UserControl>
让它打勾
现在需要一些机制来使NowInRange 属性在适当的时间点翻转其值。同样,可能有几种方法可以做到这一点。我将使用基于DispatcherTimer 的非常通用的解决方案。在这种情况下,我们将静态保存的DispatcherTimer 实例添加到TimedSample 类。它可能看起来像这样:-
static readonly DispatcherTimer timer;
static TimedSample()
{
timer = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(1) };
timer.Start();
}
public TimedSample()
{
// Do not actually do this!
timer.Tick += timer_Tick;
}
private void timer_Tick(object sender, EventArgs e)
{
DateTime now = DateTime.Now;
if (NowInRange != (Begin < now && now < End))
NowInRange = !NowInRange;
}
这可以正常工作,但有一个问题。它会泄漏内存,一旦 TimedSample 被实例化,它将永远不会被 GC 释放和收集。它将永远被计时器的 Tick 事件引用,更糟糕的是,它会继续执行 timer_Tick 中的代码,尽管没有在其他任何地方使用。
Silverlight 工具包以WeakEventListener 类的形式提供了巧妙的解决方案。 Beat Kiener 关于它的博客,并在 Simple Weak Event Listener for Silverlight 中包含它的代码。有了它,TimedSample 构造函数看起来像这样:-
public TimedSample()
{
var weakListener = new WeakEventListener<TimedSample, DispatcherTimer, EventArgs>(this, timer);
timer.Tick += weakListener.OnEvent;
weakListener.OnEventAction = (instance, source, e) => instance.timer_Tick(source, e);
weakListener.OnDetachAction = (listener, source) => timer.Tick -= listener.OnEvent;
}
当 UI 或任何其他地方不再引用 TimedSample 时,GC 可以收集它。当下一个 Tick 事件触发时,WeakEventListener 检测到对象已消失并调用 OnDetachAction 使 WeakEventListener 的实例本身也可用于垃圾回收。
我已经开始了,所以我会完成
这个答案已经很长了,对此感到抱歉,但既然如此,我不妨给你我用于上面列出的 Xaml 的测试代码隐藏:-
public partial class ListBoxStuff : UserControl
{
public ListBoxStuff()
{
InitializeComponent();
DataContext = GetTimedSamples(10, TimeSpan.FromSeconds(5));
}
IEnumerable<TimedSample> GetTimedSamples(int count, TimeSpan interval)
{
TimedSample sample = null;
for (int i = 0; i < count; i++)
{
sample = new TimedSample()
{
Value = String.Format("Item{0}", i),
Begin = sample != null ? sample.End : DateTime.Now,
End = (sample != null ? sample.End : DateTime.Now) + interval
};
yield return sample;
}
}
}