none
WPF ボタンタッチ後の色を戻したいのですが。 RRS feed

  • 質問

  • WPF C#2015で開発しております。

    Buttonをタブレットでタッチした後、押されたままの色になります。これはどこかをタッチ(他のボタン、その他の部分)をしないと押されたままのように見えます。実際にはTouchupイベントが発生していますが、色は戻りません。

    このボタンの色をタッチする前の色にTouchupのイベントでもどこでもいいので戻したいのですが、ご教授願えませんでしょうか。

    (株)シンプルコンピュータシステムズ

    今泉浩一郎

    2015年8月12日 6:31

回答

  • ボタンのテンプレートを見てみるとIsMouseOverプロパティに反応して色が変わっているようです。
    Windowsのシステム的にはタッチした位置にマウスカーソルが移動するため、ボタンをタッチするとボタンの上にマウスカーソルが移動し、IsMouseOverプロパティがtrueになります。
    タッチデバイスでは指を離してもマウスが移動するというわけでは無いので、マウスカーソルはボタン上に残り、IsMouseOverはtrueのままになり、結果的に色が変わったままになってしまいます。

    色が残らないようにするには、テンプレートを変更してIsMouseOverに反応している部分をなくすか、IsMouseOverがfalseになるような処理を行うしかないようです。

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:app="clr-namespace:WpfApplication1"
            Title="MainWindow" Height="350" Width="525">
     
        <UniformGrid Columns="2">
            <Button Content="Normal" Width="100" Height="100" Margin="10"/>
            <Button Content="RemoveIsOver" Width="100" Height="100" 
                          app:ButtonTool.RemoveTouchOver="True"/>
        </UniformGrid>
    </Window>
    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Threading;
    using System.Runtime.InteropServices;
    namespace WpfApplication1
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
        }
    
        internal class ButtonTool
        {
            #region 添付プロパティ
    
            [AttachedPropertyBrowsableForType(typeof(Button))]
            public static bool GetRemoveTouchOver(DependencyObject obj)
            {
                return (bool)obj.GetValue(RemoveTouchOverProperty);
            }
            [AttachedPropertyBrowsableForType(typeof(Button))]
            public static void SetRemoveTouchOver(DependencyObject obj, bool value)
            {
                obj.SetValue(RemoveTouchOverProperty, value);
            }
    
            public static readonly DependencyProperty RemoveTouchOverProperty =
                DependencyProperty.RegisterAttached
                ("RemoveTouchOver"
                , typeof(bool)
                , typeof(ButtonTool)
                , new UIPropertyMetadata
                    (default(bool)
                     , new PropertyChangedCallback(OnRemoveTouchOverPropertyChanged)));
    
            private static void OnRemoveTouchOverPropertyChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs e)
            {
                Button target = dpo as Button;
                if (target != null)
                {
                    if ((bool)e.OldValue)
                    {
                        target.MouseEnter -= target_MouseEnter;
                    }
                    if ((bool)e.NewValue)
                    {
                        target.MouseEnter += target_MouseEnter;
                    }
                }
            }
    
            #endregion
    
            private static void target_MouseEnter(object sender, MouseEventArgs e)
            {
                var btn = (Button)sender;
                _IsStylusEnter = e.StylusDevice != null;
                if (_IsStylusEnter)
                {
                    //たぶんタッチパネルの場合に遅延処理をする
                    btn.Dispatcher.BeginInvoke((Action<Button>)CheckIsMouseOver, DispatcherPriority.Input, btn);
                    touchedPoint = Mouse.GetPosition(btn);//現在のマウスの位置(タッチした位置)を保存
                }
            }
    
            private static Point touchedPoint;
            private static bool _IsStylusEnter;
            private static void CheckIsMouseOver(Button btn)
            {
                Point p0 = Mouse.GetPosition(btn);
                if (touchedPoint == p0 && btn.IsMouseOver && _IsStylusEnter)
                {//マウス位置が動いておらず、ボタンにIsMouseOverが残っている場合
    
                    //マウスカーソルをボタン領域外に適当に移動
                    Point p1 = btn.PointToScreen(new Point(0, 0));
                    Point p2 = btn.PointToScreen(new Point(btn.DesiredSize.Width, btn.DesiredSize.Height));
                    Point pNew = new Point(Math.Min(p1.X, p2.X) - 5, Math.Min(p1.Y, p2.Y) - 5);
                    SetCursorPos((int)pNew.X, (int)pNew.Y);
    
                    //メッセージ処理させて再度判定させる
                    btn.Dispatcher.BeginInvoke((Action<Button>)CheckIsMouseOver, DispatcherPriority.Input, btn);
                }
                else
                {
                    touchedPoint = new Point(double.PositiveInfinity, double.PositiveInfinity);
                }
    
            }
    
            [System.Runtime.InteropServices.DllImport("User32.dll")]
            private static extern bool SetCursorPos(int X, int Y);
        }
    }

    なお、この方法では強制的にボタン領域外にマウスを移動させていますが、移動先で何かのイベントが発生するかもしれず、無限ループに嵌る可能性があります。

    #テーマをクラシックにすれば色がつかないという方法も無きにしも非ず…


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 編集済み gekkaMVP 2015年8月12日 16:23
    • 回答としてマーク simple-kou 2015年8月13日 7:23
    2015年8月12日 16:11

すべての返信

  • ボタンのテンプレートを見てみるとIsMouseOverプロパティに反応して色が変わっているようです。
    Windowsのシステム的にはタッチした位置にマウスカーソルが移動するため、ボタンをタッチするとボタンの上にマウスカーソルが移動し、IsMouseOverプロパティがtrueになります。
    タッチデバイスでは指を離してもマウスが移動するというわけでは無いので、マウスカーソルはボタン上に残り、IsMouseOverはtrueのままになり、結果的に色が変わったままになってしまいます。

    色が残らないようにするには、テンプレートを変更してIsMouseOverに反応している部分をなくすか、IsMouseOverがfalseになるような処理を行うしかないようです。

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:app="clr-namespace:WpfApplication1"
            Title="MainWindow" Height="350" Width="525">
     
        <UniformGrid Columns="2">
            <Button Content="Normal" Width="100" Height="100" Margin="10"/>
            <Button Content="RemoveIsOver" Width="100" Height="100" 
                          app:ButtonTool.RemoveTouchOver="True"/>
        </UniformGrid>
    </Window>
    using System;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Threading;
    using System.Runtime.InteropServices;
    namespace WpfApplication1
    {
        public partial class MainWindow : Window
        {
            public MainWindow()
            {
                InitializeComponent();
            }
        }
    
        internal class ButtonTool
        {
            #region 添付プロパティ
    
            [AttachedPropertyBrowsableForType(typeof(Button))]
            public static bool GetRemoveTouchOver(DependencyObject obj)
            {
                return (bool)obj.GetValue(RemoveTouchOverProperty);
            }
            [AttachedPropertyBrowsableForType(typeof(Button))]
            public static void SetRemoveTouchOver(DependencyObject obj, bool value)
            {
                obj.SetValue(RemoveTouchOverProperty, value);
            }
    
            public static readonly DependencyProperty RemoveTouchOverProperty =
                DependencyProperty.RegisterAttached
                ("RemoveTouchOver"
                , typeof(bool)
                , typeof(ButtonTool)
                , new UIPropertyMetadata
                    (default(bool)
                     , new PropertyChangedCallback(OnRemoveTouchOverPropertyChanged)));
    
            private static void OnRemoveTouchOverPropertyChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs e)
            {
                Button target = dpo as Button;
                if (target != null)
                {
                    if ((bool)e.OldValue)
                    {
                        target.MouseEnter -= target_MouseEnter;
                    }
                    if ((bool)e.NewValue)
                    {
                        target.MouseEnter += target_MouseEnter;
                    }
                }
            }
    
            #endregion
    
            private static void target_MouseEnter(object sender, MouseEventArgs e)
            {
                var btn = (Button)sender;
                _IsStylusEnter = e.StylusDevice != null;
                if (_IsStylusEnter)
                {
                    //たぶんタッチパネルの場合に遅延処理をする
                    btn.Dispatcher.BeginInvoke((Action<Button>)CheckIsMouseOver, DispatcherPriority.Input, btn);
                    touchedPoint = Mouse.GetPosition(btn);//現在のマウスの位置(タッチした位置)を保存
                }
            }
    
            private static Point touchedPoint;
            private static bool _IsStylusEnter;
            private static void CheckIsMouseOver(Button btn)
            {
                Point p0 = Mouse.GetPosition(btn);
                if (touchedPoint == p0 && btn.IsMouseOver && _IsStylusEnter)
                {//マウス位置が動いておらず、ボタンにIsMouseOverが残っている場合
    
                    //マウスカーソルをボタン領域外に適当に移動
                    Point p1 = btn.PointToScreen(new Point(0, 0));
                    Point p2 = btn.PointToScreen(new Point(btn.DesiredSize.Width, btn.DesiredSize.Height));
                    Point pNew = new Point(Math.Min(p1.X, p2.X) - 5, Math.Min(p1.Y, p2.Y) - 5);
                    SetCursorPos((int)pNew.X, (int)pNew.Y);
    
                    //メッセージ処理させて再度判定させる
                    btn.Dispatcher.BeginInvoke((Action<Button>)CheckIsMouseOver, DispatcherPriority.Input, btn);
                }
                else
                {
                    touchedPoint = new Point(double.PositiveInfinity, double.PositiveInfinity);
                }
    
            }
    
            [System.Runtime.InteropServices.DllImport("User32.dll")]
            private static extern bool SetCursorPos(int X, int Y);
        }
    }

    なお、この方法では強制的にボタン領域外にマウスを移動させていますが、移動先で何かのイベントが発生するかもしれず、無限ループに嵌る可能性があります。

    #テーマをクラシックにすれば色がつかないという方法も無きにしも非ず…


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    • 編集済み gekkaMVP 2015年8月12日 16:23
    • 回答としてマーク simple-kou 2015年8月13日 7:23
    2015年8月12日 16:11
  • ありがとうございます。概念はわかります。。

    WPFの欠陥だと思っております。ここらVS2015に期待したのですが、VS2013と変わっていないという事と思っております。

    ロジックは私には難解な部分もありますので、少しづつ勉強したいと思います。

    期待した通りのです。ありがとうございました。

    2015年8月13日 7:27
  • すみません。。もう少しタッチの反応を早くする事は可能でしょうか?できれば教えていただきたく。。
    2015年8月13日 7:34