LoginSignup
5
4

More than 5 years have passed since last update.

[UWP]UIの自動非表示Behavior

Posted at

要件

動画再生と一緒に表示するコントロールUIをマウスポインタの移動やタッチ操作が一定時間されていないときに自動で非表示にしたい。

ついでにマウスポインタも消せると尚良し。

ビヘイビアで実装

AutoHideBehavior.cs

using Microsoft.Xaml.Interactivity;
using System;
using System.Threading;
using Windows.UI.Core;
using Windows.UI.Xaml;

namespace MyApp.Views.Behaviors
{
    public class AutoHide : Behavior<FrameworkElement>
    {


        #region IsEnable Property

        public static readonly DependencyProperty IsEnableProperty =
            DependencyProperty.Register("IsEnable"
                    , typeof(bool)
                    , typeof(AutoHide)
                    , new PropertyMetadata(default(bool), OnIsEnablePropertyChanged)
                );

        public bool IsEnable
        {
            get { return (bool)GetValue(IsEnableProperty); }
            set { SetValue(IsEnableProperty, value); }
        }


        public static void OnIsEnablePropertyChanged(object sender, DependencyPropertyChangedEventArgs args)
        {
            AutoHide source = (AutoHide)sender;

            var isActive = (bool)args.NewValue;

            if (isActive)
            {
                source.EnableAutoHide();
            }
            else
            {
                source.DisableteAutoHide();
            }
        }

        #endregion






        #region Delay Property

        public static readonly DependencyProperty DelayProperty =
            DependencyProperty.Register("Delay"
                    , typeof(TimeSpan)
                    , typeof(AutoHide)
                    , new PropertyMetadata(default(double))
                );

        public TimeSpan Delay
        {
            get { return (TimeSpan)GetValue(DelayProperty); }
            set { SetValue(DelayProperty, value); }
        }


        public static void OnDelayPropertyChanged(object sender, DependencyPropertyChangedEventArgs args)
        {
            AutoHide source = (AutoHide)sender;

            source._NextHideTime = source._PrevPreventTime + source.Delay;
        }

        #endregion





        #region WithCursor Property 

        public static readonly DependencyProperty WithCursorProperty =
            DependencyProperty.Register("WithCursor"
                , typeof(bool)
                , typeof(AutoHide)
                , new PropertyMetadata(true)
            );

        public bool WithCursor
        {
            get { return (bool)GetValue(WithCursorProperty); }
            set { SetValue(WithCursorProperty, value); }
        }

        #endregion







        public void PreventAutoHide()
        {
            _PrevPreventTime = DateTime.Now;
            _NextHideTime = _PrevPreventTime + Delay;

            this.AssociatedObject.Visibility = Visibility.Visible;

            CoreWindow.GetForCurrentThread().PointerCursor = _CoreCursor;
        }








        protected override void OnAttached()
        {
            base.OnAttached();

            _CoreCursor = CoreWindow.GetForCurrentThread().PointerCursor;

        }

        protected override void OnDetaching()
        {
            base.OnDetaching();

            _Timer?.Dispose();
            _Timer = null;

            CoreWindow.GetForCurrentThread().PointerCursor = _CoreCursor;
        }




        private void EnableAutoHide()
        {
            _NextHideTime = DateTime.Now + Delay;

            _Timer = new Timer(async (state) => 
            {
                await Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
                {
                    if (this.AssociatedObject.Visibility == Visibility.Visible && 
                        _NextHideTime < DateTime.Now)
                    {
                        this.AssociatedObject.Visibility = Visibility.Collapsed;

                        if (WithCursor)
                        {
                            CoreWindow.GetForCurrentThread().PointerCursor = null;
                        }
                    }
                });
            }
            , this, Delay, TimeSpan.FromMilliseconds(100));


        }

        private void DisableteAutoHide()
        {
            _Timer?.Dispose();
            _Timer = null;

            this.AssociatedObject.Visibility = Visibility.Visible;
            CoreWindow.GetForCurrentThread().PointerCursor = _CoreCursor;
        }



        CoreCursor _CoreCursor;
        DateTime _PrevPreventTime;
        DateTime _NextHideTime;
        Timer _Timer;
    }
}


XAMLで利用

UsingAutoHide.xaml
<!--
xmlns:mybehavior="using:MyApp.Views.Behaviors"
xmlns:i="using:Microsoft.Xaml.Interactivity"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
-->
<i:Interaction.Behaviors>
    <mybehavior:AutoHide x:Name="AutoHideBehavior"
                            IsEnable="{Binding IsAutoHideEnable}"
                            Delay="00:00:03" 
                            WithCursol="True"
                            >
        <i:Interaction.Behaviors>
            <core:EventTriggerBehavior SourceObject="{Binding ElementName=MediaControl}" EventName="Tapped">
                <core:CallMethodAction TargetObject="{Binding ElementName=AutoHideBehavior}" MethodName="PreventAutoHide" />
            </core:EventTriggerBehavior>
            <core:EventTriggerBehavior SourceObject="{Binding ElementName=MediaControl}" EventName="PointerMoved">
                <core:CallMethodAction TargetObject="{Binding ElementName=AutoHideBehavior}" MethodName="PreventAutoHide" />
            </core:EventTriggerBehavior>

        </i:Interaction.Behaviors>
    </mybehavior:AutoHide>
</i:Interaction.Behaviors>

自動非表示にしたいUIのビヘイビアにAutoHideBehaviorをくっつけます。
ViewModel側で予めbool型のIsAutoHideEnableプロパティを作成しておきます。
"MediaControl"とあるところは、マウス操作やタップ操作を受けてUI非表示を解除したいPanel要素等のx:Nameを指定します。

ビヘイビア動作の補足説明

前提として、FrameworkElementがVisibilityChangedイベントを持たないため、TimerとNextHideTimeの組み合わせで非表示時間をチェックする方法をとっています。

Timerのコールバック内はTimerスレッドのため、UIスレッドへの切り替えのためDispatcher.RunAsyncを使っています。

WithCursorをTrueにするとウィンドウのカーソルをまるごと表示・非表示を切り替えてます。FrameworkElementごとにカーソルを変える方法もあるようですが、シンプルに書けるのでこの形をとっています。

なお、WithCursorをTrueにすると上記の例ではMediaControl以外の要素上でもカーソルが消えてしまいます。これはウィンドウの既定のカーソル表示が存在しなくなるためです。

この問題を回避するにはMediaControl以外の要素にCursorを指定するか、または、何も表示しないCursorをリソースとして追加して、MediaControl要素に設定します。(前者は検証済みですが、後者は未検証です。)

コードのライセンス

PD/パブリックドメイン
コードについては、自分の責任で自由に使ってください。

5
4
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
5
4