任務 5 –將ViewModel 和傳感器助手進行整合
1.打開 SensorViewModel.cs ,並且在私有區域創建一個屬性來暴露光線傳感器。
C#
private SensorHelper<AmbientLightSensor, AmbientLightSensor.LuminousIntensity> _sensor;
public SensorHelper<AmbientLightSensor, AmbientLightSensor.LuminousIntensity>
LightSensor
{
get { return _sensor; }
}
2.現在我們來對加速傳感器做相同的事。
C#
private SensorHelper<Accelerometer3D, Accelerometer3D.Acceleration3D>
_acceleromaterSensor;
public SensorHelper<Accelerometer3D, Accelerometer3D.Acceleration3D>
AcceleromaterSensor
{
get { return _acceleromaterSensor; }
}
3.我們為SensorViewModel加入一個結構體,用來設置傳感器。
C#
public SensorViewModel()
{
_sensor = new SensorHelper<AmbientLightSensor,
AmbientLightSensor.LuminousIntensity>();
_sensor.Initialize();
_acceleromaterSensor = new SensorHelper<Accelerometer3D,
Accelerometer3D.Acceleration3D>();
_acceleromaterSensor.Initialize();
}
4.編譯並測試你的代碼。你應該能夠運行並且與傳感器進行整合。
任務 6 – 在UI 用戶界面添加光纖傳感器的交互。
在下面的任務中,你將使用到光線亮度傳感器。三個獨立的元素將與其進行交互:
• 亮度指示器:一個衡量光線亮度的進度條
• 圖像:當亮度增強的時候,一半圖片將變成灰色。
• 圖像名稱:在高亮的狀態下增大字號來保持可讀性。
1.添加第一個傳感器指示器,轉到MainWindow.xaml,並且定位到布局grid的左邊(包含了一個標題叫“Sensor Data”的)。
XAML
<TextBlock HorizontalAlignment="Center" Margin="0,10" VerticalAlignment="Top" FontFamily="Segoe UI" FontSize="13" Foreground="White" Text="Sensor Data"/>
<Grid Margin="9,36,9,9">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="0.4*"/> </Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Border Grid.ColumnSpan="5" Grid.RowSpan="2" Background="Black" CornerRadius="2"/>
<TextBlock Grid.Column="0" Grid.Row="2" HorizontalAlignment="Center" Margin="4,0,4,10" VerticalAlignment="Center" Foreground="White" Text="LUM"/>
<ProgressBar x:Name="LightIntensityProgressBar" Width="10" HorizontalAlignment="Center" Margin="0,8,0,5" Maximum="1" Minimum="0" Orientation="Vertical"
Style="{StaticResource SensorProgressBar}"
Value="{Binding Path=LightSensor.Value.Intensity, Mode=OneWay, Converter={StaticResource LuminosityConverter}}"/>
<Path Height="80" Grid.ColumnSpan="5" Grid.RowSpan="2" Margin="1,0" VerticalAlignment="Top" Data="M0.5,0.5L214.5,0.5 214.5,168.5 212.8,168.4C197.7,167.1 181.8,166.5 165.5,166.5 95.3,166.5 34.0,178.9 1.3,197.3L0.5,197.8z" Stretch="Fill">
<Path.Fill>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0" Color="#04FFFFFF"/>
<GradientStop Offset="0.1" Color="#10FFFFFF"/>
<GradientStop Offset="1.0" Color="#18FFFFFF"/>
</LinearGradientBrush>
</Path.Fill>
</Path>
</Grid>
2.為了我們的數據綁定工作,我們需要創建一個數據轉換器,用來在傳感器數據和進度條控件中進行轉換。那麼我們可以在MainWindow.xaml.cs文件中添加一個轉換器的類。
C#
public class LuminosityConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType,
object parameter,
System.Globalization.CultureInfo culture)
{
double d = System.Convert.ToDouble(value);
return Math.Max(Math.Min(Math.Log(d, 10000), 1.0), 0.0);
}
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
3.在我們已經創建了LuminosityConverter類之後,我們需要將它聲明為一個資源。返回到MainWindow.xaml,並且“local”XAML命名空間的聲明要和轉換器的命名空間相同。
XAML
<Window x:Class="SensorHOL.MainWindow"
xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
xmlns:local="clr-namespace:SensorHOL"
Title="MainWindow" WindowState="Maximized" Background="Black">
4.在Windows��件中添加一個資源塊。
XAML
<Window.Resources>
</Window.Resources>
5.在資源塊中,聲明一個Converter資源。
XAML
<local:LuminosityConverter x:Key="LuminosityConverter" />
6.編譯並且運行應用程序。
你將看到燈光指示器能夠反映光線傳感器。
7.現在將這個與其他的我們希望它們與之交互的元素進行連接。
a.首先,改變圖片名稱的字體大小。
那麼在光線測量值和字體大小間的轉換,又一次的用到了轉換器。
b.然後,定位到ImageName的文本區域。
(請注意,我們對之前實現的進度條已經進行了綁定)。
XAML
<TextBlock Foreground="White" Grid.Column="1" Grid.Row="2" HorizontalAlignment="Center" VerticalAlignment="Center" FontFamily="Segoe UI" FontSize="{Binding ElementName=LightIntensityProgressBar, Path=Value, Mode=OneWay, Converter={StaticResource LuminosityToFontSizeConverter}}" Text="{Binding ImageName, Mode=OneWay}"/>
8.轉到轉換器的後台代碼實現。
C#
public class LuminosityToFontSizeConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object
parameter, System.Globalization.CultureInfo culture)
{
double d = System.Convert.ToDouble(value);
return 70.0 * d + 13.0;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
9.現在,將這個轉換器聲明成一個資源。
XAML
<Window.Resources>
<local:LuminosityConverter x:Key="LuminosityConverter" />
<local:LuminosityToFontSizeConverter
x:Key="LuminosityToFontSizeConverter" />
</Window.Resources>
10.編譯並運行代碼。
當光線增強的時候,圖片名稱的文字應該變大。
現在將圖片連接到光線指示器,並且允許圖片使用灰白效果。
11.我們從你的開始解決方案中,向PixelShaders添加XAML命名空間開始。
XAML
<Window x:Class="SensorHOL.MainWindow"
xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation
xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml
xmlns:local="clr-namespace:SensorHOL"
xmlns:PixelShaders="clr-namespace:SensorHOL.PixelShaders"
Title="MainWindow" WindowState="Maximized" Background="Black">
12.現在我們可以添加PixelShader元素,並且對第二張圖片添加OpacityMask。
XAML
<Image Source="{Binding ImagePath}" Stretch="Uniform" Margin="10">
</Image>
<Image Source="{Binding ImagePath}" Stretch="Uniform" Margin="10">
<Image.Effect>
<PixelShaders:GrayscaleEffect DesaturationFactor="{Binding ElementName=LightIntensityProgressBar, Path=Value, Mode=OneWay, Converter={StaticResource InvertLuminosityConverter}}"/>
</Image.Effect>
<Image.OpacityMask>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,0">
<GradientStop Offset="{Binding ElementName=OverlayAdjustmentSlider, Path=Value}" Color="#0000"/>
<GradientStop Offset="{Binding ElementName=OverlayAdjustmentSlider, Path=Value}" Color="#F000"/>
</LinearGradientBrush>
</Image.OpacityMask>
</Image>
13.同樣,在光線測量值和PixelShader值之前,實現一個數據轉換器。
C#
public class InvertLuminosityConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
double d = System.Convert.ToDouble(value);
return 1 - d;
}
public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
14.和我們創建完轉換器一樣,我們還需要將其指定為一個資源。
XAML
<Window.Resources>
<local:LuminosityConverter x:Key="LuminosityConverter" />
<local:LuminosityToFontSizeConverter
x:Key="LuminosityToFontSizeConverter" />
<local:InvertLuminosityConverter x:Key="InvertLuminosityConverter" />
</Window.Resources>
15.編譯並運行你的代碼。現在你應該已經可以使用滑塊來控制圖片,並且其中一部分的圖片將與傳感器進行交互,展示灰白的圖片。
任務 7 – 將加速傳感器與UI 用戶界面進行整合(可選)
在這個任務中,你將會把3D加速傳感器與UI用戶界面進行連接。另外,還有左邊的加速器指示器,右邊的應用程序隨每個傳感器運動進行的旋轉。
1.首先,你要實現X,Y,Z軸的運動指示器:
a.在MainWindow.xaml中,找到LightIntensityProgressBar。
b.如果想要做這個,再添加3個指示器。
其實他們在代碼上看很清楚,因為所有3個指示器都綁定到同一個屬性,我們只需要一種方法來區分每個指示器。這個就是通過轉換進程來關心這個問題的轉換器元素的目的。
XAML
<TextBlock Grid.Column="0" Grid.Row="2" HorizontalAlignment="Center" Margin="4,0,4,10" VerticalAlignment="Center" Foreground="White" Text="LUM"/>
<ProgressBar x:Name="LightIntensityProgressBar" Width="10" HorizontalAlignment="Center" Margin="0,8,0,5" Maximum="1" Minimum="0" Orientation="Vertical" Style="{StaticResource SensorProgressBar}" Value="{Binding Path=LightSensor.Value.Intensity, Mode=OneWay, Converter={StaticResource LuminosityConverter}}"/>
<TextBlock Grid.Column="1" Grid.Row="2" HorizontalAlignment="Center" Margin="4,0,4,10" VerticalAlignment="Center" Foreground="White" Text="X"/>
<ProgressBar x:Name="AccelerometerX" Width="10" Grid.Column="1" HorizontalAlignment="Center" Margin="0,8,0,5" Maximum="1.3" Minimum="-1.3" Orientation="Vertical" Style="{StaticResource SensorProgressBar}" Value="{Binding Path=AcceleromaterSensor.Value, Mode=OneWay, Converter={StaticResource AccelerationConverter}, ConverterParameter=X}"/>
<TextBlock Grid.Column="2" Grid.Row="2" HorizontalAlignment="Center" Margin="4,0,4,10" VerticalAlignment="Center" Foreground="White" Text="Y"/>
<ProgressBar x:Name="AccelerometerY" Width="10" Grid.Column="2" HorizontalAlignment="Center" Margin="0,8,0,5" Maximum="1.3" Minimum="-1.3" Orientation="Vertical" Style="{StaticResource SensorProgressBar}" Value="{Binding Path=AcceleromaterSensor.Value, Mode=OneWay, Converter={StaticResource AccelerationConverter}, ConverterParameter=Y}"/>
<TextBlock Grid.Column="3" Grid.Row="2" HorizontalAlignment="Center" Margin="4,0,4,10" VerticalAlignment="Center" Foreground="White" Text="Z"/>
<ProgressBar x:Name="AccelerometerZ" Width="10" Grid.Column="3" HorizontalAlignment="Center" Margin="0,8,0,5" Maximum="1.3" Minimum="-1.3" Orientation="Vertical" Style="{StaticResource SensorProgressBar}" Value="{Binding Path=AcceleromaterSensor.Value, Mode=OneWay, Converter={StaticResource AccelerationConverter}, ConverterParameter=Z}"/>
2.現在我們可以添加一個使用轉換元素的數據轉換器,但是首先我們需要添加下面的引用部分。
C#
using Microsoft.WindowsAPICodePack.Sensors;
3.添加下面的代碼。
C#
public class AccelerationConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object
parameter, System.Globalization.CultureInfo culture)
{
Accelerometer3D.AccelerationAxis? axis =
(Accelerometer3D.AccelerationAxis?)Enum.Parse(typeof(Acceleromete r3D.AccelerationAxis), parameter as string);
if (axis.HasValue)
{
var a3d = value as Accelerometer3D.Acceleration3D;
if (a3d != null)
return a3d[axis.Value];
}
return 0.0;
}
public object ConvertBack(object value, Type targetType, object
parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
4.添加數據轉換器資源聲明。
XAML
<Window.Resources>
<local:LuminosityConverter x:Key="LuminosityConverter" />
<local:LuminosityToFontSizeConverter x:Key="LuminosityToFontSizeConverter" />
<local:InvertLuminosityConverter x:Key="InvertLuminosityConverter" />
<local:AccelerationConverter x:Key="AccelerationConverter" /> </Window.Resources>
現在我們就可以使用3D加速器傳感器,和屏幕上的旋轉元素了。
我們使用WPF中的使用角度來進行旋轉。轉換器將加速器的X軸和Y軸轉換成角度。為了保證旋轉有效的工作,必須將它設置在元素的中央。這就是為什麼我們還有一個專門計算元素中央位置的轉換器。
5.為每一個圖片添加下面的代碼片段。
XAML
<Image.RenderTransform>
<RotateTransform
Angle="{Binding Path=AcceleromaterSensor.Value, Mode=OneWay, Converter={StaticResource DegreesFromAcceleratorConverter}}" CenterX="{Binding RelativeSource={RelativeSource AncestorType={x:Type Image}, AncestorLevel=1}, Path=ActualWidth, Converter={StaticResource DoubleSplitConverter}}"
CenterY="{Binding RelativeSource={RelativeSource AncestorType={x:Type Image}, AncestorLevel=1}, Path=ActualHeight, Converter={StaticResource DoubleSplitConverter}}" />
</Image.RenderTransform>
6.在Slider元素中,添加下面的代碼片段(與上面的代碼非常相似)。
XAML
<Slider.RenderTransform>
<RotateTransform
Angle="{Binding Path=AcceleromaterSensor.Value, Mode=OneWay, Converter={StaticResource DegreesFromAcceleratorConverter}}"
CenterX="{Binding RelativeSource={RelativeSource AncestorType={x:Type Slider}, AncestorLevel=1}, Path=ActualWidth, Converter={StaticResource DoubleSplitConverter}}"
CenterY="{Binding RelativeSource={RelativeSource AncestorType={x:Type Slider}, AncestorLevel=1}, Path=ActualHeight, Converter={StaticResource DoubleSplitConverter}}"/>
</Slider.RenderTransform>
7.實現第一個轉換器,DegreesFromAcceleratorConverter。
C#
public class DegreesFromAcceleratorConverter : IValueConverter
{
private double _prevX;
private double _prevY;
private const double _histCoeff = 0.6;
private const double _threshold = 0.1;
private bool _prevValuesInit;
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
var a3d = value as Accelerometer3D.Acceleration3D;
if (a3d != null)
{
double x = a3d[Accelerometer3D.AccelerationAxis.X];
double y = a3d[Accelerometer3D.AccelerationAxis.Y];
if (!_prevValuesInit)
{
_prevValuesInit = true;
_prevX = x;
_prevY = y;
}
if (Math.Abs(_prevX - x) < _threshold)
x = _prevX;
if (Math.Abs(_prevY - y) < _threshold)
y = _prevY;
_prevX = (_histCoeff * _prevX) + (1.0 - _histCoeff) * x;
_prevY = (_histCoeff * _prevY) + (1.0 - _histCoeff) * y;
double radians = Math.Atan2(_prevX, -_prevY);
return radians * 180 / Math.PI;
}
return 0.0;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
8.現在添加第二個轉換器。
C#
public class DoubleSplitConverter : IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
double doubleValue = System.Convert.ToDouble(value);
return doubleValue/2.0;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
9.所有剩下的你需要做的,就是將那些轉換器聲明成資源。
XAML
<local:DoubleSplitConverter x:Key="DoubleSplitConverter" /> <local:DegreesFromAcceleratorConverter x:Key="DegreesFromAcceleratorConverter" />
10.編譯並運行你的代碼。
你現在應該可以使用滑塊來單獨控制每個圖片的旋轉了。
你可以在Final解決方案文件夾中找到這個項目的最終版。做得好!
將Windows 7傳感器支持整合到你的應用程序中,通過使應用程序能夠對周圍環境作出更快更好的反應,將大大提高你的應用程序的機會。在這個實驗中,你已經創建了一個WPF應用程序,並且通過整合不同的傳感器,制造了令人興奮的UI用戶界面體驗。