需求
在物联网应用中,可视化端经常需要将实物信息详细的呈现到用户视野之中。在室内环境中,经常可见的设备空调和灯。本次课题主要以室内空调和灯监控出发,实现室内监控信息可视化。为了让可视化更加直观,我们需要完成的任务有:
1.用户操作开关之后,界面要同步运行状态。
2.美化界面,让信息状态变化直观的投射给用户。
首先上效果:
环境
Windows 10
Visual Studio 2019
.Net Framework 4.7.2
设计
UI设计:
功能设计:
当打开灯时:灯会亮起来(白色),关闭时暗下去(灰色)。
当打开空调时:空调量绿灯运行,同时吹风;关闭时绿灯消失,风停止。
通过属性绑定和值转换器实现以上功能。
实现
1.自定义抽象值转换器
using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Markup;
namespace Deamon.ValueConverters
{
public abstract class BaseValueConverter<T> : MarkupExtension, IValueConverter
where T : class, new()
{
private static T Converter = null;
public override object ProvideValue(IServiceProvider serviceProvider)
{
return Converter ?? (Converter = new T());
}
public abstract object Convert(object value, Type targetType, object parameter, CultureInfo culture);
public abstract object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture);
}
}
2.实现一个将Boolean值转换成Visibility值得转换器
using System;
using System.Globalization;
using System.Windows;
namespace Deamon.ValueConverters
{
/// <summary>
/// 一个转换器,将一个布尔值转换成 <see cref="Visibility"/> 类型
/// 如果带有参数 parameter 则是 True 表示显示,FALSE 表示隐藏
/// 如果不带参数 parameter 则是 True 表示隐藏,FALSE 表示显示
///
/// </summary>
public class BooleanToVisibilityConverter : BaseValueConverter<BooleanToVisibilityConverter>
{
public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (parameter == null)
return (bool)value ? Visibility.Hidden : Visibility.Visible;
else
return (bool)value ? Visibility.Visible : Visibility.Hidden;
}
public override object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
3.设计XAML实现UI,使用元素绑定和值转换器实现界面刷新
<Window x:Class="Deamon.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Deamon"
xmlns:converter="clr-namespace:Deamon.ValueConverters"
mc:Ignorable="d"
Title="房间信息实时监控" Height="450" Width="800">
<Grid Background="#FF423E3E">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!-- 室内信息 -->
<Grid Grid.Column="1">
<Border Width="400" Height="400" BorderThickness="1" BorderBrush="Black" Background="#FF063463">
<Grid>
<!-- 房间的边缘 -->
<Path Stroke="Black" StrokeThickness="2" Data="M1,1 1,50"/>
<Path Stroke="Black" StrokeThickness="20" Data="M10,160 10,240"/>
<Path Stroke="#FFB0ADAD" StrokeThickness="3" Data="M398,10 398,180"/>
<Path Stroke="#FFB0ADAD" StrokeThickness="3" Data="M398,220 398,390"/>
<!-- 默认静态灯 -->
<Border Padding="10" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border x:Name="StaticLamb1" Background="Gray" Grid.Row="0" Grid.Column="0" BorderBrush="Black" BorderThickness="2" Width="30" Height="100"/>
<Border x:Name="StaticLamb2" Background="Gray" Grid.Row="0" Grid.Column="1" BorderBrush="Black" BorderThickness="2" Width="30" Height="100"/>
<Border x:Name="StaticLamb3" Background="Gray" Grid.Row="1" Grid.Column="0" BorderBrush="Black" BorderThickness="2" Width="30" Height="100"/>
<Border x:Name="StaticLamb4" Background="Gray" Grid.Row="1" Grid.Column="1" BorderBrush="Black" BorderThickness="2" Width="30" Height="100"/>
</Grid>
</Border>
<!-- 点亮的灯 -->
<Border Padding="10" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Grid.Row="0" Grid.Column="0"
Visibility="{Binding ElementName=ControlLamp12, Path=IsChecked,Converter={converter:BooleanToVisibilityConverter}, ConverterParameter=True}"
Opacity="1">
<Border.Background>
<RadialGradientBrush Center="0.7 0.5">
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="#FF024391" Offset="1"/>
</RadialGradientBrush>
</Border.Background>
<Border x:Name="Lamb1"
Background="White" BorderBrush="Black" BorderThickness="2" Width="30" Height="100">
<Border.Effect>
<DropShadowEffect BlurRadius="50" Opacity="1" ShadowDepth="0" Color="White"/>
</Border.Effect>
</Border>
</Border>
<Border Grid.Row="0" Grid.Column="1"
Visibility="{Binding ElementName=ControlLamp12, Path=IsChecked,Converter={converter:BooleanToVisibilityConverter}, ConverterParameter=True}"
Opacity="1">
<Border.Background>
<RadialGradientBrush Center="0.3 0.5">
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="#FF024391" Offset="1"/>
</RadialGradientBrush>
</Border.Background>
<Border x:Name="Lamb2"
Background="White" BorderBrush="Black" BorderThickness="2" Width="30" Height="100">
<Border.Effect>
<DropShadowEffect BlurRadius="50" Opacity="1" ShadowDepth="0" Color="White"/>
</Border.Effect>
</Border>
</Border>
<Border Grid.Row="1" Grid.Column="0"
Visibility="{Binding ElementName=ControlLamp34, Path=IsChecked,Converter={converter:BooleanToVisibilityConverter}, ConverterParameter=True}"
Opacity="1">
<Border.Background>
<RadialGradientBrush>
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="#FF024391" Offset="1"/>
</RadialGradientBrush>
</Border.Background>
<Border x:Name="Lamb3"
Background="White" BorderBrush="Black" BorderThickness="2" Width="30" Height="100">
<Border.Effect>
<DropShadowEffect BlurRadius="50" Opacity="1" ShadowDepth="0" Color="White"/>
</Border.Effect>
</Border>
</Border>
<Border Grid.Row="1" Grid.Column="1"
Visibility="{Binding ElementName=ControlLamp34, Path=IsChecked,Converter={converter:BooleanToVisibilityConverter}, ConverterParameter=True}"
Opacity="1">
<Border.Background>
<RadialGradientBrush>
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="#FF024391" Offset="1"/>
</RadialGradientBrush>
</Border.Background>
<Border x:Name="Lamb4"
Background="White" BorderBrush="Black" BorderThickness="2" Width="30" Height="100">
<Border.Effect>
<DropShadowEffect BlurRadius="50" Opacity="1" ShadowDepth="0" Color="White"/>
</Border.Effect>
</Border>
</Border>
</Grid>
</Border>
<!-- 空调 -->
<Grid Width="60" Height="100" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="20 40 0 0" >
<Grid Width="40" HorizontalAlignment="Right" Opacity="1"
Visibility="{Binding ElementName=ControlAirCondition,Path=IsChecked,Converter={converter:BooleanToVisibilityConverter},ConverterParameter=True}"
Margin="0 0 0 0">
<StackPanel x:Name="AirConditionWind" Width="40" VerticalAlignment="Center" Margin="0 0 0 0">
<StackPanel.Triggers>
<EventTrigger RoutedEvent="Loaded">
<BeginStoryboard>
<Storyboard>
<ThicknessAnimation Duration="00:00:04" RepeatBehavior="Forever"
Storyboard.TargetProperty="Margin"
Storyboard.TargetName="AirConditionWind"
To="20 0 0 0" From="0 0 0 0"/>
</Storyboard>
</BeginStoryboard>
</EventTrigger>
</StackPanel.Triggers>
<Viewbox Height="20">
<Path Stroke="White" StrokeThickness="10"
Data="M 10,100 C 100,0 200,200 300,100 M 300,100 C 400,0 500,200 600,100" />
</Viewbox>
<Viewbox Height="20">
<Path Stroke="Red" StrokeThickness="10"
Data="M 10,100 C 100,0 200,200 300,100 M 300,100 C 400,0 500,200 600,100" />
</Viewbox>
<Viewbox Height="20">
<Path Stroke="White" StrokeThickness="10"
Data="M 10,100 C 100,0 200,200 300,100 M 300,100 C 400,0 500,200 600,100" />
</Viewbox>
<Viewbox Height="20">
<Path Stroke="White" StrokeThickness="10"
Data="M 10,100 C 100,0 200,200 300,100 M 300,100 C 400,0 500,200 600,100" />
</Viewbox>
</StackPanel>
</Grid>
<Border Visibility="Visible" HorizontalAlignment="Left" Width="40" Height="100" BorderBrush="Black" BorderThickness="1">
<Border.Background>
<LinearGradientBrush EndPoint="1,1" StartPoint="0,1">
<GradientStop Color="#FF072442" Offset="0"/>
<GradientStop Color="#FF213D5B" Offset="0.8"/>
<GradientStop Color="#FF486683" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
<Grid>
<Border BorderBrush="Black" BorderThickness="1" Margin="2" Width="4" HorizontalAlignment="Right"/>
<Rectangle Fill="Lime"
Width="15"
Visibility="{Binding ElementName=ControlAirCondition,Path=IsChecked,Converter={converter:BooleanToVisibilityConverter},ConverterParameter=True}"
Height="15"
/>
</Grid>
</Border>
</Grid>
</Grid>
</Border>
</Grid>
<!-- 显示控制板 -->
<Border Grid.Column="2">
<StackPanel VerticalAlignment="Center">
<Border BorderBrush="AliceBlue" BorderThickness="1" Width="230" Margin="10" Padding="10" >
<StackPanel>
<Grid Margin="0 0 0 5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="20"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Foreground="White" Text="一二号灯状态:" HorizontalAlignment="Right"/>
<TextBlock Grid.Column="1"
Visibility="{Binding ElementName=ControlLamp12,Path=IsChecked,Converter={converter:BooleanToVisibilityConverter}}"
Foreground="Orange"
Text="关闭"/>
<TextBlock Grid.Column="1"
Visibility="{Binding ElementName=ControlLamp12,Path=IsChecked,Converter={converter:BooleanToVisibilityConverter},ConverterParameter=True}"
Foreground="Lime"
Text="运行"/>
<CheckBox Grid.Column="2"
x:Name="ControlLamp12" IsChecked="False" Content="开启" HorizontalAlignment="Center" Foreground="White"/>
</Grid>
<Grid Margin="0 5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="20"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Foreground="White" Text="三四号灯状态:" HorizontalAlignment="Right"/>
<TextBlock Grid.Column="1"
Visibility="{Binding ElementName=ControlLamp34,Path=IsChecked,Converter={converter:BooleanToVisibilityConverter}}"
Foreground="Orange"
Text="关闭"/>
<TextBlock Grid.Column="1"
Visibility="{Binding ElementName=ControlLamp34,Path=IsChecked,Converter={converter:BooleanToVisibilityConverter},ConverterParameter=True}"
Foreground="Lime"
Text="运行"/>
<CheckBox Grid.Column="2" x:Name="ControlLamp34" IsChecked="False" Content="开启" HorizontalAlignment="Center" Foreground="White"/>
</Grid>
</StackPanel>
</Border>
<Border BorderBrush="AliceBlue" BorderThickness="1" Width="230" Margin="10" Padding="10">
<StackPanel>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="20"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Foreground="White" Text="空调运行状态:" HorizontalAlignment="Right"/>
<TextBlock Grid.Column="1"
Visibility="{Binding ElementName=ControlAirCondition,Path=IsChecked,Converter={converter:BooleanToVisibilityConverter}}"
Foreground="Orange"
Text="关闭"/>
<TextBlock Grid.Column="1"
Visibility="{Binding ElementName=ControlAirCondition,Path=IsChecked,Converter={converter:BooleanToVisibilityConverter},ConverterParameter=True}"
Foreground="Lime"
Text="运行"/>
<CheckBox Grid.Column="2" x:Name="ControlAirCondition" VerticalAlignment="Center" IsChecked="False" Content="开启"
HorizontalAlignment="Center" Foreground="White">
</CheckBox>
</Grid>
</StackPanel>
</Border>
</StackPanel>
</Border>
</Grid>
</Window>
划重点
元素绑定:
Visibility="{Binding ElementName=ControlLamp12, Path=IsChecked,Converter={converter:BooleanToVisibilityConverter}, ConverterParameter=True}"
如上红字警告:如果是普通的元素绑定,红字基本就能表示(方法有很多哈,上面的不是唯一的)。但是,我们知道属性IsChecked 是bool类型的但是我们的属性Visibility却是Visibility的枚举值。因此,我们需要后面Converter来实现属性值之间的转换。
值转换器:
Visibility="{Binding ElementName=ControlLamp12, Path=IsChecked,Converter={converter:BooleanToVisibilityConverter}, ConverterParameter=True}"
解析下:红字表示属性Visibility的Converter转换器是使用的{converter:BooleanToVisibilityConverter}这个转换器,其中是converter名字空间,在文件头可以看到 xmlns:converter="clr-namespace:Deamon.ValueConverters" 。后面的ConverterParameter=True 表示传递的参数,具体信息在BooleanToVisibilityConverter转换器类中有详细说明。
Over
每次记录一小步...点点滴滴人生路...