Start at 2013/8/3 15:05:07

WPF控件模型

  • FrameworkElement
    • Controls.Control: 表示用户界面元素的基类,这些元素使用 ControlTemplate 来定义其外观。
      • ContentControl: 一种包含一段内容的 Control。
        • HeaderedContentControl: 包含一段内容并具有 Header 的 ContentControl。
      • ItemsControl: 可包含多个项目(例如字符串、对象或其他元素)的 Control。
        • HeaderedItemsControl: 可包含多个项目(例如字符串、对象或其他元素)并具有标题的ItemsControl。
      • TextBoxBase: 抽象类。
        • TextBox
    • Panel: 为所有 Panel 元素提供基类。使用 Panel 元素在 Windows Presentation Foundation (WPF) 应用程序中放置和排列子对象。抽象类。
    • Shape: 为 Ellipse、Polygon 和 Rectangle 之类的形状元素提供基类。抽象类。
    • Decorator: 提供在单个子元素(如 Border 或 Viewbox)上或周围应用效果的元素的基类。
    • TextBlock

ContentControl

属于此类型系列的类型

Button, ButtonBase, CheckBox, ComboBoxItem, ContentControl, Frame, GridViewColumnHeader, GroupItem, Label, ListBoxItem, ListViewItem, NavigationWindow, RadioButton, RepeatButton, ScrollViewer, StatusBarItem, ToggleButton, ToolTip, UserControl, Window

可包含 ContentControl 类型的类型

ContentControl 类、HeaderedContentControl 类、ItemsControl 类、HeaderedItemsControl 类、Panel 类、Decorator 类和 Adorner 类

HeaderedContentControl

属于此类型系列的类型

Expander, GroupBox, HeaderedContentControl, TabItem

可包含 HeaderedContentControl 类型的类型

ContentControl 类、HeaderedContentControl 类、ItemsControl 类、HeaderedItemsControl 类、Panel 类、Decorator 类和 Adorner 类

ItemsControl

属于此类型系列的类型

Menu, MenuBase, ContextMenu, ComboBox, ItemsControl, ListBox, ListView, TabControl, TreeView, Selector, StatusBar

可包含 ItemsControl 类型的类型

ContentControl 类、HeaderedContentControl 类、ItemsControl 类、HeaderedItemsControl 类、Panel 类、Decorator 类和 Adorner 类

HeaderedItemsControl

属于此类型系列的类型

HeaderedItemsControl, MenuItem, TreeViewItem, ToolBar

可包含 HeaderedItemsControl 类型的类型

通常,MenuItem 对象用作 Menu 元素的子元素;TreeViewItem 对象用作 TreeView 元素的子元素;ToolBar 对象用作 ToolBarTray 的子元素。

Panel内容模型

属于此类型系列的类型

Canvas, DockPanel, Grid, TabPanel, ToolBarOverflowPanel, StackPanel, ToolBarPanel, UniformGrid, VirtualizingPanel, VirtualizingStackPanel, WrapPanel

可包含 Panel 类型的类型

Decorator模型

属于此类型系列的类型

ButtonChrome, ClassicBorderDecorator, ListBoxChrome, SystemDropShadowChrome, Border, InkPresenter, BulletDecorator, Viewbox, AdornerDecorator

可包含 Decorator 类型的类型

ContentControl 类、HeaderedContentControl 类、ItemsControl 类、HeaderedItemsControl 类、Panel 类、Decorator 类和 Adorner 类


WPF控件内容模型

一、ContentControl模型 ContentControl模型的类型具有一个 Content 属性。Content 属性的类型为 Object,因此,对于您在 ContentControl 中可以放置的内容没有任何限制。可以使用可扩展应用程序标记语言 (XAML) 或代码来设置 Content。

以下控件使用 ContentControl 内容模型:

Button、ButtonBase、CheckBox、ComboBoxItem、ContentControl、Frame、GridViewColumnHeader、GroupItem、Label、ListBoxItem、ListViewItem、NavigationWindow、RadioButton、RepeatButton、ScrollViewer、StatusBarItem、ToggleButton、ToolTip、UserControl、Window

在Content中只能放置一个控件(可以放置一个容器,然后再在容器中放置多个控件)。

二、HeaderedContentControl模型 HeaderedContentControl类继承ContentControl类,表示带有Header的ContentControl,其除了具有ContentControl的Content属性外,还具有一个Header属性,Header的类型也是Object对象,与Content属性的用法类似。

从 HeaderedContentControl 继承的控件有:Expander、GroupBox、TabItem。

三、ItemsControl模型 从 ItemsControl 继承的控件包含一个对象集合。 ItemsControl 的一个示例是 ListBox。可以使用 ItemsSource 属性或 Items 属性来填充一个 ItemsControl。

  1. 使用ItemSource属性
  2. 使用Items属性

四、HeaderedItemsControl模型 HeaderedItemsControl 从 ItemsControl 类继承。HeaderedItemsControl 定义 Header 属性,该属性遵从相同的规则,因为 HeaderedContentControl. WPF 的 Header 属性附带三个从 HeaderedItemsControl 继承的控件:MenuItem、ToolBar、TreeViewItem

HeaderedItemsControl模型可以理解为如下结构:一个HeaderedItemsControl包含一个Items集合,每一个Item包含一个Header属性,一个子Items集合


WPF Panel内容模型、Decorator内容模型及其他

一、Panel内容模型

Panel内容模型指从System.Windows.Controls.Panel继承的控件,这些控件都是容器,可以在内部承载其他的控件和子容器。Panel内容模型包含的容器有:

  • Canvas
  • DockPanel
  • Grid
  • TabPanel
  • ToolBarOverflowPanel
  • UniformGrid
  • StackPanel
  • ToolBarPanel
  • VirtualizingPanel
  • VirtualizingStackPanel
  • WrapPanel

对于Panel模型,其包含一个Children属性,表示其所有的子控件和子容器的集合,在XAML代码中可以省略<XXX.Children>标记

二、Decorator内容模型

Decorator内容模型指的是从System.Windows.Controls.Decorator类继承的控件,主要是对其中的一个子元素的边缘进行修饰。Decorator模型的主要控件包含:

  • AdornerDecorator
  • Border
  • BulletDecorator
  • ButtonChrome
  • ClassicBorderDecorator
  • InkPresenter
  • ListBoxChrome
  • SystemDropShadowChrome
  • Viewbox

Decorator模型包含一个Child属性,表示其包含的一个子元素(注意,只能是一个子元素(控件或容器,在容器中可以再添加其他的控件)),Child属性的XAML标记可以省略。

三、TextBlock模型

TextBlock模型实际上指的就是System.Windows.Controls.TextBlock类,它是一个用于显示少量流内容的轻量控件。其中包含一个InLines属性,支持 Inline 流内容元素的承载和显示。 支持的元素包括 AnchoredBlock、Bold(粗体字符串)、Hyperlink(超链接,在浏览器支持的模式下有效)、InlineUIContainer(承载其他控件的容器)、Italic(斜体字符串)、LineBreak(换行符)、Run(普通字符串)、Span(可以设置字体、颜色等的Span) 和 Underline(下划线)。

四、TextBox模型

System.Windows.Controls.TextBox类,实现的是可编辑的文本框,文本框的内容由字符串类型的Text属性指定,并且整个TextBox的内容使用共同的(即TextBox指定的)样式。


依赖项属性和路由事件

一、依赖项属性(Dependency Property)

Windows Presentation Foundation (WPF) 提供了一组服务,这些服务可用于扩展公共语言运行时 (CLR) 属性的功能。这些服务通常统称为 WPF 属性系统。由 WPF 属性系统支持的属性称为依赖项属性。本概述介绍 WPF 属性系统以及依赖项属性的功能,这包括如何在可扩展应用程序标记语言 (XAML) 中和代码中使用现有的依赖项属性。

依赖项属性的用途在于提供一种方法来基于其他输入的值计算属性值。这些其他输入可以包括系统属性(如主题和用户首选项)、实时属性确定机制(如数据绑定和动画/演示图板)、重用模板(如资源和样式)或者通过与元素树中其他元素的父子关系来公开的值。另外,可以通过实现依赖项属性来提供独立验证、默认值、监视其他属性的更改的回调以及可以基于可能的运行时信息来强制指定属性值的系统。派生类还可以通过重写依赖项属性元数据(而不是重写现有属性的实际实现或者创建新属性)来更改现有属性的某些具体特征。

  1. 依赖项属性与CLR 包装属性

  2. 使用由依赖项属性提供的属性功能

    依赖项属性提供用来扩展属性功能的功能,这与字段支持的属性相反。每个这样的功能通常都表示或支持整套 WPF 功能中的特定功能:

    • 资源
    • 数据绑定
    • 样式
    • 动画
    • 元数据重写
    • 属性值继承
    • WPF 设计器集成
  3. 自定义依赖项属性及重写依赖项属性

对于自定义依赖项属性,其所在的类型必须直接或间接继承System.Windows.DependencyObject类,依赖项属性是通过调用 Register 方法(或 RegisterReadOnly,自定义的只读的依赖项属性)在 WPF 属性系统中注册,并通过 DependencyProperty 标识符字段备份的属性。依赖项属性只能由 DependencyObject 类型使用,但 DependencyObject 在 WPF 类层次结构中的级别很高,因此,WPF 中的大多数可用类都支持依赖项属性。在对依赖项属性及CLR包装属性命名时必须满足:CLR包装属性名+Property=依赖项属性名。

二、路由事件(RoutedEvent)


键盘输入、鼠标输入、焦点处理

一、键盘类和键盘事件

WPF提供了基础的键盘类(System.Input.Keyboard类),该类提供与键盘相关的事件、方法和属性,这些事件、方法和属性提供有关键盘状态的信息。Keyboard的事件也通过UIElement等XAML基元素类的事件向外提供。

对于键盘操作,其常用的事件有两组:

  • KeyDown事件和PreviewKeyDown事件:处理键盘键按下
  • KeyUp事件和PreviewKeyUp事件:处理键盘键抬起

其中KeyDown和KeyUp事件属于冒泡路由事件,而PreviewKeyDown和PreviewKeyup属于隧道路由事件。

为了使元素能够接收键盘输入,该元素必须可获得焦点。默认情况下,大多数 UIElement 派生对象都可获得焦点。如果不是这样,则要使元素可获得焦点,请将基元素上的 Focusable 属性设置为 true。像 StackPanel 和 Canvas 这样的 Panel 类将 Focusable 的默认值设置为 false。因此,对要获取键盘焦点的这些对象而言,必须将 Focusable 设置为 true。

二、鼠标类和鼠标事件

WPF提供的System.Input.Mouse类提供与鼠标相关的事件、方法和属性,这些事件、方法和属性提供有关鼠标状态的信息。与Keyboard类类似,其事件也通过UIElement等基元素向外提供。

其事件主要有以下几组(每个事件均包含XXX冒泡路由事件和PreviewXXX隧道路由事件)

  • MouseDown、MouseUp事件:处理鼠标键的按下与抬起
  • MouseEnter、MouseLeave、MouseMove:处理鼠标进入、离开控件及在控件上移动
  • MouseWheel:处理鼠标滚轮滚动

另外,对于鼠标位置的捕获,使用Mouse类的GetPosition方法,其参数是一个UIElement,表示其鼠标位置基于哪一个控件的坐标系。

三、焦点处理

在 WPF 中,有两个与焦点有关的主要概念:键盘焦点和逻辑焦点。 键盘焦点指接收键盘输入的元素,而逻辑焦点指焦点范围中具有焦点的元素。

  1. 键盘焦点:

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

    为了使元素能够获取键盘焦点,基元素的 Focusable 和 IsVisible 属性必须设置为 true。 有些类(如 Panel 基类)默认情况下将 Focusable 设置为 false;因此,如果您希望此类元素能够获取键盘焦点,必须将 Focusable 设置为 true。

    可以通过用户与 UI 交互(例如,按 Tab 键定位到某个元素或者在某些元素上单击鼠标)来获取键盘焦点。 还可以通过使用 Keyboard 类的 Focus 方法,以编程方式获取键盘焦点。 Focus 方法尝试将键盘焦点给予指定的元素。 返回的元素是具有键盘焦点的元素,如果有旧的或新的焦点对象阻止请求,则具有键盘焦点的元素可能不是所请求的元素。

  2. 逻辑焦点

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

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

    GetFocusScope 返回指定元素的焦点范围。

    WPF 中默认情况下即为焦点范围的类有 Window、MenuItem、ToolBar 和 ContextMenu。

    GetFocusedElement 获取指定焦点范围的焦点元素。SetFocusedElement 设置指定焦点范围中的焦点元素。SetFocusedElement 通常用于设置初始焦点元素。

  3. 键盘导航

    当按下导航键之一时,KeyboardNavigation 类将负责实现默认键盘焦点导航。 导航键有:Tab、Shift+Tab、Ctrl+Tab、Ctrl+Shift+Tab、向上键、向下键、向左键和向右键。

    可以通过设置附加的 KeyboardNavigation 属性 TabNavigation、ControlTabNavigation 和 DirectionalNavigation 来更改导航容器的导航行为。 这些属性是 KeyboardNavigationMode 类型,可能值有 Continue、Local、Contained、Cycle、Once 以及 None。 默认值是 Continue,这意味着元素不是导航容器。

  4. 焦点事件

    与键盘焦点相关的事件有 PreviewGotKeyboardFocus、GotKeyboardFocus、PreviewLostKeyboardFocus 以及 LostKeyboardFocus。 这些事件定义为 Keyboard 类的附加事件,但更便于作为基元素类上的等效路由事件来访问。

    当元素获取键盘焦点时,会引发 GotKeyboardFocus。当元素失去键盘焦点时,会引发 LostKeyboardFocus。 如果处理了 PreviewGotKeyboardFocus 事件或 PreviewLostKeyboardFocusEvent 事件,并且 Handled 设置为 true,则焦点将不会改变。


WPF命令(Commands)

WPF中的命令路由与事件路由是两个很让初学者头痛的概念,对于命令路由可以理解为,系统(WPF)定义了一系列的操作,在应用程序中可以直接使用。例如,定义一系列菜单,执行对窗体中文本框的复制、剪切、粘贴操作,简单地可以这样做:

<Grid>
      <Grid.RowDefinitions>
          <RowDefinition Height="23" />
          <RowDefinition />
      </Grid.RowDefinitions>
      <Menu Grid.Row="0" Grid.Column="0">
          <MenuItem Header="Edit">
              <MenuItem x:Name="menuCopy" Header="Copy" 
                        Command="ApplicationCommands.Copy" />
             <MenuItem x:Name="menuCut" Header="Cut" 
                       Command="ApplicationCommands.Cut" />
             <MenuItem x:Name="menuPaste" Header="Paste" 
                       Command="ApplicationCommands.Paste" />
         </MenuItem>
     </Menu>
     <TextBox Grid.Row="1" Grid.Column="0" x:Name="mainText" 
              TextWrapping="Wrap" AcceptsReturn="True" />
 </Grid>

WPF 中的路由命令模型可以分为四个主要概念:命令、命令源、命令目标以及命令绑定:

  • 命令是要执行的操作。在本例中命令为ApplicationCommands.Copy、Cut、Paste
  • 命令源是调用命令的对象。 在本例中命令源为三个MenuItem控件
  • 命令目标是在其上执行命令的对象。 在本例中命令目标是mainText这个TextBox文本框
  • 命令绑定是将命令逻辑映射到命令的对象。 在本例中命令绑定到系统定义的对于文本框的“复制”、“剪切”、“粘贴”操作

一、命令:

WPF 中的命令是通过实现 ICommand 接口来创建的。ICommand 公开两个方法(Execute 和 CanExecute)和一个事件 (CanExecuteChanged)。Execute 执行与命令关联的操作。CanExecute 确定是否可以在当前命令目标上执行命令。如果集中管理命令操作的命令管理器检测到命令源中发生了更改,此更改可能使得已引发但尚未由命令绑定执行的命令无效,则将引发 CanExecuteChanged。ICommand 的 WPF 实现是 RoutedCommand 类。

RoutedCommand 上的 Execute 方法在命令目标上引发 PreviewExecuted 和 Executed 事件。RoutedCommand 上的 CanExecute 方法在命令目标上引发 CanExecute 和 PreviewCanExecute 事件。这些事件沿元素树以隧道和冒泡形式传递,直到遇到具有该特定命令的 CommandBinding 的对象。

WPF 提供了一组常用的路由命令,这组命令分布在几个类中:MediaCommands、ApplicationCommands、NavigationCommands、ComponentCommands 和 EditingCommands。这些类仅包含 RoutedCommand 对象,而不包含命令的实现逻辑。实现逻辑由在其上执行命令的对象负责。

WPF已封装的命令类有:

  • ApplicationCommands: Close、Cut、Copy、Paste、Save、Print
  • NavigationCommands: BrowseForward、BrowseBack、Zoom、Search
  • EditingCommands: AlignXXX、MoveXXX、SelectXXX
  • MediaCommands: Play、Pause、NextTrack、IncreaseVolume、Record、Stop
  • ComponentCommands: MoveXXX、SelectXXX、ScrollXXX、ExtendSelectionXXX

XXX 代表操作的集合,例如 MoveNext 和 MovePrevious。其中ApplicationCommands为默认的命令类,引用其中的命令时可以省略ApplicationCommands。

二、命令源

命令源是调用命令的对象。例如,MenuItem、Button 和 KeyGesture 就是命令源。

WPF 中的命令源通常实现 ICommandSource 接口。

ICommandSource 公开三个属性:Command、CommandTarget 和 CommandParameter:

  • Command 是在调用命令源时执行的命令。
  • CommandTarget 是要在其上执行命令的对象。值得注意的是,在 WPF 中,ICommandSource 上的 CommandTarget 属性只有在 ICommand 是 RoutedCommand 时才适用。如果在 ICommandSource 上设置了 CommandTarget,而对应的命令不是 RoutedCommand,将会忽略命令目标。如果未设置 CommandTarget,则具有键盘焦点的元素将是命令目标。
  • CommandParameter 是用户定义的数据类型,用于将信息传递到实现命令的处理程序。

实现 ICommandSource 的 WPF 类包括:ButtonBase、MenuItem、Hyperlink 以及 InputBinding。ButtonBase、MenuItem 和 Hyperlink 在被单击时调用命令,InputBinding 在与之关联的 InputGesture 执行时调用命令。

ButtonBase等直接使用控件的Command属性绑定命令:

  <Button Command="ApplicationCommands.Copy" />

而InputBinding使用KeyBinding或MouseBinding绑定特定的输入手势到某一命令上:

例如在Window上注册Ctrl+F2快捷键到ApplicationCommands.Open上:

  <KeyBinding Command="ApplicationCommands.Open"
              Key="F2" Modifiers="Control" />

三、命令目标

命令目标是在其上执行命令的元素。对于 RoutedCommand 而言,命令目标是 Executed 和 CanExecute 的路由的起始元素。前面已提到,在 WPF 中,ICommandSource 上的 CommandTarget 属性只有在 ICommand 是一个 RoutedCommand 时才适用。如果在 ICommandSource 上设置了 CommandTarget,而对应的命令不是 RoutedCommand,将会忽略命令目标。

命令源可以显式设置命令目标。如果未定义命令目标,则具有键盘焦点的元素将用作命令目标。将具有键盘焦点的元素用作命令目标的一个好处是,应用程序开发人员可以使用同一个命令源在多个目标上调用命令,而不必跟踪命令目标。例如,如果 MenuItem 在具有一个 TextBox 控件和一个 PasswordBox 控件的应用程序中调用“粘贴”命令,则目标既可以是 TextBox,也可以是 PasswordBox,具体取决于哪个控件具有键盘焦点。

如将Paste命令设置到mainText控件上:

  <MenuItem x:Name="menuPaste" Header="Paste" 
            Command="ApplicationCommands.Paste"
            CommandTarget="{Binding ElementName=txtMain}"/>

四、命令绑定

CommandBinding 将一个命令与实现该命令的事件处理程序关联。

CommandBinding 类包含一个 Command 属性以及 PreviewExecuted、Executed、PreviewCanExecute 和 CanExecute 事件。

Command 是 CommandBinding 要与之关联的命令。附加到 PreviewExecuted 和 Executed 事件的事件处理程序实现命令逻辑。附加到 PreviewCanExecute 和 CanExecute 事件的事件处理程序确定命令是否可以在当前命令目标上执行。

  • CanExecute事件和PreviewCanExecute事件,通过其EventArgs参数中的CanExecute属性,设置其命令是否可以执行,并且系统会自动的和命令目标的某些特定属性进行绑定,例如Button、MenuItem等在CanExecute属性的值设为False时,会“灰化”,不可用
  • Executed事件和PreviewExecuted事件的代码,是执行命令的真正代码。

例如,将ApplicationCommans.Save绑定到菜单栏的Save菜单项中,并在文本框中没有任何文本时不可用:

  <Window x:Class="InputCommandAndFocus.WinCommandDemo"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="WinCommandDemo" Height="300" Width="300" Focusable="True">
      <Window.InputBindings>
          <KeyBinding Command="ApplicationCommands.Save"
                      Key="F3" Modifiers="Control" />
      </Window.InputBindings>
      <Window.CommandBindings>
         <CommandBinding Command="ApplicationCommands.Save"
                         CanExecute="CommandBinding_Save_CanExecute"
                         Executed="CommandBinding_Save_Executed" />
     </Window.CommandBindings>
     <Grid>
         <Grid.RowDefinitions>
             <RowDefinition Height="23" />
             <RowDefinition />
         </Grid.RowDefinitions>
         <Menu Grid.Row="0" Grid.Column="0">
             <MenuItem Header="File">
                 <MenuItem x:Name="menuSave" Header="Save"
                       Command="ApplicationCommands.Save" />
             </MenuItem>
         </Menu>
         <TextBox Grid.Row="1" Grid.Column="0" x:Name="mainText" 
              TextWrapping="Wrap" AcceptsReturn="True" />
     </Grid>
 </Window>
  private void CommandBinding_Save_CanExecute(object sender, CanExecuteRoutedEventArgs e)
  {
      if (mainText.Text == string.Empty)
      {
          // 如果文本框中没有任何文本,则不可以保存
          e.CanExecute = false;
      }
      else
      {
         e.CanExecute = true;
     }
 }

 private void CommandBinding_Save_Executed(object sender, ExecutedRoutedEventArgs e)
 {
     // 保存文件对话框
     SaveFileDialog save = new SaveFileDialog();
     save.Filter = "文本文件|*.txt|所有文件|*.*";

     bool? result = save.ShowDialog();
     if (result.Value)
     {
         // 执行保存文件操作
     }
 }

End at 2013/8/3 16:23:23 常伟华,感谢您

知识共享许可协议
《WPF and Silverlight 学习笔记2》 常伟华 创作。
本作品采用知识共享署名-相同方式共享 4.0 国际许可协议 | 3.0 中国大陆许可协议进行许可。

站内公告