LoginSignup
4
9

More than 5 years have passed since last update.

USBデバイスの抜き差しを検知する

Posted at

やりたいこと

USBデバイスが抜き差しされたタイミングで、ViewModelのコマンドを実行する。

実装

USB抜き差し時にコマンドを実行するビヘイビアを作成する。

ビヘイビア
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;

namespace Sample {
    public class DeviceChangeBehavior {
        private static readonly uint WM_DEVICECHANGE = 0x0219;

        public static DependencyProperty CommandProperty
            = DependencyProperty.RegisterAttached(
                "Command",
                typeof(ICommand),
                typeof(DeviceChangeBehavior),
                new PropertyMetadata(Command_PropertyChanged)
            );

        public static void SetCommand(FrameworkElement element, ICommand value)
            => element.SetValue(CommandProperty, value);

        public static ICommand GetCommand(FrameworkElement element)
            => (ICommand)element.GetValue(CommandProperty);

        private static Dictionary<FrameworkElement, ICommand> _commands
            = new Dictionary<FrameworkElement, ICommand>();

        private static void Command_PropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) {
            var element = d as FrameworkElement;
            if (element == null) return;

            if (e.NewValue != null) {
                element.Loaded += FrameworkElement_Loaded;
                _commands.Add(control, e.NewValue as ICommand);
            } else {
                element.Loaded -= FrameworkElement_Loaded;
                var handle = ((HwndSource)HwndSource.FromVisual(element)).Handle;
                var source = HwndSource.FromHwnd(handle);
                source.RemoveHook(WndProc);
                _commands.Remove(element);
            }
        }

        private static void FrameworkElement_Loaded(object sender, RoutedEventArgs e) {
            var element = sender as FrameworkElement;
            if (element == null) return;

            //ロード完了後でないとハンドラを登録できない
            var handle = ((HwndSource)HwndSource.FromVisual(element)).Handle;
            var source = HwndSource.FromHwnd(handle);

            source.AddHook(WndProc);
        }

        private static IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) {
            if (msg != WM_DEVICECHANGE)
                return IntPtr.Zero;

            _commands
                .Select(x => new { ((HwndSource)HwndSource.FromVisual(x.Key))?.Handle, Command = x.Value })
                .Where(x => x.Handle == hwnd && x.Command.CanExecute(null))
                .ForEach(x => x.Command.Execute(null));

            return IntPtr.Zero;
        }
    }
}

使い方

XAML
<Window x:Class="Sample.MainView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:Sample"
        local:DeviceChangeBehavior.Command="{Binding XXXCommand}"
        Title="MainView" Height="300" Width="300">
    <Grid>
        <TextBox />
    </Grid>
</Window>

Window以外にも指定できるが、ルート要素に指定するのが無難。

4
9
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
9