通过WPF模拟交通红绿灯(图文教程)
先看一下效果图:
第一步:新建一个WPF应用程序,这一步的操作就这里省略了。
第二步:在刚才新建的WPF应用程序中添加一个UserControl命名为:TrafficLightControl,如下图所示
关键代码如下,可以直接拷贝到VS2010即可:
1: <UserControl x:Class="WPF绑定转换器.TrafficLightControl"
2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5: xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
6: xmlns:myConverter="clr-namespace:WPF绑定转换器"
7: Width="120" Height="350">
8: <UserControl.Resources>
9: <myConverter:ColorConverter x:Key="colorConverter" />
10: </UserControl.Resources>
11: <Grid>
12: <Border Background="Black" CornerRadius="10" BorderBrush="Gray" BorderThickness="2">
13: <StackPanel VerticalAlignment="Center">
14: <StackPanel.Resources>
15: <Style TargetType="{x:Type Ellipse}">
16: <Setter Property="Width" Value="100"></Setter>
17: <Setter Property="Height" Value="100"></Setter>
18: <Setter Property="Fill" Value="LightGray"></Setter>
19: <Setter Property="Stroke" Value="Gray"></Setter>
20: <Setter Property="StrokeThickness" Value="2"></Setter>
21: <Setter Property="Margin" Value="4"></Setter>
22: </Style>
23: </StackPanel.Resources>
24: <Ellipse Fill="{Binding State,Converter={StaticResource colorConverter},
ConverterParameter=RED}"/>
25: <Ellipse Fill="{Binding State,Converter={StaticResource colorConverter},
ConverterParameter=YELLOW}"/>
26: <Ellipse Fill="{Binding State,Converter={StaticResource colorConverter},
ConverterParameter=GREEN}"/>
27: </StackPanel>
28: </Border>
29: </Grid>
30: </UserControl>
代码解释:为了模拟红绿灯,代码24,25,26行分别创建了3个椭圆,分别设置椭圆的Fill属性,每个椭圆的Fill属性通过绑定的一个状态枚举State{GREEN,YELLOW,RED},Fill属性值实际是一个Brush实例,由于这里用到了枚举值,所以这里同时又创建了一个Converter,在第6行是具体引入这个转换器的方法,下面贴出这个转换器的代码:
1: [ValueConversion(typeof(TrafficLight.States), typeof(Brush))]
2: public class ColorConverter : IValueConverter
3: {
4: public enum Lights
5: {
6: GREEN,
7: YELLOW,
8: RED
9: }
10: public object Convert(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
11: {
12: TrafficLight.States state = (TrafficLight.States)value;
13: Lights light = (Lights)Enum.Parse(typeof(Lights), (string)parameter);
14: switch (state)
15: {
16: case TrafficLight.States.GREEN:
17: if (light == Lights.GREEN)
18: {
19: return new SolidColorBrush(Colors.Green);
20: }
21: break;
22: case TrafficLight.States.YELLOW:
23: if (light == Lights.YELLOW)
24: {
25: return new SolidColorBrush(Colors.Yellow);
26: }
27: break;
28: case TrafficLight.States.RED:
29: if (light == Lights.RED)
30: {
31: return new SolidColorBrush(Colors.Red);
32: }
33: break;
34: }
35: return new SolidColorBrush(Colors.LightGray);
36: }
37:
38: public object ConvertBack(object value, Type targetType, object parameter,
System.Globalization.CultureInfo culture)
39: {
40: return null;
41: }
42: }
代码解释:由于上面创建椭圆的Fill属性值就是一个Color实例,所以这里的转换器实现的基本功能就是,根据传来的枚举状态值(GREEN,RED,YELLOW),返回相应的色彩画刷(new SolidColorBrush(Colors.颜色)),实现了转换器之后,下面继续实现状态枚举State,代码如下:
1: public class TrafficLight : INotifyPropertyChanged
2: {
3: public event PropertyChangedEventHandler PropertyChanged;
4:
5: private States _state;
6:
7: public enum States
8: {
9: GREEN,
10: YELLOW,
11: RED
12: }
13:
14: public States State
15: {
16: get
17: {
18: return _state;
19: }
20: set
21: {
22: if (value != _state)
23: {
24: _state = value;
25: if (PropertyChanged != null)
26: {
27: PropertyChanged(this,new PropertyChangedEventArgs("State"));
28: }
29: }
30: }
31: }
32: }
代码解释:状态枚举的定义非常简单,这里不做解释了,拷贝代码只可。
第三步:实现了第二步操作,第三步主要工作是,在项目里添加一个WPF窗口来容纳第二步创建的用户控件(TrafficLightControl)
1、页面代码,Xaml代码如下:
1: <Window x:Class="WPF绑定转换器.MainWindow"
2: xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3: xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4: xmlns:my="clr-namespace:WPF绑定转换器"
5: Title="MainWindow" Height="512" Width="400" Name="mainWnd" AllowsTransparency="True"
6: WindowStyle="None" WindowStartupLocation="CenterScreen"
MouseLeftButtonDown="mainWnd_MouseLeftButtonDown">
7: <Window.Resources>
8: <my:TrafficLight State="Red" x:Key="myTrafficLight"/>
9: <Style BasedOn="{x:Null}" TargetType="{x:Type Button}">
10: <Setter Property="Template">
11: <Setter.Value>
12: <ControlTemplate TargetType="{x:Type Button}">
13: <Grid x:Name="buttonClose">
14: <Ellipse Stroke="{x:Null}" StrokeThickness="1" x:Name="btnEllipse" >
15: <Ellipse.Fill>
16: <RadialGradientBrush>
17: <GradientStop Color="#BFABA7A4" Offset="0.777"/>
18: <GradientStop Color="#FF897F77" Offset="1"/>
19: </RadialGradientBrush>
20: </Ellipse.Fill>
21: </Ellipse>
22:
23:
24: <ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True"/>
25:</Grid>
26: <ControlTemplate.Triggers>
27: <Trigger Property="IsFocused" Value="True"/>
28: <Trigger Property="IsDefaulted" Value="True"/>
29: <Trigger Property="IsMouseOver" Value="True">
30: <Setter Property="Fill" Value="#FF5D4E3E" TargetName="btnEllipse"/>
31: </Trigger>
32: <Trigger Property="IsPressed" Value="True"/>
33: <Trigger Property="IsEnabled" Value="False"/>
34: </ControlTemplate.Triggers>
35: </ControlTemplate>
36: </Setter.Value>
37: </Setter>
38: </Style>
39: </Window.Resources>
40:
41: <Border BorderBrush="LightGray" BorderThickness="5">
42: <Grid Background="AliceBlue">
43: <Grid.RowDefinitions>
44: <RowDefinition Height="30"></RowDefinition>
45: <RowDefinition Height="30"></RowDefinition>
46: <RowDefinition Height="360"></RowDefinition>
47: <RowDefinition Height="*"></RowDefinition>
48: </Grid.RowDefinitions>
49: <Grid Grid.Row="0">
50: <Grid.ColumnDefinitions>
51: <ColumnDefinition Width="260"></ColumnDefinition>
52: <ColumnDefinition Width="100"></ColumnDefinition>
53: <ColumnDefinition Width="*"></ColumnDefinition>
54: </Grid.ColumnDefinitions>
55: <TextBlock Grid.Column="0" HorizontalAlignment="Right"
VerticalAlignment="Center" FontFamily="Arial" FontSize="20" Text="模拟红绿灯实验">
</TextBlock>
56: <Button Name="btnClose" Grid.Column="1" HorizontalAlignment="Right"
VerticalAlignment="Center" Content="X" Width="16" Height="16" Click="btnClose_Click"></Button>
57: </Grid>
58: <Grid Grid.Row="1">
59: <Grid.ColumnDefinitions>
60: <ColumnDefinition Width="200"></ColumnDefinition>
61: <ColumnDefinition Width="*"></ColumnDefinition>
62: </Grid.ColumnDefinitions>
63: <Ellipse Name="flagEllipse" HorizontalAlignment="Right"
VerticalAlignment="Center" Grid.Column="0" Fill="Red" Width="18" Height="18"></Ellipse>
64: <Label Name="lblMessage" HorizontalAlignment="Left" VerticalAlignment="Center"
Grid.Column="1" Height="27"></Label>
65: </Grid>
66: my:TrafficLightControl Grid.Row="2" DataContext="{StaticResource myTrafficLight}"/>
67: <StackPanel Orientation="Horizontal" Grid.Row="3" HorizontalAlignment="Center" Height="30">
68: <Button Content="开始模拟" Width="100" Margin="2" Click="ButtonClicked" Name="btnStart"/>
69: <Button Content="停止模拟" Width="100" Margin="2" Click="ButtonClicked" Name="btnStop"/>
70: </StackPanel>
71: </Grid>
72: </Border>
73: </Window>
2、后台代码,cs代码:
1: /// <summary>
2: /// Interaction logic for MainWindow.xaml
3: /// </summary>
4: public partial class MainWindow : Window
5: {
6: BackgroundWorker _backgroundWorker = new BackgroundWorker();
7: DispatcherTimer dispatcherTimer = new DispatcherTimer();
8: public MainWindow()
9: {
10: InitializeComponent();
11: _backgroundWorker.DoWork += new DoWorkEventHandler(_backgroundWorker_DoWork);
12: _backgroundWorker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(_backgroundWorker_RunWorkerCompleted);
13:
14: dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
15: dispatcherTimer.Interval = TimeSpan.FromMilliseconds(200);
16: }
17:
18: void _backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
19: {
20: if (e.Cancelled)
21: {
22: lblMessage.Content = "终止";
23: }
24: else if (e.Error != null)
25: {
26: lblMessage.Content = "异常";
27: }
28: else
29: {
30: lblMessage.Content = "运行";
31: }
32: }
33:
34: void _backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
35: {
36: TrafficLight tLight = (TrafficLight)this.FindResource("myTrafficLight");
37: if (tLight.State == TrafficLight.States.GREEN)
38: {
39: tLight.State = TrafficLight.States.RED;
40: Thread.Sleep(TimeSpan.FromSeconds(5));
41: }
42: else if (tLight.State == TrafficLight.States.RED)
43: {
44: tLight.State = TrafficLight.States.YELLOW;
45: Thread.Sleep(TimeSpan.FromSeconds(2));
46: }
47: else if (tLight.State == TrafficLight.States.YELLOW)
48: {
49: tLight.State = TrafficLight.States.GREEN;
50: Thread.Sleep(TimeSpan.FromSeconds(5));
51: }
52: }
53:
54: void dispatcherTimer_Tick(object sender, EventArgs e)
55: {
56: if (!_backgroundWorker.IsBusy)
57: {
58: _backgroundWorker.RunWorkerAsync();
59: }
60: }
61:
62: private void ButtonClicked(object sender, RoutedEventArgs e)
63: {
64: if (sender == btnStart)
65: {
66: dispatcherTimer.Start();
67: flagEllipse.Fill = Brushes.Red;
68: DoubleAnimation da =
new DoubleAnimation(1, 0, new Duration(TimeSpan.FromSeconds(0.2)));
69: da.AutoReverse = true;
70: da.RepeatBehavior = RepeatBehavior.Forever;
71: flagEllipse.BeginAnimation(Ellipse.OpacityProperty, da);
72: }
73: else if (sender == btnStop)
74: {
75: dispatcherTimer.Stop();
76: lblMessage.Content = "中止";
77: flagEllipse.Fill = Brushes.Black;
78: }
79: }
80:
81: private void mainWnd_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
82: {
83: this.DragMove();
84: }
85:
86: private void btnClose_Click(object sender, RoutedEventArgs e)
87: {
88: this.Close();
89: }
90: }
代码解释:拷贝相应代码即可,为了解决界面更新容易假死的情况,这里利用了异步多线程模型BackgroundWorker,对BackgroundWorker模型用法不清楚的可以BaiDu一下,到处都是。
总结:事实上为了模拟红绿灯效果,并不是一步到位的,比如我开始是利用三个按钮(红灯、黄灯、绿灯),并添加相应的Click事件在运行时改变State属性值,之后改成通过DispatcherTimer 调度时钟,自动开启BackgroundWorker异步模型,并每隔一段时间查询模型运行状态,也就是模拟红红绿灯的运行状态。
第四步:一下Demo下载地址
下载:交通红绿灯