在 SensorViewModel.cs 中建立感應器屬性:
private SensorHelper<AmbientLightSensor, AmbientLightSensor.LuminousIntensity> _sensor;
public SensorHelper<AmbientLightSensor, AmbientLightSensor.LuminousIntensity> LightSensor
{
get { return _sensor; }
}
private SensorHelper<Accelerometer3D, Accelerometer3D.Acceleration3D> _accelerometerSensor;
public SensorHelper<Accelerometer3D, Accelerometer3D.Acceleration3D> AccelerometerSensor
{
get { return _accelerometerSensor; }
}
建構函式初始化:
public SensorViewModel()
{
_sensor = new SensorHelper<AmbientLightSensor, AmbientLightSensor.LuminousIntensity>();
_sensor.Initialize();
_accelerometerSensor = new SensorHelper<Accelerometer3D, Accelerometer3D.Acceleration3D>();
_accelerometerSensor.Initialize();
}
編譯測試,確認感應器整合運作。
編輯 MainWindow.xaml,在左側感應器資料區域加入:
<TextBlock ... Text="LUM"/>
<ProgressBar x:Name="LightIntensityProgressBar" Width="10" Orientation="Vertical"
Maximum="1" Minimum="0"
Style="{StaticResource SensorProgressBar}"
Value="{Binding Path=LightSensor.Value.Intensity, Mode=OneWay, Converter={StaticResource LuminosityConverter}}"/>
在 MainWindow.xaml.cs 加入:
public class LuminosityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double d = System.Convert.ToDouble(value);
return Math.Max(Math.Min(Math.Log(d, 10000), 1.0), 0.0); // 對數縮放映射 0~1
}
public object ConvertBack(...) { throw new NotImplementedException(); }
}
<Window.Resources>
<local:LuminosityConverter x:Key="LuminosityConverter" />
</Window.Resources>
執行應用程式,進度條將反映環境光強度。
XAML 繫結:
<TextBlock FontSize="{Binding ElementName=LightIntensityProgressBar, Path=Value, Converter={StaticResource LuminosityToFontSizeConverter}}"
Text="{Binding ImageName, Mode=OneWay}"/>
新增轉換器:
public class LuminosityToFontSizeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double d = System.Convert.ToDouble(value);
return 70.0 * d + 13.0; // 映射至 13~83pt
}
...
}
宣告資源並執行,光線增強時字體放大。
引入 PixelShaders 命名空間:
xmlns:PixelShaders="clr-namespace:SensorHOL.PixelShaders"
第二張圖片套用灰階效果與不透明度遮罩:
<Image Source="{Binding ImagePath}" Stretch="Uniform" Margin="10">
<Image.Effect>
<PixelShaders:GrayscaleEffect DesaturationFactor="{Binding ElementName=LightIntensityProgressBar, Path=Value, 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>
public class InvertLuminosityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
double d = System.Convert.ToDouble(value);
return 1 - d; // 亮度高 → 去飽和度低(保留彩色)
}
...
}
宣告資源後執行,滑桿控制圖片遮罩位置,光線感應器控制灰階強度。
<ProgressBar x:Name="AccelerometerX" ...
Value="{Binding Path=AccelerometerSensor.Value, Converter={StaticResource AccelerationConverter}, ConverterParameter=X}"/>
<ProgressBar x:Name="AccelerometerY" ... ConverterParameter=Y />
<ProgressBar x:Name="AccelerometerZ" ... ConverterParameter=Z />
using Microsoft.WindowsAPICodePack.Sensors;
public class AccelerationConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var axis = (Accelerometer3D.AccelerationAxis?)Enum.Parse(typeof(Accelerometer3D.AccelerationAxis), parameter as string);
if (axis.HasValue)
{
var a3d = value as Accelerometer3D.Acceleration3D;
if (a3d != null) return a3d[axis.Value];
}
return 0.0;
}
...
}
為 Image 與 Slider 加入 RotateTransform,繫結角度與中心點:
<Image.RenderTransform>
<RotateTransform
Angle="{Binding Path=AccelerometerSensor.Value, Converter={StaticResource DegreesFromAcceleratorConverter}}"
CenterX="{Binding RelativeSource={RelativeSource AncestorType=Image}, Path=ActualWidth, Converter={StaticResource DoubleSplitConverter}}"
CenterY="{Binding RelativeSource={RelativeSource AncestorType=Image}, Path=ActualHeight, Converter={StaticResource DoubleSplitConverter}}"/>
</Image.RenderTransform>
public class DegreesFromAcceleratorConverter : IValueConverter
{
private double _prevX, _prevY;
private const double _histCoeff = 0.6, _threshold = 0.1;
private bool _prevValuesInit;
public object Convert(object value, Type targetType, object parameter, 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 - _histCoeff) * x;
_prevY = _histCoeff * _prevY + (1 - _histCoeff) * y;
double radians = Math.Atan2(_prevX, -_prevY);
return radians * 180 / Math.PI;
}
return 0.0;
}
...
}
public class DoubleSplitConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return System.Convert.ToDouble(value) / 2.0;
}
...
}
<Window.Resources>
<local:LuminosityConverter x:Key="LuminosityConverter"/>
<local:LuminosityToFontSizeConverter x:Key="LuminosityToFontSizeConverter"/>
<local:InvertLuminosityConverter x:Key="InvertLuminosityConverter"/>
<local:AccelerationConverter x:Key="AccelerationConverter"/>
<local:DoubleSplitConverter x:Key="DoubleSplitConverter"/>
<local:DegreesFromAcceleratorConverter x:Key="DegreesFromAcceleratorConverter"/>
</Window.Resources>

最終版本位於 Final 解決方案資料夾。透過感應器 API 整合,應用程式能即時回應環境變化,創造豐富使用者體驗。