【WPF.NET开发】WPF中的输入

本文内容

  1. 输入 API
  2. 事件路由
  3. 处理输入事件
  4. 文本输入
  5. 触摸和操作
  6. 侧重点
  7. 鼠标位置
  8. 鼠标捕获
  9. 命令
  10. 输入系统和基元素

Windows Presentation Foundation (WPF) 子系统提供了一个功能强大的 API,用于从各种设备(包括鼠标、键盘、触摸和触笔)获取输入。 本主题介绍了 WPF 提供的服务,并说明了输入系统的体系结构。

1、输入 API

主要输入 API 公开存在于以下基元素类上:UIElement、ContentElement、FrameworkElement 和 FrameworkContentElement。 这些类提供有关输入事件(例如按键、鼠标按钮、鼠标滚轮、鼠标移动、焦点管理和鼠标捕获等)的功能。 通过将输入 API 放置在基元素上,而不是将所有输入事件视作一项服务,该输入体系结构使输入事件可以由 UI 中的特定对象指明其出处,并支持事件路由方案,从而使得多个元素有机会处理输入事件。 许多输入事件都具有与之相关联的一对事件。 例如,键盘按下事件与 KeyDown 和 PreviewKeyDown 事件相关联。 这些事件的区别在于它们如何路由至目标元素。 预览事件将元素树从根元素到目标元素向下进行隧道操作。 冒泡事件从目标元素到根元素向上进行冒泡操作。 

1.1 键盘和鼠标类

除了基元素类上的输入 API 之外,Keyboard 类和 Mouse 类还提供了更多 API 来处理键盘和鼠标输入。

Keyboard 类上的输入 API 示例包括可返回当前按下的 ModifierKeys 的 Modifiers 属性和可确定是否按下了特定键的 IsKeyDown 方法。

以下示例使用 GetKeyStates 方法来确定 Key 是否处于按下状态。

// Uses the Keyboard.GetKeyStates to determine if a key is down.
// A bitwise AND operation is used in the comparison.
// e is an instance of KeyEventArgs.
if ((Keyboard.GetKeyStates(Key.Return) & KeyStates.Down) > 0)
{
    btnNone.Background = Brushes.Red;
}

Mouse 类上的输入 API 示例包括可获取鼠标中键状态的 MiddleButton 和可获得鼠标指针当前指向的元素的 DirectlyOver。

以下示例确定了鼠标的 LeftButton 是否处于 Pressed 状态。

if (Mouse.LeftButton == MouseButtonState.Pressed)
{
    UpdateSampleResults("Left Button Pressed");
}

Mouse 和 Keyboard 类在本概述中有更详细的介绍。

1.2 触笔输入

WPF 集成了对 Stylus 的支持。 Stylus 是因 Tablet PC 而变得普及的一种笔输入。 WPF 应用程序可以通过使用鼠标 API 将触笔视为鼠标,但 API 也公开了触笔设备抽象,其使用的模型与键盘和鼠标类似。 所有与触笔相关的 API 都包含单词“Stylus”(触笔)。

由于触笔可充当鼠标,因此仅支持鼠标输入的应用程序仍可以自动获得一定程度的触笔支持。 以这种方式使用触笔时,应用程序有能力处理相应的触笔事件,然后处理相应的鼠标事件。 此外,通过触笔设备抽象也可以使用墨迹输入等较高级别的服务。 

2、事件路由

FrameworkElement 可以在其内容模型中包含其他元素作为子元素,从而形成一个元素树。 在 WPF 中,父元素可以通过处理事件来参与定向到其子元素或其他后代的输入。 这对于从较小的控件生成控件(该过程称为“控件组合”或“组合”)特别有用。

事件路由是将事件转发到多个元素的过程,以便使路由中的特定对象或元素可以选择对已由其他元素指明来源的事件提供重要响应(通过处理)。 路由事件使用三种路由机制的其中一种:直接、浮升和隧道。 在直接路由中,源元素是收到通知的唯一元素,事件不会路由至任何其他元素。 但是相对于标准 CLR 事件,直接路由事件仍然提供一些仅针对路由事件而存在的其他功能。 浮升操作在元素树中向上进行,首先通知指明了事件来源的第一个元素,然后是父元素等等。 隧道操作从元素树的根开始,然后向下进行,以原始的源元素结束。 

WPF 输入事件通常成对出现,由一个隧道事件和一个浮升事件组成。 隧道事件与冒泡事件的不同之处在于它有“预览”前缀。 例如,PreviewMouseMove 是鼠标移动事件的隧道版本,MouseMove 是此事件的浮升版本。 此事件配对是在元素级别实现的一种约定,不是 WPF 事件系统的固有功能。 

3、处理输入事件

若要在元素上接收输入,必须将事件处理程序与该特定事件关联。 在 XAML 中,这很简单:将事件的名称作为要侦听此事件的元素的特性进行引用。 然后,根据委托,将特性的值设置为所定义的事件处理程序的名称。 事件处理程序必须用代码(例如 C#)编写,并且可以包含在代码隐藏文件中。

当操作系统报告发生键操作时,如果键盘焦点正处在元素上,则将发生键盘事件。 鼠标和触笔事件分别分为两类:报告指针位置相对于元素的变化的事件,和报告设备按钮状态的变化的事件。

3.1 键盘输入事件示例

以下示例侦听按下向左键的操作。 创建了一个具有 Button 的 StackPanel。 用于侦听按下向左键的事件处理程序附加到了 Button 实例。

示例的第一部分创建了 StackPanel 和 Button,并且附加了 KeyDown 的事件处理程序。

// Create the UI elements.
StackPanel keyboardStackPanel = new StackPanel();
Button keyboardButton1 = new Button();

// Set properties on Buttons.
keyboardButton1.Background = Brushes.AliceBlue;
keyboardButton1.Content = "Button 1";

// Attach Buttons to StackPanel.
keyboardStackPanel.Children.Add(keyboardButton1);

// Attach event handler.
keyboardButton1.KeyDown += new KeyEventHandler(OnButtonKeyDown);

第二部分用代码编写,定义了事件处理程序。 按下向左键且 Button 具有键盘焦点时,处理程序会运行,且 Button 的 Background 颜色会发生变化。 如果按下了一个键,但不是向左键,Button 的 Background 颜色将变回其初始颜色。

private void OnButtonKeyDown(object sender, KeyEventArgs e)
{
    Button source = e.Source as Button;
    if (source != null)
    {
        if (e.Key == Key.Left)
        {
            source.Background = Brushes.LemonChiffon;
        }
        else
        {
            source.Background = Brushes.AliceBlue;
        }
    }
}

3.2 鼠标输入事件示例

在下面的示例中,当鼠标指针进入 Button 时,Button 的 Background 颜色会发生变化。 当鼠标离开 Button 时,Background 颜色将还原。

示例的第一部分创建了 StackPanel 和 Button 控件,并将 MouseEnter 和 MouseLeave 事件附加到了 Button。

<StackPanel>
  <Button Background="AliceBlue"
          MouseEnter="OnMouseExampleMouseEnter"
          MouseLeave="OnMosueExampleMouseLeave">Button
          
  </Button>
</StackPanel>
// Create the UI elements.
StackPanel mouseMoveStackPanel = new StackPanel();
Button mouseMoveButton = new Button();

// Set properties on Button.
mouseMoveButton.Background = Brushes.AliceBlue;
mouseMoveButton.Content = "Button";

// Attach Buttons to StackPanel.
mouseMoveStackPanel.Children.Add(mouseMoveButton);

// Attach event handler.
mouseMoveButton.MouseEnter += new MouseEventHandler(OnMouseExampleMouseEnter);
mouseMoveButton.MouseLeave += new MouseEventHandler(OnMosueExampleMouseLeave);

该示例的第二部分用代码编写,定义了事件处理程序。 当鼠标进入 Button 时,Button 的 Background 颜色将变为 SlateGray。 当鼠标离开 Button 时,Button 的 Background 颜色将变回 AliceBlue。

private void OnMosueExampleMouseLeave(object sender, MouseEventArgs e)
{
    // Cast the source of the event to a Button.
    Button source = e.Source as Button;

    // If source is a Button.
    if (source != null)
    {
        source.Background = Brushes.AliceBlue;
    }
}

4、文本输入

通过 TextInput 事件,你能够借助与设备无关的方式侦听文本输入。 键盘是文本输入的主要方式,但通过语音、手写和其他输入设备也可以生成文本输入。

对于键盘输入,WPF 首先发送相应的 KeyDown/KeyUp 事件。 如果未处理这些事件,并且按键是文本(而不是诸如方向箭头或功能键之类的控制键),则将引发 TextInput 事件。 KeyDown/KeyUp 和 TextInput 事件之间并不总是简单的一对一映射,因为多次击键可以生成单个字符的文本输入,而单次击键可以生成多个字符串。 对于中文、日文和韩文等语言尤其如此,这些语言使用输入法编辑器 (IME) 生成由其对应的字母组成的成千上万个可能的字符。

当 WPF 发送 KeyUp/KeyDown 事件时,如果击键可能成为 TextInput 事件的一部分(例如按下 ALT+S),Key 将设置为 Key.System。 这允许 KeyDown 事件处理程序中的代码检查是否存在 Key.System,如果有,则留给随后引发的 TextInput 事件的处理程序处理。 在这些情况下,可以使用 TextCompositionEventArgs 参数的各种属性来确定原始击键。 同样,如果 IME 处于活动状态,则 Key 具有值 Key.ImeProcessed,且 ImeProcessedKey 会提供一个或多个原始击键。

以下示例定义了 Click 事件的处理程序和 KeyDown 事件的处理程序。

第一段代码或标记创建用户界面。

<StackPanel KeyDown="OnTextInputKeyDown">
  <Button Click="OnTextInputButtonClick"
          Content="Open" />
  <TextBox> . . . </TextBox>
</StackPanel>
// Create the UI elements.
StackPanel textInputStackPanel = new StackPanel();
Button textInputeButton = new Button();
TextBox textInputTextBox = new TextBox();
textInputeButton.Content = "Open";

// Attach elements to StackPanel.
textInputStackPanel.Children.Add(textInputeButton);
textInputStackPanel.Children.Add(textInputTextBox);

// Attach event handlers.
textInputStackPanel.KeyDown += new KeyEventHandler(OnTextInputKeyDown);
textInputeButton.Click += new RoutedEventHandler(OnTextInputButtonClick);

第二段代码包含事件处理程序。

private void OnTextInputKeyDown(object sender, KeyEventArgs e)
{
    if (e.Key == Key.O && Keyboard.Modifiers == ModifierKeys.Control)
    {
        handle();
        e.Handled = true;
    }
}

private void OnTextInputButtonClick(object sender, RoutedEventArgs e)
{
    handle();
    e.Handled = true;
}

public void handle()
{
    MessageBox.Show("Pretend this opens a file");
}

由于输入事件在事件路由中向上浮升,因此不管哪个元素具有键盘焦点,StackPanel 都将接收输入。 会首先通知 TextBox 控件,仅当 TextBox 不处理输入时,才会调用 OnTextInputKeyDown 处理程序。 如果使用了 PreviewKeyDown 事件而不是 KeyDown 事件,将首先调用 OnTextInputKeyDown 处理程序。

在此示例中,处理逻辑写入了两次,分别针对 CTRL+O和按钮的单击事件。 使用命令,而不是直接处理输入事件,可简化此过程。 

5、触摸和操作

Windows 7 操作系统中的新硬件和 API 使应用程序能够同时接收来自多个触控的输入。 WPF 通过在触摸发生时引发事件,使应用程序能够以类似于响应其他输入(例如鼠标或键盘)的方式来检测和响应触摸设备。

发生触摸时,WPF 将公开两种类型的事件:触摸事件和操作事件。 触摸事件提供有关触摸屏上每个手指及其移动的原始数据。 操作事件将输入解释为特定操作。 本部分将讨论这两种类型的事件。

5.1 先决条件

需要以下组件才能开发响应触摸的应用程序。

  • Visual Studio 2010。

  • Windows 7。

  • 支持 Windows 触控的设备,如触摸屏。

5.2 术语

讨论触摸时使用了以下术语。

  • 触摸是 Windows 7 可识别的一种用户输入。 通常,将手指放在触敏式屏幕上会触发触摸。 请注意,如果设备仅将手指的位置和移动转换为鼠标输入,则笔记本电脑上常用的触摸板等设备不支持触摸。

  • 多点触摸是同时发生在多个点上的触摸。 Windows 7 和 WPF 支持多点触摸。 WPF 文档中每当论及触摸时,相关概念均适用于多点触摸。

  • 当触摸被解释为应用于对象的实际操作时,就发生了操作。 在 WPF 中,操作事件将输入解释为平移、展开或旋转操作。

  • touch device 表示产生触摸输入的设备,例如触摸屏上的一根手指。

5.3 响应触摸的控件

如果以下控件的内容延伸到视图之外,则可以通过在控件上拖动手指来滚动该控件。

  • ComboBox

  • ContextMenu

  • DataGrid

  • ListBox

  • ListView

  • MenuItem

  • TextBox

  • ToolBar

  • TreeView

ScrollViewer 定义了 ScrollViewer.PanningMode 附加属性,该属性让你可以指定是水平、垂直、同时以这两种方式还是不以任何一种方式启用触摸移动。 ScrollViewer.PanningDeceleration 属性指定当用户从触摸屏上抬起手指时滚动速度减慢的速度。 ScrollViewer.PanningRatio 附加属性指定滚动偏移与平移操作偏移的比率。

5.4 触摸事件

基类 UIElement、UIElement3D 和 ContentElement 定义你可以订阅的事件,以便你的应用程序对触摸做出响应。 当应用程序将触摸解释为操作对象以外的其他操作时,触摸事件非常有用。 例如,使用户能够以一个或多个手指绘制的应用程序将订阅触摸事件。

所有三个类都定义了以下事件,其行为类似,而无论定义类是什么。

  • TouchDown

  • TouchMove

  • TouchUp

  • TouchEnter

  • TouchLeave

  • PreviewTouchDown

  • PreviewTouchMove

  • PreviewTouchUp

  • GotTouchCapture

  • LostTouchCapture

像键盘和鼠标事件一样,触摸事件也是路由事件。 以 Preview 开头的事件是隧道事件,以 Touch 开头的事件是冒泡事件。 当你处理这些事件时,可以通过调用 GetTouchPoint 或 GetIntermediateTouchPoints 方法获得输入相对于任何元素的位置。

为了理解触控事件之间的交互,请考虑以下这种情况:用户将一个手指放在元素上,在该元素中移动手指,然后将手指从该元素上移开。 下图显示了冒泡事件的执行(为简单起见,省略了隧道事件)。

ndp-touchevents.png?view=netframeworkdesktop-4.8

 触摸事件

下列内容描述了上图中的事件顺序。

  1. 当用户将手指放在元素上时,会发生一次 TouchEnter 事件。

  2. 会发生一次 TouchDown 事件。

  3. 当用户在元素中移动手指时,会发生多次 TouchMove 事件。

  4. 当用户从元素抬起手指时,会发生一次 TouchUp 事件。

  5. 会发生一次 TouchLeave 事件。

当使用两根以上的手指时,每根手指都会发生事件。

5.5 操作事件

对于应用程序支持用户操作对象的情况,UIElement 类定义了操作事件。 与只是报告触摸位置的触摸事件不同,操作事件会报告可采用何种方式解释输入。 有三种类型的操作:转换、扩展和旋转。 下列内容介绍了如何调用这三种类型的操作。

  • 将一根手指放在对象上,并在触摸屏上拖动手指以调用转换操作。 此操作通常会移动对象。

  • 将两根手指放在物体上,并将手指相互靠拢或分开以调用扩展操作。 此操作通常会调整对象的大小。

  • 将两根手指放在对象上,并将一个手指围绕另一个手指旋转以调用旋转操作。 此操作通常会旋转对象。

多种类型的操作可以同时发生。

使对象响应操作时,可以让对象看起来具有惯性。 这样可以使对象模拟真实的世界。 例如,在桌子上推一本书时,如果你足够用力,书将在你松手后继续移动。 利用 WPF,可以通过在用户的手指松开对象后引发操作事件来模拟这种行为。

UIElement 定义了以下操作事件。

  • ManipulationStarting

  • ManipulationStarted

  • ManipulationDelta

  • ManipulationInertiaStarting

  • ManipulationCompleted

  • ManipulationBoundaryFeedback

默认情况下,UIElement 不会收到这些操作事件。 若要在 UIElement 上收到操作事件,请将 UIElement.IsManipulationEnabled 设置为 true

操作事件的执行路径

考虑用户“抛出”一个对象的情况。 用户将手指放在对象上,将手指在触摸屏上移动一段短距离,然后在移动的同时抬起手指。 此操作的结果是,该对象将在用户的手指下方移动,并在用户抬起手指后继续移动。

下图显示了操作事件的执行路径和每个事件的重要信息。

ndp-manipulationevents.png?view=netframeworkdesktop-4.8

 操作事件

下列内容描述了上图中的事件顺序。

  1. 当用户将手指放在对象上时,会发生 ManipulationStarting 事件。 此外,此事件还允许你设置 ManipulationContainer 属性。 在后续事件中,操作的位置将相对于 ManipulationContainer。 在除 ManipulationStarting 之外的事件中,此属性是只读的,因此你只能在发生 ManipulationStarting 事件时设置此属性。

  2. 接下来会发生 ManipulationStarted 事件。 此事件报告操作的原始位置。

  3. 当用户的手指在触摸屏上移动时,会发生多次 ManipulationDelta 事件。 ManipulationDeltaEventArgs 类的 DeltaManipulation 属性报告操作是解释为移动、展开还是平移。 这是你执行操作对象的大部分工作的地方。

  4. 当用户的手指与对象失去接触时,会发生 ManipulationInertiaStarting 事件。 此事件使你可以指定操作在惯性期间的减速。 这样,选择时对象就可以模拟不同的物理空间或特性。 例如,假设应用程序有两个表示真实世界中的物品的对象,并且一个物品比另一个物品重。 你可以使较重的对象比较轻的对象减速更快。

  5. 在发生惯性时,会发生多次 ManipulationDelta 事件。 请注意,当用户的手指在触摸屏上移动并且 WPF 模拟惯性时,将发生此事件。 换句话说,在 ManipulationInertiaStarting 事件之前和之后,会发生 ManipulationDelta。 ManipulationDeltaEventArgs.IsInertial 属性报告在惯性期间是否发生了 ManipulationDelta 事件,以便你可以检查该属性并根据其值执行不同的操作。

  6. 当操作和任何惯性结束时,会发生 ManipulationCompleted 事件。 也就是说,在所有 ManipulationDelta 事件发生后,会发生 ManipulationCompleted 事件以指示操作已完成。

UIElement 还定义了 ManipulationBoundaryFeedback 事件。 在 ManipulationDelta 事件中调用 ReportBoundaryFeedback 方法时,会发生此事件。 ManipulationBoundaryFeedback 事件使应用程序或组件可以在对象到达边界时提供可视反馈。 例如,Window 类会处理 ManipulationBoundaryFeedback 事件,以便在到达窗口边缘时使窗口轻微移动。

你可以通过对任意操作事件中的事件参数调用 Cancel 方法来取消操作,ManipulationBoundaryFeedback 除外。 当你调用 Cancel 时,不再引发操作事件,触摸会发生鼠标事件。 下表描述了取消操作的时间与所发生的鼠标事件之间的关系。

展开表

在其中调用取消的事件针对已经发生的输入发生的鼠标事件
ManipulationStarting 和 ManipulationStarted鼠标按下事件。
ManipulationDelta鼠标按下和鼠标移动事件。
ManipulationInertiaStarting 和 ManipulationCompleted鼠标按下、鼠标移动和鼠标弹起事件。

请注意,如果你在操作处于惯性期间调用 Cancel,则该方法返回 false,并且输入不会引发鼠标事件。

5.6 触摸事件和操作事件之间的关系

UIElement 可以始终收到触摸事件。 当 IsManipulationEnabled 属性设置为 true 时,UIElement 会同时收到触摸和操作事件。 如果未处理 TouchDown 事件(即 Handled 属性为 false),则操作逻辑会将触摸捕获到元素并生成操作事件。 如果 Handled 属性在 TouchDown 事件中设置为 true,则操作逻辑不会生成操作事件。 下图显示了触摸事件和操作事件之间的关系。

ndp-touchmanipulateevents.png?view=netframeworkdesktop-4.8

 触摸事件和操作事件

下列内容描述了上图中所示的触摸事件和操作事件之间的关系。

  • 当第一个触摸设备在 UIElement 上生成 TouchDown 事件时,操作逻辑将调用 CaptureTouch 方法,该方法会生成 GotTouchCapture 事件。

  • 当发生 GotTouchCapture 时,操作逻辑将调用 Manipulation.AddManipulator 方法,该方法会生成 ManipulationStarting 事件。

  • 当发生 TouchMove 事件时,操作逻辑将生成在 ManipulationInertiaStarting 事件之前发生的 ManipulationDelta 事件。

  • 当元素上的最后一个触摸设备引发 TouchUp 事件时,操作逻辑将生成 ManipulationInertiaStarting 事件。

6、侧重点

在 WPF 中,有两个与焦点有关的主要概念:键盘焦点和逻辑焦点。

6.1 键盘焦点

键盘焦点指当前正在接收键盘输入的元素。 在整个桌面上,只能有一个具有键盘焦点的元素。 在 WPF 中,具有键盘焦点的元素会将 IsKeyboardFocused 设置为 true。 静态 Keyboard 方法 FocusedElement 返回当前具有键盘焦点的元素。

可以通过 Tab 键移到某个元素或通过在特定元素(如 TextBox)上单击鼠标来获取键盘焦点。 也可以使用 Keyboard 类的 Focus 方法以编程方式获取键盘焦点。 Focus 尝试为指定元素提供键盘焦点。 Focus 返回的元素是当前具有键盘焦点的元素。

为使元素获得键盘焦点,必须将 Focusable 属性和 IsVisible 属性设置为 true。 某些类(例如 Panel)默认将 Focusable 设置为 false;因此,如果希望该元素能够获得焦点,必须将此属性设置为 true

以下示例使用 Focus 将键盘焦点设置在 Button 上。 Loaded 事件处理程序是在应用程序中设置初始焦点的推荐位置。

private void OnLoaded(object sender, RoutedEventArgs e)
{
    // Sets keyboard focus on the first Button in the sample.
    Keyboard.Focus(firstButton);
}

6.2 逻辑焦点

逻辑焦点是指焦点范围内的FocusManager.FocusedElement。 一个应用程序中可以有多个具有逻辑焦点的元素,但在一个特定的焦点范围中只能有一个具有逻辑焦点的元素。

焦点范围是一个容器元素,用于跟踪其范围内的 FocusedElement。 焦点离开焦点范围时,焦点元素会失去键盘焦点,但保留逻辑焦点。 焦点返回到焦点范围时,焦点元素会再次获得键盘焦点。 这使得键盘焦点可在多个焦点范围之间切换,但确保了焦点返回到焦点范围时,焦点范围中的焦点元素仍为焦点元素。

通过将 FocusManager 附加属性 IsFocusScope 设置为 true,或者通过在代码中使用 SetIsFocusScope 方法设置该附加属性,可将元素转换为 Extensible Application Markup Language (XAML) 中的焦点范围。

以下示例通过设置 IsFocusScope 附加属性将 StackPanel 转换为焦点范围。

<StackPanel Name="focusScope1" 
            FocusManager.IsFocusScope="True"
            Height="200" Width="200">
  <Button Name="button1" Height="50" Width="50"/>
  <Button Name="button2" Height="50" Width="50"/>
</StackPanel>
StackPanel focuseScope2 = new StackPanel();
FocusManager.SetIsFocusScope(focuseScope2, true);

WPF 中默认为焦点范围的类是 Window、Menu、ToolBar 和 ContextMenu。

具有键盘焦点的元素还具有它所属的焦点范围的逻辑焦点;因此,在 Keyboard 类或基元素类上使用 Focus 方法将焦点设置在一个元素上将尝试赋予该元素键盘焦点和逻辑焦点。

要确定焦点范围中的焦点元素,请使用 GetFocusedElement。

7、鼠标位置

WPF 输入 API 提供了与坐标空间有关的有用信息。 例如,坐标 (0,0) 为左上角坐标,但该坐标是树中那一个元素的左上角坐标? 是属于输入目标的元素? 是在其上附加事件处理程序的元素? 还是其他内容? 为了避免混淆,WPF 输入 API 要求,在处理通过鼠标获取的坐标时,应指定参考框架。 GetPosition 方法返回鼠标指针相对于指定元素的坐标。

8、鼠标捕获

鼠标设备专门保留称为鼠标捕获的模式特征。 鼠标捕获用于在拖放操作开始时保持转换的输入状态,从而不一定发生涉及鼠标指针的标称屏幕位置的其他操作。 拖动过程中,未终止拖放时用户无法单击,这使得大多数鼠标悬停提示在拖动来源拥有鼠标捕获时是不合适的。 输入系统公开了可确定鼠标捕获状态的 API 以及可强制在特定元素上捕获鼠标或清除鼠标捕获状态的 API。 

9、命令

使用命令,输入处理可以更多地在语义级别(而不是在设备输入级别)进行。 命令是简单的指令,如 CutCopyPaste 或 Open。 命令可用于集中命令逻辑。 可从 Menu、在 ToolBar 上或者通过键盘快捷方式使用相同的命令。 命令还提供一种机制,用于在命令不可用时禁用控件。

RoutedCommand 是 ICommand 的 WPF 实现。 当执行 RoutedCommand 时,将在命令目标上引发 PreviewExecuted 和 Executed 事件,这会像其他输入一样,在元素树中发生隧道操作和浮升操作。 如果未设置命令目标,则具有键盘焦点的元素将成为命令目标。 执行命令的逻辑将附加到 CommandBinding。 当 Executed 事件到达该特定命令的 CommandBinding 时,将调用 CommandBinding 上的 ExecutedRoutedEventHandler。 此处理程序执行该命令的操作。

WPF 提供由 ApplicationCommands、MediaCommands、ComponentCommands、NavigationCommands 和 EditingCommands 组成的常用命令库,你也可以定义自己的命令。

以下示例显示了如何设置 MenuItem,以便在单击时它将调用 TextBox 上的 Paste 命令,假定 TextBox 具有键盘焦点。

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste" />
  </Menu>
  <TextBox />
</StackPanel>
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();

// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);

// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;

// Setting the command target to the TextBox
pasteMenuItem.CommandTarget = pasteTextBox;

10、输入系统和基元素

输入事件(例如由 Mouse、Keyboard 和 Stylus 类定义的附加事件)由输入系统引发,并基于在运行时命中测试可视化树来注入到对象模型中的某个特定位置。

Mouse、Keyboard 和 Stylus 定义为附加事件的每个事件也会由基元素类 UIElement 和 ContentElement 重新公开为新路由事件。 基元素路由事件由处理原始附加事件并重用事件数据的类生成。

当输入事件通过其基元素输入事件实现与特定源元素相关联时,可以通过基于逻辑和可视化树对象的组合的事件路由的其余部分进行路由,并由应用程序代码进行处理。 通常,使用 UIElement 和 ContentElement 上的路由事件处理这些与设备有关的输入事件更为方便,因为可以使用 XAML 中和代码中更直观的事件处理程序语法。 你可以选择处理发起进程的附加事件,但将会面临几个问题:附加事件可能会被基元素类处理标记为已处理,并且你需要使用访问器方法(而不是真正的事件语法)才能为附加事件附加处理程序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/287002.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

orange3,一个无敌的 Python 库!

更多Python学习内容&#xff1a;ipengtao.com 大家好&#xff0c;今天为大家分享一个无敌的 Python 库 - orange3。 Github地址&#xff1a;https://github.com/biolab/orange3 数据科学和机器学习是当今科技领域的重要组成部分&#xff0c;而数据分析和建模通常是其中的关键步…

【数据分析】指数移动平均线的直观解释

slavahead 一、介绍 在时间序列分析中&#xff0c;通常需要通过考虑先前的值来了解序列的趋势方向。序列中下一个值的近似可以通过多种方式执行&#xff0c;包括使用简单基线或构建高级机器学习模型。 指数&#xff08;加权&#xff09;移动平均线是这两种方法之间的稳健权衡。…

【Unity中的A星寻路】Navigation导航寻路系统四大页签详解

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…

STL map容器与pair类模板(解决扫雷问题)

CSTL之Map容器 - 数据结构教程 - C语言网 (dotcpp.com)https://www.dotcpp.com/course/118CSTL之Pair类模板 - 数据结构教程 - C语言网 (dotcpp.com)https://www.dotcpp.com/course/119 刷到一个扫雷的题目&#xff0c;之前没有玩怎么过扫雷&#xff0c;于是我就去玩了玩…

M3u8视频地址如何转为mp4视频

在当今数字化的时代&#xff0c;视频格式的转换已成为日常需求。M3u8格式的视频由于其分段的特性&#xff0c;常常给播放和编辑带来不便。而MP4格式则因其通用性和高质量而广受欢迎。那么&#xff0c;如何将M3u8视频地址转换为MP4格式呢&#xff1f;接下来&#xff0c;我们将为…

网络安全B模块(笔记详解)- 数字取证

数据分析数字取证-attack 1.使用Wireshark查看并分析Windows 7桌面下的attack.pcapng数据包文件,通过分析数据包attack.pcapng找出恶意用户的IP地址,并将恶意用户的IP地址作为Flag(形式:[IP地址])提交; 解析:http.request.method==POST ​ Flag:[172.16.1.102] 2.继续…

This is probably not a problem with npm.

项目场景&#xff1a; 新创建的vue3项目&#xff0c;根据elementplus官网安装步骤进行按需导入安装&#xff0c;运行项目报错 This is probably not a problem with npm.There is likely additional logging output above. 原因分析&#xff1a; 是elementplus安装版本和自动…

克隆clone github上某个项目的子目录

有时会遇到只需要克隆github某个项目的子目录&#xff0c;此时可以使用以下方法实现&#xff1a; 需求示例&#xff1a; 现需要克隆&#xff1a;https://github.com/SingleZombie/DL-Demos项目中的ddim项目 注&#xff1a;ddim项目的地址为&#xff1a;https://github.com/Sing…

Pytorch上采样

文章目录 Upsample特殊上采样 Upsample 所谓上采样&#xff0c;实则是一个插值过程。所以上采样对象在初始化时&#xff0c;需要指定一个插值类型&#xff0c;Upsample是torch.nn中最基础的上采样类&#xff0c;初始化参数如下 Upsample(sizeNone, scale_factorNone, modenea…

Django(五)

员工管理系统 1.新建项目 2.创建app python manage.py startapp app012.1 注册app 3. 设计表结构&#xff08;django&#xff09; from django.db import modelsclass Department(models.Model):""" 部门表 """title models.CharField(verbos…

AJAX(一)

一、AJAX简介 AJAX全称为 Asynchronous JavaScript And XML,就是异步的JS和XML。 通过AJAX可以在浏览器中向服务器发送异步请求&#xff0c;最大的优势&#xff1a;无刷新获取数据 AJAX不是新的编程语言&#xff08;使用的js)&#xff0c;而是一种将现有的标准组合在一起使用的…

【Apache-2.0】springboot-openai-chatgpt超级AI大脑产品架构图

springboot-openai-chatgpt: 一个基于SpringCloud的Chatgpt机器人&#xff0c;已对接GPT-3.5、GPT-4.0、百度文心一言、stable diffusion AI绘图、Midjourney绘图。用户可以在界面上与聊天机器人进行对话&#xff0c;聊天机器人会根据用户的输入自动生成回复。同时也支持画图&a…

解决json.decoder.JSONDecodeError: Extra data: line 1 column 721 (char 720)问题

python中将字符串序反列化成json格式时报错 fn result_json[0].decode(utf-8).strip(\00) json_object json.loads(fn) print(type(json_object))排查了以下原因应该是序列化的字符串全都在一行&#xff0c;json库不能一次性处理这么长的序列

每天刷两道题——第三天

1.1两两交换链表中的节点 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09; 输入&#xff1a;[1,2,3,4] 输出&#xff1a;[2,1,4,3…

摄像头监控系统/视频监控云平台EasyCVR鼠标指示故障,该如何解决?

安防视频监控/视频集中存储/云存储/磁盘阵列EasyCVR平台可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有国标GB28181、RTSP/Onvif、RTMP等&#xff0c;以及支持厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等。平台既具备传统安…

OLED硬件电路设计

OLED,全称有机自发光二极管。其主要通过控制注入子像素发光材料的电流大小&#xff0c;实现不同颜色的显示。 OLED屏幕的每个像素点都可以理解成一颗独立控制的灯珠&#xff0c;开启时只需要进行显示的像素点即可。不像LCD一样&#xff0c;显示需要整块背光的亮度&#xff0c;…

fastadmin传递参数给html和js,通过身份判断动态显示列表头部住店和离店按钮

首先将管理员或者酒店人员的身份传递给html和js做按钮显示权限 roomorder.php index.html {if $admin_id != 1}<a class="btn btn-success btn-change btn-start btn-disabled" data-params=

2023年总结及2024年目标之关键字“提速”

1. 感受 时光荏苒&#xff0c;都365天下来了&#xff0c;从一开始试水&#xff0c;到后面为素材焦虑&#xff0c;然后有存货了&#xff0c;渐渐也就习惯成自然了&#xff0c;现在回头看&#xff0c;还是那句话"事非经过不知难"&#xff0c;后面再来一句&#xff0c;…

【Linux操作系统】探秘Linux奥秘:进程与任务管理的解密与实战

&#x1f308;个人主页&#xff1a;Sarapines Programmer&#x1f525; 系列专栏&#xff1a;《操作系统实验室》&#x1f516;诗赋清音&#xff1a;柳垂轻絮拂人衣&#xff0c;心随风舞梦飞。 山川湖海皆可涉&#xff0c;勇者征途逐星辉。 目录 &#x1fa90;1 初识Linux OS &…

SpringSecurity-2.7中跨域问题

SpringSecurity-2.7中跨域问题 访问测试 起因 写这篇的起因是会了解到 SSM(CrosOrigin)解决跨域,但是会在加入SpringSecurity配置后,这个跨域解决方案就失效了,而/login这个请求上是无法添加这个注解或者通过配置(WebMvcConfig)去解决跨域,所以只能使用SpringSecurity提供的.c…