4

I have a requirement on WPF to play a video repeatedly on idle event and restore back to the previous window on the detection of user activity.

For this, I followed the answered https://stackoverflow.com/a/4970019/6696609 by Martin Buberl.

In XAML, I have VideoGrid and WindowGrid Grid, With the detection of idle event, I changed the visibility of each other.

Here is the source code for the illustration of described issue https://github.com/DavidSilwal/wpfvideoissue The idle event is suppose to be occurred at 5 sec. When you click on button, idle event occurred at the 10 sec.

It works perfectly fine except

For the first idle event of the button click, VideoGrid attempts to be visible but it couldn’t be visible (it just blinks) and then every next idle event worked fine.

Feedback and suggestion to get rid of the blink issue are appreciated.

    // set UI on inactivity
    private  void OnInactivity(object sender, EventArgs e)
    {
        _inactiveMousePosition = Mouse.GetPosition(this);                    

        System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
          {
              if (WindowGrid.Visibility == Visibility.Visible)
              {
                  WindowGrid.Visibility = Visibility.Collapsed;
              }

              if (VideoGrid.Visibility == Visibility.Collapsed)
              {
                  WindowState = WindowState.Maximized;
                  WindowStyle = WindowStyle.None;
                  //play video
                  videoplayer.Play();
                  VideoGrid.Visibility = Visibility.Visible;

              }
          }));
    }

     private void OnActivity(object sender, PreProcessInputEventArgs e)
    {
        var inputEventArgs = e.StagingItem.Input;

        if (inputEventArgs is System.Windows.Input.MouseEventArgs || inputEventArgs is KeyboardEventArgs)
        {
            if (e.StagingItem.Input is System.Windows.Input.MouseEventArgs)
            {
                var mouseEventArgs = (System.Windows.Input.MouseEventArgs)e.StagingItem.Input;

                 // no button is pressed and the position is still the same as the application became inactive       
                if (!(
                    mouseEventArgs.LeftButton == MouseButtonState.Pressed ||
                    mouseEventArgs.RightButton == MouseButtonState.Pressed ||
                    mouseEventArgs.MiddleButton == MouseButtonState.Pressed ||
                    mouseEventArgs.XButton1 == MouseButtonState.Pressed ||
                    mouseEventArgs.XButton2 == MouseButtonState.Pressed
                    //|| _inactiveMousePosition != mouseEventArgs.GetPosition(this)
                    ))
                {
                    return;
                }
            }

            // set UI on activity
            System.Windows.Application.Current.Dispatcher.BeginInvoke(new Action(() =>
              {
                  if (VideoGrid.Visibility == Visibility.Visible)
                  {
                      WindowState = WindowState.Normal;
                      WindowStyle = WindowStyle.SingleBorderWindow;
                      //stop video
                      videoplayer.Stop();
                      VideoGrid.Visibility = Visibility.Collapsed;
                  }

                  if (WindowGrid.Visibility == Visibility.Collapsed)
                  {
                      WindowGrid.Visibility = Visibility.Visible;
                  }
              }));

            _activityTimer.Stop();
            _activityTimer.Start();
        }
    }
David Silwal
  • 581
  • 5
  • 18
  • Are you sure the VideoGrid is initialized properly (i.e. Visibility.Collapsed)? Also, keep in mind Visibilty has a third possible state of Hidden. – Jeff R. May 22 '19 at 15:11
  • Thank you, i am much sure VideoGrid is initialized properly. i don't think so it's a visibility issue. if there is no button click, no issue appeared.it's only appeared on the first idle event of button click. – David Silwal May 23 '19 at 04:11

1 Answers1

2

By debugging your code, I can give you a answer, but maybe not a very satisfactory one.

I added some debug output to your code, in OnInactivity ...

private async void OnInactivity(object sender, EventArgs e)
{
    // remember mouse position
    _inactiveMousePosition = Mouse.GetPosition(this);
    // set UI on inactivity

    Debug.WriteLine ( DateTime.Now.ToString("HH:mm:ss.fffffff") + " Inactivity" ) ;

    await System.Windows.Application.Current.Dispatcher.InvokeAsync(new Action(() =>
    {
        Grid1.Visibility = Visibility.Collapsed;
        videoplayer.Play();
        Grid2.Visibility = Visibility.Visible;
    }));
}

and in OnActivity ...

private async void OnActivity(object sender, PreProcessInputEventArgs e)
{
    var inputEventArgs = e.StagingItem.Input;

    if (inputEventArgs is System.Windows.Input.MouseEventArgs || inputEventArgs is KeyboardEventArgs)
    {
        if (e.StagingItem.Input is System.Windows.Input.MouseEventArgs)
        {
            var mouseEventArgs = (System.Windows.Input.MouseEventArgs)e.StagingItem.Input;

            if (!(
                mouseEventArgs.LeftButton == MouseButtonState.Pressed ||
                mouseEventArgs.RightButton == MouseButtonState.Pressed ||
                mouseEventArgs.MiddleButton == MouseButtonState.Pressed ||
                mouseEventArgs.XButton1 == MouseButtonState.Pressed ||
                mouseEventArgs.XButton2 == MouseButtonState.Pressed
                || _inactiveMousePosition != mouseEventArgs.GetPosition(this)
                ))
            {
                return;
            }
        }

        Debug.WriteLine ( DateTime.Now.ToString("HH:mm:ss.fffffff") + " Activity " + inputEventArgs.RoutedEvent.ToString() ) ;

        // set UI on activity
        await System.Windows.Application.Current.Dispatcher.InvokeAsync(new Action(() =>
        {
            videoplayer.Stop();
            Grid1.Visibility = Visibility.Visible;

            Grid2.Visibility = Visibility.Hidden;
        }));

        _activityTimer.Stop();
        _activityTimer.Start();
    }
}

Here is a bit of the output after pressing the button:

23:45:31.0583181 Activity Mouse.MouseMove
23:45:31.0643177 Activity Mouse.QueryCursor
23:45:31.0653178 Activity Mouse.PreviewMouseMove
23:45:31.0663188 Activity Mouse.MouseMove
23:45:31.0723180 Activity Mouse.QueryCursor
23:45:31.0753176 Activity Mouse.PreviewMouseMove
23:45:31.0763193 Activity Mouse.MouseMove
23:45:36.0722537 Inactivity
23:45:36.0752537 Activity Keyboard.PreviewGotKeyboardFocus
23:45:36.0782576 Activity Keyboard.PreviewKeyboardInputProviderAcquireFocus
23:45:36.0802536 Activity Keyboard.KeyboardInputProviderAcquireFocus
23:45:36.0832529 Activity Keyboard.LostKeyboardFocus
23:45:36.0842549 Activity Keyboard.GotKeyboardFocus
23:45:41.1041883 Inactivity
23:45:42.4981698 Activity Mouse.QueryCursor
23:45:42.5001740 Activity Mouse.PreviewMouseMove
23:45:42.5031736 Activity Mouse.MouseMove
23:45:42.5071741 Activity Mouse.QueryCursor
23:45:42.5091736 Activity Mouse.PreviewMouseMove
23:45:42.5111735 Activity Mouse.MouseMove

After 5 seconds of inactivity, it tries to start the video, but immediately causes some events related to the changing focus.

I guess that means that clicking on the button gives the button the focus. Starting the video causes it to lose the focus, generating some events.

I was able to fix the problem by filtering out these events. This is my modified version of the OnActivity function, in which I filter out these events by name:

private async void OnActivity(object sender, PreProcessInputEventArgs e)
{
    var inputEventArgs = e.StagingItem.Input;

    switch ( inputEventArgs.RoutedEvent.Name )
    {
        case "PreviewGotKeyboardFocus":
        case "PreviewKeyboardInputProviderAcquireFocus":
        case "KeyboardInputProviderAcquireFocus":
        case "LostKeyboardFocus":
        case "GotKeyboardFocus":
            return ;
    }

    if (inputEventArgs is System.Windows.Input.MouseEventArgs || inputEventArgs is KeyboardEventArgs)
    {
        if (e.StagingItem.Input is System.Windows.Input.MouseEventArgs)
        {
            var mouseEventArgs = (System.Windows.Input.MouseEventArgs)e.StagingItem.Input;

            if (!(
                mouseEventArgs.LeftButton == MouseButtonState.Pressed ||
                mouseEventArgs.RightButton == MouseButtonState.Pressed ||
                mouseEventArgs.MiddleButton == MouseButtonState.Pressed ||
                mouseEventArgs.XButton1 == MouseButtonState.Pressed ||
                mouseEventArgs.XButton2 == MouseButtonState.Pressed
                || _inactiveMousePosition != mouseEventArgs.GetPosition(this)
                ))
            {
                return;
            }
        }

        // set UI on activity
        await System.Windows.Application.Current.Dispatcher.InvokeAsync(new Action(() =>
        {
            videoplayer.Stop();
            Grid1.Visibility = Visibility.Visible;

            Grid2.Visibility = Visibility.Hidden;
        }));

        _activityTimer.Stop();
        _activityTimer.Start();
    }
}

I do not know if there are other events which you should filter out, and I do not know whether filtering by the event name is the best way to do it.

Phil Jollans
  • 3,605
  • 2
  • 37
  • 50