XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEToolBox

【WPF】XFE工具箱

公开
关注 0 Fork 0 Star 0
返回提交历史

XFEstudio/XFEToolBox

优化控件默认行为与样式兼容性,补充测试文档

本次变更: - DataGridAssist 优化样式应用逻辑,避免覆盖用户自定义列样式。 - ProgressRing 默认不活跃,默认值为 0,调整动画绘制逻辑。 - PopupHelper 增强滚动条样式兼容性,新增文本弹窗重载。 - 同步更新 tool-packages.md,补充新 API 及样式查找说明。 - 新增统一控件默认行为相关测试用例,验证样式与默认值。

715a04d
XFE工作室室长 <mail@xfegzs.com>
提交于

代码差异

5 个文件 +118 -33
Added XFEToolBox.Client.Wpf.Test/UnifiedControlDefaultsTests.cs +67 -0
@@ -0,0 +1,67 @@
1 using System.Windows;
2 using System.Windows.Controls;
3 using XFEToolBox.WpfCore.Controls;
4
5 namespace XFEToolBox.Client.Wpf.Test;
6
7 public static class UnifiedControlDefaultsTests
8 {
9 [Test]
10 public static void DataGridGeneratedColumnsReceiveUnifiedStylesWithoutOverwritingExplicitStyles()
11 {
12 RunSta(() =>
13 {
14 var elementStyle = new Style(typeof(CheckBox));
15 var editingStyle = new Style(typeof(CheckBox));
16 var grid = new DataGrid();
17 DataGridAssist.SetCheckBoxElementStyle(grid, elementStyle);
18 DataGridAssist.SetCheckBoxEditingStyle(grid, editingStyle);
19 DataGridAssist.SetUseUnifiedCellControls(grid, true);
20
21 var generatedColumn = new DataGridCheckBoxColumn();
22 grid.Columns.Add(generatedColumn);
23 Ensure(ReferenceEquals(generatedColumn.ElementStyle, elementStyle),
24 "DataGridCheckBoxColumn 仍在使用 WPF 系统默认显示样式。");
25 Ensure(ReferenceEquals(generatedColumn.EditingElementStyle, editingStyle),
26 "DataGridCheckBoxColumn 仍在使用 WPF 系统默认编辑样式。");
27
28 var explicitStyle = new Style(typeof(CheckBox));
29 var explicitColumn = new DataGridCheckBoxColumn { ElementStyle = explicitStyle };
30 grid.Columns.Add(explicitColumn);
31 Ensure(ReferenceEquals(explicitColumn.ElementStyle, explicitStyle),
32 "统一样式覆盖了页面作者显式指定的列样式。");
33 });
34 }
35
36 [Test]
37 public static void ProgressRingDefaultsToZeroAndInactive()
38 {
39 RunSta(() =>
40 {
41 var ring = new ProgressRing();
42 Ensure(Math.Abs(ring.Value) < double.Epsilon, "ProgressRing 默认 Value 不是 0。");
43 Ensure(!ring.IsActive, "ProgressRing 默认状态仍会启动进度动画。");
44 });
45 }
46
47 private static void RunSta(Action action)
48 {
49 Exception? failure = null;
50 var thread = new Thread(() =>
51 {
52 try { action(); }
53 catch (Exception exception) { failure = exception; }
54 });
55 thread.SetApartmentState(ApartmentState.STA);
56 thread.Start();
57 Ensure(thread.Join(TimeSpan.FromSeconds(10)), "WPF 控件测试超时。");
58 if (failure is not null)
59 throw new InvalidOperationException(failure.Message, failure);
60 }
61
62 private static void Ensure(bool condition, string message)
63 {
64 if (!condition)
65 throw new InvalidOperationException(message);
66 }
67 }
Modified XFEToolBox.WpfCore/Controls/DataGridAssist.cs +11 -6
@@ -168,21 +168,26 @@ public static class DataGridAssist
168 168
169 169 private static void ApplyStylesIfUnset(DataGridBoundColumn column, Style? elementStyle, Style? editingStyle)
170 170 {
171 // 显式设置的列样式代表页面作者的主动选择,只替换 WPF 自动回退的系统默认样式。
172 if (column.ElementStyle is null && elementStyle is not null)
171 // DataGridCheckBoxColumn 等类型通过属性元数据提供非 null 的系统默认样式。
172 // ReadLocalValue 才能区分“页面作者显式设置”和“WPF 元数据回退”。
173 if (column.ReadLocalValue(DataGridBoundColumn.ElementStyleProperty) == DependencyProperty.UnsetValue
174 && elementStyle is not null)
173 175 column.ElementStyle = elementStyle;
174 176
175 if (column.EditingElementStyle is null && editingStyle is not null)
177 if (column.ReadLocalValue(DataGridBoundColumn.EditingElementStyleProperty) == DependencyProperty.UnsetValue
178 && editingStyle is not null)
176 179 column.EditingElementStyle = editingStyle;
177 180 }
178 181
179 182 private static void ApplyStylesIfUnset(DataGridComboBoxColumn column, Style? elementStyle, Style? editingStyle)
180 183 {
181 // DataGridComboBoxColumn 不继承 DataGridBoundColumn,但拥有独立的同名样式属性。
182 if (column.ElementStyle is null && elementStyle is not null)
184 // DataGridComboBoxColumn 不继承 DataGridBoundColumn,但拥有独立的同名依赖属性。
185 if (column.ReadLocalValue(DataGridComboBoxColumn.ElementStyleProperty) == DependencyProperty.UnsetValue
186 && elementStyle is not null)
183 187 column.ElementStyle = elementStyle;
184 188
185 if (column.EditingElementStyle is null && editingStyle is not null)
189 if (column.ReadLocalValue(DataGridComboBoxColumn.EditingElementStyleProperty) == DependencyProperty.UnsetValue
190 && editingStyle is not null)
186 191 column.EditingElementStyle = editingStyle;
187 192 }
188 193 }
Modified XFEToolBox.WpfCore/Controls/ProgressRing.cs +9 -7
@@ -16,7 +16,7 @@ public class ProgressRing : RangeBase
16 16
17 17 public static readonly DependencyProperty IsActiveProperty = DependencyProperty.Register(
18 18 nameof(IsActive), typeof(bool), typeof(ProgressRing),
19 new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsRender, OnAnimationStateChanged));
19 new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsRender, OnAnimationStateChanged));
20 20
21 21 public static readonly DependencyProperty IsIndeterminateProperty = DependencyProperty.Register(
22 22 nameof(IsIndeterminate), typeof(bool), typeof(ProgressRing),
@@ -27,6 +27,13 @@ public class ProgressRing : RangeBase
27 27 new FrameworkPropertyMetadata(3d, FrameworkPropertyMetadataOptions.AffectsMeasure | FrameworkPropertyMetadataOptions.AffectsRender,
28 28 null, CoerceRingThickness));
29 29
30 static ProgressRing()
31 {
32 ValueProperty.OverrideMetadata(
33 typeof(ProgressRing),
34 new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.AffectsRender));
35 }
36
30 37 public ProgressRing()
31 38 {
32 39 Loaded += (_, _) => UpdateAnimation();
@@ -69,10 +76,7 @@ public class ProgressRing : RangeBase
69 76
70 77 drawingContext.DrawEllipse(null, trackPen, center, radius, radius);
71 78
72 if (!IsActive)
73 drawingContext.PushOpacity(0.34);
74
75 if (IsIndeterminate)
79 if (IsActive && IsIndeterminate)
76 80 {
77 81 DrawArc(drawingContext, progressPen, center, radius, -90 + (double)GetValue(RotationAngleProperty), 96);
78 82 }
@@ -86,8 +90,6 @@ public class ProgressRing : RangeBase
86 90 DrawArc(drawingContext, progressPen, center, radius, -90, progress * 359.9);
87 91 }
88 92
89 if (!IsActive)
90 drawingContext.Pop();
91 93 }
92 94
93 95 protected override void OnMinimumChanged(double oldMinimum, double newMinimum)
Modified XFEToolBox/Utilities/PopupHelper.cs +28 -19
@@ -20,29 +20,32 @@ public static class PopupHelper
20 20 return dialogPage;
21 21 }
22 22
23 private static ScrollViewer CreateTextContent(string text, Color textColor) => new()
23 private static ScrollViewer CreateTextContent(string text, Color textColor)
24 24 {
25 Content = new TextBlock
26 {
27 Text = text,
28 Foreground = new SolidColorBrush(textColor),
29 Margin = new Thickness(20, 20, 20, 0),
30 TextWrapping = TextWrapping.WrapWithOverflow
31 },
32 HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled,
33 VerticalScrollBarVisibility = ScrollBarVisibility.Auto,
34 Resources = new ResourceDictionary
25 var content = new ScrollViewer
35 26 {
27 Content = new TextBlock
36 28 {
37 typeof(ScrollBar),
38 new Style
39 {
40 TargetType = typeof(ScrollBar),
41 BasedOn = (Style)Application.Current.FindResource("ConsoleScrollBar")
42 }
43 }
29 Text = text,
30 Foreground = new SolidColorBrush(textColor),
31 Margin = new Thickness(20, 20, 20, 0),
32 TextWrapping = TextWrapping.WrapWithOverflow
33 },
34 HorizontalScrollBarVisibility = ScrollBarVisibility.Disabled,
35 VerticalScrollBarVisibility = ScrollBarVisibility.Auto
36 };
37
38 // 精简工具宿主只加载 ToolThemeResources,不一定包含主程序遗留的 ConsoleScrollBar 键。
39 // 优先复用可用的工具箱样式;两者都不存在时保留隐式/系统 ScrollBar 样式,不能让弹窗崩溃。
40 var scrollBarStyle = Application.Current?.TryFindResource("ConsoleScrollBar") as Style
41 ?? Application.Current?.TryFindResource("ToolBoxScrollBarStyle") as Style;
42 if (scrollBarStyle is not null)
43 {
44 content.Resources[typeof(ScrollBar)] = new Style(typeof(ScrollBar), scrollBarStyle);
44 45 }
45 };
46
47 return content;
48 }
46 49
47 50 public static MessageBoxResult? ShowConfirmDialog(object content, bool showCancelButton = false, string confirmText = "确定", string cancelText = "取消")
48 51 => ShowConfirmDialog(content, new PopupWindowOptions(), showCancelButton, confirmText, cancelText);
@@ -62,6 +65,9 @@ public static class PopupHelper
62 65
63 66 public static MessageBoxResult? ShowConfirmDialog(string text, bool showCancelButton = false, string confirmText = "确定", string cancelText = "取消") => ShowConfirmDialog(text, Colors.Black, showCancelButton, confirmText, cancelText);
64 67
68 public static MessageBoxResult? ShowConfirmDialog(string text, PopupWindowOptions options, bool showCancelButton = false, string confirmText = "确定", string cancelText = "取消") =>
69 ShowConfirmDialog(CreateTextContent(text, Colors.Black), options, showCancelButton, confirmText, cancelText);
70
65 71 public static MessageBoxResult? ShowYesOrNoDialog(object content, bool showCancelButton = false, string yesText = "是", string noText = "否")
66 72 => ShowYesOrNoDialog(content, new PopupWindowOptions(), showCancelButton, yesText, noText);
67 73
@@ -81,6 +87,9 @@ public static class PopupHelper
81 87
82 88 public static MessageBoxResult? ShowYesOrNoDialog(string text, bool showCancelButton = false, string yesText = "是", string noText = "否") => ShowYesOrNoDialog(text, Colors.Black, showCancelButton, yesText, noText);
83 89
90 public static MessageBoxResult? ShowYesOrNoDialog(string text, PopupWindowOptions options, bool showCancelButton = false, string yesText = "是", string noText = "否") =>
91 ShowYesOrNoDialog(CreateTextContent(text, Colors.Black), options, showCancelButton, yesText, noText);
92
84 93 public static MessageBoxResult? ShowDialog(object content, double width = 320, double height = 230) =>
85 94 ShowDialog(content, new PopupWindowOptions { Width = width, Height = height });
86 95
Modified docs/tool-packages.md +3 -1
@@ -774,15 +774,17 @@ var result = PopupHelper.ShowDialog(
774 774 | `ShowConfirmDialog(object content, bool showCancelButton = false, string confirmText = "确定", string cancelText = "取消")` | 确定对话框,可选取消按钮 |
775 775 | `ShowConfirmDialog(string text, Color textColor, bool showCancelButton = false, string confirmText = "确定", string cancelText = "取消")` | 文本版本,自定义颜色 |
776 776 | `ShowConfirmDialog(string text, bool showCancelButton = false, string confirmText = "确定", string cancelText = "取消")` | 文本版本,默认黑色 |
777 | `ShowConfirmDialog(string text, PopupWindowOptions options, ...)` | 文本版本,并使用完整窗口配置 |
777 778 | `ShowYesOrNoDialog(object content, bool showCancelButton = false, string yesText = "是", string noText = "否")` | 是/否对话框,可选取消按钮 |
778 779 | `ShowYesOrNoDialog(string text, Color textColor, ...)` | 文本与颜色版本 |
779 780 | `ShowYesOrNoDialog(string text, bool showCancelButton = false, ...)` | 默认文本版本 |
781 | `ShowYesOrNoDialog(string text, PopupWindowOptions options, ...)` | 文本版本,并使用完整窗口配置 |
780 782 | `ShowDialog(object content, double width = 320, double height = 230)` | 使用默认外壳显示任意内容 |
781 783 | `ShowDialog(object content, PopupWindowOptions options)` | 完整配置版本 |
782 784
783 785 返回值均为 `MessageBoxResult?`。确认、是、否、取消分别使用 `OK`、`Yes`、`No`、`Cancel`;标题栏关闭或 `Esc` 返回 `None`,窗口在结果赋值前被外部关闭时也可能为 `null`。
784 786
785 > 当前源码中的 `string` 便捷重载仍会查找旧资源键 `ConsoleScrollBar`,而工具主题没有提供该键。在该兼容问题修复前,工具应使用 `object content` 重载并自行传入采用主题画刷的 `TextBlock` / `ScrollViewer`,上方示例即为安全写法。
787 文本便捷重载会优先复用工具箱滚动条样式;精简工具宿主未提供相应资源键时会自动回退到隐式或系统样式,不会因缺少资源而中断工具运行。
786 788
787 789 ### `PopupWindowOptions`
788 790