Posts

WPF IInputElement 继承体系

IInputElement 是 WPF 输入体系的根接口(不带任何基接口),定义了 RaiseEvent / AddHandler / CaptureMouse / Focus 等输入核心契约;而类继承主链是 DispatcherObject → DependencyObject → Visual → UIElement → FrameworkElement。

总览图

object
└── DispatcherObject                          (WindowsBase, abstract)
    └── DependencyObject                      (WindowsBase)
        ├── Visual                            (PresentationCore/System/Windows/Media, abstract)
        │   └── UIElement : Visual, IInputElement, IAnimatable
        │       ├── FrameworkElement          (PresentationFramework)
        │       │   └── Control / Panel / Border / ...
        │       └── CaretSubElement           (PresentationFramework, 内部文本插入符元素)
        ├── Visual3D                          (PresentationCore/System/Windows/Media3D, abstract)
        │   └── UIElement3D : Visual3D, IInputElement
        │       ├── ContainerUIElement3D
        │       └── ModelUIElement3D
        ├── ContentElement : DependencyObject, IInputElement, IAnimatable
        │   └── FrameworkContentElement       (PresentationFramework)
        └── 其他非元素类型:Freezable / AnimationTimeline / Style / Trigger / BindingBase ...

IInputElement                                 (PresentationCore, 根接口)
├── UIElement
├── ContentElement
├── UIElement3D
└── IFrameworkInputElement                    (PresentationFramework, 接口)
    ├── FrameworkElement                      (同时实现)
    └── FrameworkContentElement               (同时实现)

一、类继承主链:DispatcherObject → DependencyObject → Visual

WPF 所有 UI 类型的公共基类链,自顶向下:

  1. DispatcherObject(WindowsBase, abstract)——提供线程亲和性(Dispatcher),是所有 WPF UI 类型的最顶层基类。
  2. DependencyObject(WindowsBase)——WPF 属性系统的根,GetValue / SetValue / 依赖属性 / 附加属性都由它支撑;还派生了大量非”元素”类型(Freezable 体系、Style、Trigger、BindingBase、动画等)。
  3. Visual(PresentationCore, abstract)——2D 渲染基元,进入可视树的门槛;与它平级的是 3D 兄弟 Visual3D(都直接继承 DependencyObject)。
类声明文件
DispatcherObjectpublic abstract class DispatcherObjectWindowsBase/System/Windows/Threading/DispatcherObject.cs:21
DependencyObjectpublic class DependencyObject : DispatcherObjectWindowsBase/System/Windows/DependencyObject.cs:41
Visualpublic abstract partial class Visual : DependencyObject, DUCE.IResourcePresentationCore/System/Windows/Media/Visual.cs:77
Visual3Dpublic abstract partial class Visual3D : DependencyObject, DUCE.IResource, IVisual3DContainerPresentationCore/System/Windows/Media3D/Visual3D.cs:15

二、输入契约:IInputElement 接口

IInputElement(PresentationCore/System/Windows/IInputElement.cs)是接口支路,不在类继承链上——UIElement、ContentElement、UIElement3D 各自以”实现接口”的方式接入它。

类声明文件
UIElementpublic partial class UIElement : Visual, IInputElement, IAnimatablePresentationCore/System/Windows/UIElement.cs:67
ContentElementpublic partial class ContentElement : DependencyObject, IInputElement, IAnimatablePresentationCore/System/Windows/ContentElement.cs:20
UIElement3Dpublic abstract partial class UIElement3D : Visual3D, IInputElementPresentationCore/System/Windows/UIElement3D.cs:25

注意:ContentElement 直接继承 DependencyObject,不经过 Visual——它没有自己的渲染,寄宿在 TextBlock / FlowDocumentScrollViewer 等容器中。

三、框架层扩展:IFrameworkInputElement

IInputElement
└── IFrameworkInputElement           (PresentationFramework)
    ├── FrameworkElement             (= UIElement 支路的框架层)
    └── FrameworkContentElement      (= ContentElement 支路的框架层)

两条支路在框架层汇合:FrameworkElement 与 FrameworkContentElement 各自继承不同的 PresentationCore 基类(UIElement / ContentElement),但都实现 IFrameworkInputElement,因此 FocusManager.GetFocusedElement、命令路由等 API 可以用统一的 IInputElement / IFrameworkInputElement 类型同时操作两者。

Name 的实现方式:

实现者实现方式源码位置
FrameworkElement定义依赖属性 NameProperty(DependencyProperty.Register)+ CLR 包装器FrameworkElement.cs:3190-3212
FrameworkContentElement通过 NameProperty.AddOwner(...) 复用同一个 DP + 自己的 CLR 包装器FrameworkContentElement.cs:811-824

两者共享同一个 FrameworkElement.NameProperty,且都在 OnPropertyChanged 中把名字变化注册进 NameScope(FrameworkElement.cs:2068、FrameworkContentElement.cs:696),所以 RegisterName / FindName 在元素树和内容树中行为一致。

四、框架层组件:FrameworkElement 的派生类型

直接继承 FrameworkElement 的类型(即直接实现 IFrameworkInputElement 的框架层组件):

直接派生类说明代表性二级派生
Control所有交互控件的基类ButtonBase(Button/CheckBox/RadioButton)、TextBox、ComboBox、ListBox/ListView/TreeView、DataGrid、Menu/MenuItem、ToolBar、TabControl/TabItem、Slider、Calendar、DatePicker
ContentControl(→Control)内容控件基类Button、Label、GroupBox、Expander、Window(→NavigationWindow)、Page、UserControl、HeaderedContentControl
Panel布局容器基类Grid、StackPanel、DockPanel、WrapPanel、Canvas、UniformGrid、VirtualizingPanel(→VirtualizingStackPanel)
TextBlock文本显示(终类)—
Border单子元素装饰边框(终类)—
Image / MediaElement图像 / 媒体(终类)—
Shape2D 图形基类Rectangle、Ellipse、Line、Path、Polygon、Polyline
InkCanvas / InkPresenter墨迹承载—
Viewport3D3D 内容宿主(终类)—
AdornerDecorator装饰器层宿主(内部常用)—
AccessText带助记符的文本—

FrameworkContentElement 支路的组件(同样实现 IFrameworkInputElement):

判定规则:

// 某组件有 x:Name 语义
// ⇔ 继承自 FrameworkElement 或 FrameworkContentElement
// ⇔ 实现了 IFrameworkInputElement

反过来,纯 PresentationCore 层的 UIElement / ContentElement / UIElement3D 只实现 IInputElement,没有 Name,也无法用 FindName 查找。

五、例外与叶子类型

关键结论

原文链接