InputManager игнорирует вырезать / копировать / вставить при запуске из меню - PullRequest
0 голосов
/ 11 июня 2010

Я использую InputManager, чтобы проверить, сделаны ли изменения в элементах управления пользователем или кодом.Это работает нормально, за исключением случаев, когда пользователь использует контекстное меню для вырезания / копирования / вставки.Если пользователь делает ctrl + v в текстовом поле, InputManager правильно замечает это.Однако, если вставка выполняется из контекстного меню текстового поля, InputManager никогда не запускает события PreNotifyInput или PostNotifyInput.Кто-нибудь знает почему?Или как обнаружить, что эти действия пользователя?Ниже приведен рабочий образец.Нижний текстовый блок никогда не обновляется, когда пользователь использует меню вырезания / копирования / вставки в вышеприведенном текстовом поле, поскольку PreNotifyInput никогда не запускается.

XAML:

<Window x:Class="InputMgrDemo.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="300" Width="300">
    <StackPanel>
        <TextBox TextChanged="TextBox_TextChanged" />
        <TextBlock Name="_text" />
    </StackPanel>
</Window>

Код позади:

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace InputMgrDemo
{
    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();

            InputManager.Current.PreNotifyInput += ((sender, e) => _userInput = true);
            InputManager.Current.PostNotifyInput += ((sender, args) => _userInput = false);
        }

        private void TextBox_TextChanged(object sender, TextChangedEventArgs e)
        {
            if (_userInput)
            {
                _text.Text = (sender as TextBox).Text;
            }
        }

        private bool _userInput;
    }
}

1 Ответ

2 голосов
/ 12 июня 2010

На самом деле событие PreNotifyInput срабатывает при событии MouseLeftButtonUp, но затем PostNotifyInput срабатывает до фактической вставки.

Вот последовательность операций:

  • Пользователь отпускает кнопку мыши в пункте меню
  • Ваш обработчик события PreNotifyInput называется
  • Возникает событие MouseLeftButtonUp, которое всплывает до MenuItem
  • MenuItem обрабатывает MouseButtonUp и преобразует его в OnClick
  • OnClick вызывает PreviewClickEvent, затем планирует диспетчерский обратный вызов для вызова события Click и выполнения команды
  • Ваш обработчик события PostNotifyInput называется , так как событие MouseLeftButtonUp обрабатывается

  • Любой рендеринг, запланированный Диспетчером, завершается

  • Диспетчер вызывает обратный вызов в MenuItem

  • MenuItem запускает событие Click, которое ничего не делает
  • MenuItem выполняет команду Вставить
  • TextBox обрабатывает команду Вставить и вставляет данные
  • TextBox запускает событие TextChanged

В WPF эффекты «пользовательского ввода» могут произвольно задерживаться обратными вызовами диспетчера и т. Д., Поэтому вы не можете знать, вызвано ли изменение пользовательским вводом или нет.

На самом деле, теоретически это вообще верно. Рассмотрим следующие сценарии:

  1. Пользователь нажимает кнопку в другом месте вашего приложения, которая вызывает загрузку новых данных, которые обновляют ваше значение.
  2. Пользователь нажимает кнопку в другом приложении , которое записывает файл, заставляя ваше приложение обновлять и отображать новые данные.
  3. Пользователь заходит на другой компьютер и обновляет некоторые данные на веб-сайте. Ваше приложение отслеживает этот веб-сайт и обнаруживает изменение.

Очевидно, что в каждом из этих случаев изменение было вызвано пользовательским вводом ;-) Вы понимаете, куда я иду с этим? С философской точки зрения нет никакого фундаментального способа решить, было ли изменение сделано вашим пользователем или кем-то еще.

Если вы действительно хотите «любых изменений, которые происходят между моментом, когда пользователь щелкает мышью или использует клавиатуру в этом приложении, и временем, когда приложение бездействует», вы можете реализовать это:

InputManager.Current.PreNotifyInput += (sender, e) =>
{
  _userInput = true;
  Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, new Action(() =>
  {
    _userInput = false;
  }));
}; 

Но в этом случае, если у вас есть данные, поступающие динамически из внешнего источника, они могут ошибочно считаться пользовательским вводом.

Другой подход - перевернуть ваш вверх дном: каждый раз, когда вы обновляете данные из внешнего источника данных, установите флаг, говорящий, что вы делаете это. Затем каждый раз, когда вы видите изменения с не установленным флагом, вы предполагаете, что это было взаимодействие с пользователем. Это может быть проще для реализации, если вы сможете обеспечить обновление всех ваших внешних данных выше DispatcherPriority.Render.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...