前言
WPF 是一个强大且灵活的 UI 框架,它不仅提供了丰富的内置控件,还支持高度定制化的控件开发。对于想要深入掌握 WPF 的开发者来说,理解控件的继承体系是迈出自定义控件开发的第一步。
本文将带你梳理 WPF 中用于开发自定义控件的核心继承类,并通过一个完整的实战案例,实现如何从零开始开发一个自绘圆形进度条控件。
WPF 控件继承体系详解
WPF 的控件体系庞大而有序,每一层都有其独特的职责和定位。
为了帮助大家快速选择合适的基类来实现你的控件目标,整理了下面这份控件继承类汇总表,并附上每个类的用途说明和典型应用场景。
控件继承类汇总表
| 继承类 | 继承自 | 用途/定位 | 是否参与布局 | 可否包含子元素 | 典型用途示例 |
|---|---|---|---|---|---|
Visual | DependencyObject | 最底层绘图元素,无布局、无交互 | ❌ | ❌ | 图形渲染、构建低级绘图控件 |
DrawingVisual | Visual | 可绘制内容的轻量级元素,适合自绘型 UI | ❌ | ❌ | 图形编辑器、缩略图、图层渲染控件 |
UIElement | Visual | 增加了布局、输入、焦点、事件支持 | ✅ | ❌ | 自定义输入处理控件,如手势控件 |
FrameworkElement | UIElement | 提供布局、样式、数据绑定等完整基础 | ✅ | ✅ | 自定义容器、自绘控件、数据绑定控件 |
Decorator | FrameworkElement | 只能包含一个子元素,用于包裹和增强子元素功能 | ✅ | ✅(单个) | Border、自定义阴影或边框控件 |
Panel | FrameworkElement | 容器控件,可布局多个子元素 | ✅ | ✅(多个) | Grid、StackPanel、自定义布局容器 |
Control | FrameworkElement | 基础控件类,支持样式和模板 | ✅ | ✅(可定义) | 创建样式化可重用控件,如开关、进度条等 |
ContentControl | Control | 支持一个内容的控件,内容可为任意对象 | ✅ | ✅(一个) | 自定义按钮、卡片、标签等 |
ItemsControl | Control | 支持多个内容项的控件,通常用于数据列表 | ✅ | ✅(多个) | 列表、标签组、自定义列表控件 |
Shape | FrameworkElement | 用于矢量图形绘制(线、圆、矩形等) | ✅ | ❌ | 自定义图形控件(如频谱图、路径动画等) |
Adorner | FrameworkElement | 用于视觉装饰其他控件的叠加层 | ✅ | ✅ | 拖拽提示框、元素边框装饰、拖放指示等 |
UserControl | ContentControl | 组合控件的容器,适合快速搭建封装控件(不是最底层) | ✅ | ✅ | 快速开发组合控件,如登录框、自定义弹窗等 |
继承建议速查
| 目标 | 推荐继承类 | 理由 |
|---|---|---|
| 需要完全自绘和极致性能 | DrawingVisual / UIElement | 控制精细,性能高,适合复杂绘图控件(但功能需全手动实现) |
| 自定义布局容器 | Panel | 完全控制子元素的布局规则 |
| 做一个轻量但样式可变的控件 | Control | 支持样式、模板、绑定,是自定义控件的标准方式 |
| 做带有子内容的容器控件 | ContentControl / ItemsControl | 继承 Control,支持模板化和内容绑定 |
| 做快速组合控件(例如封装控件树) | UserControl | 简洁方便,但不适合深度定制的可重用控件 |
| 做装饰或增强其他控件 | Decorator / Adorner | 无需重新绘制,可复用现有控件外观 |
实战案例
自绘圆形进度条 RingProgressBar
以一个实际项目中常用的控件——圆形进度条为例,演示如何从头创建一个完全自绘的控件。
这个控件不会依赖任何现成的 UI 元素(比如 Border、TextBlock),而是通过重写 OnRender 方法,使用 DrawingContext 手动绘制出一个漂亮的进度环。
控件实现
using System;
using System.Windows;
using System.Windows.Media;
using System.Windows.Controls;
/// <summary>
/// 自定义圆环进度条控件,继承自 FrameworkElement,重写 OnRender 方法进行绘制。
/// </summary>
public class RingProgressBar : FrameworkElement
{
/// <summary>
/// 声明并注册一个依赖属性 Progress,用于控制进度条显示的百分比(0 ~ 100)。
/// </summary>
public static readonly DependencyProperty ProgressProperty =
DependencyProperty.Register(
nameof(Progress),
typeof(double),
typeof(RingProgressBar),
new FrameworkPropertyMetadata(
0.0,
FrameworkPropertyMetadataOptions.AffectsRender));
/// <summary>
/// Progress 属性包装器,提供进度值的获取和设置。
/// 设置时自动裁剪到 0~100 范围。
/// </summary>
public double Progress
{
get => (double)GetValue(ProgressProperty);
set => SetValue(ProgressProperty, Math.Max(0, Math.Min(100, value)));
}
/// <summary>
/// 重写 OnRender 方法,使用 DrawingContext 绘制控件外观。
/// </summary>
protected override void OnRender(DrawingContext dc)
{
double width = ActualWidth;
double height = ActualHeight;
double radius = Math.Min(width, height) / 2 - 5;
Point center = new(width / 2, height / 2);
// 绘制背景圆环(灰色)
dc.DrawEllipse(
null,
new Pen(Brushes.LightGray, 10),
center,
radius, radius);
// 计算角度
double angle = Progress / 100 * 360;
double radians = (angle - 90) * Math.PI / 180;
// 圆环起点
Point startPoint = new(center.X, center.Y - radius);
Point endPoint = new(
center.X + radius * Math.Cos(radians),
center.Y + radius * Math.Sin(radians));
bool isLargeArc = angle > 180;
PathFigure figure = new(
startPoint,
new[]
{
new ArcSegment(
endPoint,
new Size(radius, radius),
0,
isLargeArc,
SweepDirection.Clockwise,
true)
},
false);
PathGeometry geometry = new();
geometry.Figures.Add(figure);
// 绘制进度圆环(蓝色)
dc.DrawGeometry(
null,
new Pen(Brushes.SteelBlue, 10),
geometry);
}
protected override Size MeasureOverride(Size availableSize) => new Size(100, 100);
protected override Size ArrangeOverride(Size finalSize) => finalSize;
}
使用方式
首先在 XAML 中引入命名空间:
<Window
x:Class="Custom.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ct="clr-namespace:Custom.Controls.Controls;assembly=Custom.Controls"
Title="RingProgressBar 自定义控件预览效果"
Width="800"
Height="450"
WindowStartupLocation="CenterScreen">
<Grid>
<!-- 自定义控件 -->
<ct:RingProgressBar
x:Name="ringProgress"
Width="200"
Height="200"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Progress="0" />
<TextBlock
x:Name="ShowProgressValue"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="24"
Foreground="#d3d3d3" />
<Button
Width="100"
Height="30"
Margin="0,50"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
Click="Button_Click"
Content="🚀 执行" />
</Grid>
</Window>
效果预览
该控件会在一个 200x200 的区域内绘制一个灰色圆环,根据 Progress 属性的变化,动态绘制一段彩色弧线作为进度条。整个控件没有使用任何现有控件元素,完全基于 OnRender 方法完成自定义绘制。
总结
WPF 的自定义控件开发并不神秘,关键在于理解控件的继承层级以及每种基类的适用场景。
-
如果你追求极致性能和完全控制,可以从
DrawingVisual或UIElement开始; -
如果你需要布局支持和样式扩展性,
Control是最佳起点; -
如果你只是想快速封装一组控件,
UserControl是最便捷的选择; -
而如果想开发一个完全自绘的控件,像本文中的
RingProgressBar这样继承FrameworkElement并重写OnRender,就是一条可行之路。
通过不断实践和探索,会发现自定义控件其实并不难,它就像是一把钥匙,打开了一扇通往更高层次 UI 开发的大门。
关键词
WPF、自定义控件、FrameworkElement、DrawingContext、OnRender、RingProgressBar、UIElement、Control、UserControl、Panel
最后
如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。
也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!
优秀是一种习惯,欢迎大家留言学习!
作者:听风睡好觉
出处:blog.csdn.net/yzm3411/article/details/147893442
声明:网络内容,仅供学习,尊重版权,侵权速删,歉意致谢!