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 类型的公共基类链,自顶向下:
DispatcherObject(WindowsBase, abstract)——提供线程亲和性(Dispatcher),是所有 WPF UI 类型的最顶层基类。DependencyObject(WindowsBase)——WPF 属性系统的根,GetValue/SetValue/ 依赖属性 / 附加属性都由它支撑;还派生了大量非”元素”类型(Freezable体系、Style、Trigger、BindingBase、动画等)。Visual(PresentationCore, abstract)——2D 渲染基元,进入可视树的门槛;与它平级的是 3D 兄弟Visual3D(都直接继承DependencyObject)。
| 类 | 声明 | 文件 |
|---|---|---|
DispatcherObject | public abstract class DispatcherObject | WindowsBase/System/Windows/Threading/DispatcherObject.cs:21 |
DependencyObject | public class DependencyObject : DispatcherObject | WindowsBase/System/Windows/DependencyObject.cs:41 |
Visual | public abstract partial class Visual : DependencyObject, DUCE.IResource | PresentationCore/System/Windows/Media/Visual.cs:77 |
Visual3D | public abstract partial class Visual3D : DependencyObject, DUCE.IResource, IVisual3DContainer | PresentationCore/System/Windows/Media3D/Visual3D.cs:15 |
二、输入契约:IInputElement 接口
IInputElement(PresentationCore/System/Windows/IInputElement.cs)是接口支路,不在类继承链上——UIElement、ContentElement、UIElement3D 各自以”实现接口”的方式接入它。
- 声明为
public interface IInputElement,不带任何基接口——它不继承IDisposable、IAnimatable等任何接口,是类型层次中的根节点,也没有同级兄弟接口。 - 定义的输入核心契约:
- 事件:
RaiseEvent/AddHandler/RemoveHandler - 捕获:
CaptureMouse/ReleaseMouseCapture/CaptureStylus/ReleaseStylusCapture - 焦点:
Focus - 输入状态属性:
IsKeyboardFocusWithin、IsMouseOver等
- 事件:
- 三条实现分支对应三棵元素树:
- UIElement——2D 可视元素
- ContentElement——流文档等内容元素
- UIElement3D——3D 元素
| 类 | 声明 | 文件 |
|---|---|---|
UIElement | public partial class UIElement : Visual, IInputElement, IAnimatable | PresentationCore/System/Windows/UIElement.cs:67 |
ContentElement | public partial class ContentElement : DependencyObject, IInputElement, IAnimatable | PresentationCore/System/Windows/ContentElement.cs:20 |
UIElement3D | public abstract partial class UIElement3D : Visual3D, IInputElement | PresentationCore/System/Windows/UIElement3D.cs:25 |
注意:ContentElement 直接继承 DependencyObject,不经过 Visual——它没有自己的渲染,寄宿在 TextBlock / FlowDocumentScrollViewer 等容器中。
三、框架层扩展:IFrameworkInputElement
- 源码位置:
PresentationFramework/System/Windows/IFrameworkInputElement.cs:9,声明为public interface IFrameworkInputElement : IInputElement——它是IInputElement唯一的派生接口。 - 只在
IInputElement基础上增加一个成员:string Name { get; set; },把框架层的属性概念(元素名,用于RegisterName/FindName/ XAMLx:Name)纳入输入契约。 - 实现者恰好是”框架层双兄弟”:
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 | 图像 / 媒体(终类) | — |
Shape | 2D 图形基类 | Rectangle、Ellipse、Line、Path、Polygon、Polyline |
InkCanvas / InkPresenter | 墨迹承载 | — |
Viewport3D | 3D 内容宿主(终类) | — |
AdornerDecorator | 装饰器层宿主(内部常用) | — |
AccessText | 带助记符的文本 | — |
FrameworkContentElement 支路的组件(同样实现 IFrameworkInputElement):
TextElement→Run、Span(Bold、Italic、Underline、Hyperlink)、InlineUIContainer、FigureBlock→Paragraph、Section、List/ListItem、Table(TableRowGroup/TableRow/TableCell)FlowDocument(继承Block)
判定规则:
// 某组件有 x:Name 语义
// ⇔ 继承自 FrameworkElement 或 FrameworkContentElement
// ⇔ 实现了 IFrameworkInputElement
反过来,纯 PresentationCore 层的 UIElement / ContentElement / UIElement3D 只实现 IInputElement,没有 Name,也无法用 FindName 查找。
五、例外与叶子类型
- CaretSubElement:声明于 CaretElement.cs:1187 的
private class CaretSubElement : UIElement,是CaretElement的私有嵌套类,作为文本插入符(caret)在可视树中的呈现载体。它是叶子类型——全仓库无任何派生,且因直接继承UIElement而非FrameworkElement,只有IInputElement能力,没有Name/x:Name语义(前表中”例外”的出处)。
关键结论
- 完整主链:
DispatcherObject→DependencyObject→Visual→UIElement→FrameworkElement→Control/Panel/ … IInputElement是接口支路,不在类继承链上;IFrameworkInputElement是它唯一的派生接口,仅增加Name。ContentElement直接继承DependencyObject,不经过Visual;Visual与Visual3D是平级兄弟(2D vs 3D 渲染基元)。- 两条支路在框架层汇合:
FrameworkElement与FrameworkContentElement都实现IFrameworkInputElement,共享同一个NameProperty。 DependencyObject还派生了大量非”元素”类型,是整个 WPF 属性系统的根;DispatcherObject提供线程亲和性,是所有 WPF UI 类型的最顶层基类。