您当前的位置: 首页 > 

寒冰屋

暂无认证

  • 11浏览

    0关注

    2286博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

WPF开发人员必读:WPF控件测试台

寒冰屋 发布时间:2022-10-17 21:47:35 ,浏览量:11

介绍

WpfControlTestbench帮助您为您的控件或您想要调查其行为的任何控件编写快速复杂的测试窗口。只需十几行XAML即可创建以下Window内容:

它在左下角显示你要测试的控件,在Window上半部分是你Controls控件的所有属性。您可以在运行时更改它们的值。在右下角,您会看到一个跟踪查看器,其中显示了值已更改的任何属性,以及保存您的控件和控件的WPF容器如何对控件进行布局。

WPF控件支持范围广泛的布局(Margin、Border、Padding、Width/ Height、对齐等)功能,因此开发起来具有挑战性。更难的是测试控件在许多可能的情况下是否正常运行。WpfControlTestbench允许您以可视化和交互方式对此进行测试,您可以立即看到属性值更改对控件的影响。开箱即用,WpfControlTestbench显示从FrameworkElement和Control继承的所有重要属性。当然,您可以轻松地添加更多控件来研究特定于您的控件的属性。

调查您的控件及其WPF父级的交互

控件的行为会有所不同,具体取决于它们在Parent逻辑树中的身份,即,它们放置在哪个WPF ContentControl或Panel中。Canvas可能会提供您的控件想要的所有空间,而StackPanel将限制您的控件的宽度或高度。要让不同WPF ContentControls和您的控件之间的这种交互正常工作可能是相当具有挑战性的。幸运的是,WpfControlTestbench可以很容易地在运行时将您的控件放入ContentControls不同的位置。

调试测量、排列和渲染问题

编写直接渲染到屏幕的WPF Control是一种黑魔法,但它提供了极大的灵活性和最高的速度。使用Visual Studio调试它很困难,因为无法使用断点,它们会在VS尝试切换到您的窗口时不断触发,然后调用您的方法,该方法在该断点处停止并再次显示VS。您可以使用System.Diagnostics.Debug.WriteLine()信息来跟踪该信息,但这可能需要大量工作,而且您不能为父级控件这样做。但通常是与父级控件的相互作用导致问题。但不要害怕,WpfControlTestbench可以为您追踪所有这些信息等等。

在上图中,您可以看到CanvasContainer,它是要测试的Control父级,称为StackPanel。父级获得877个水平像素和381个垂直像素的屏幕空间来安排其子级。子级StackPanel只收到0个像素,因为在测量期间(图中未显示),它没有请求任何空间,因为它是空的。有趣的是,当子进程仍在其父进程的Arrange()调用中时,Render()已经在执行了。

WpfControlTestbench使控件的内部工作透明。它实时显示更改了哪个属性以及父控件和控件如何交互以进行此更改的布局。

在编写自己的控件时,它的行为必须与Microsoft的控件相似。但微软的文档缺乏所需的信息。要检查Microsoft控件的行为,请将其托管在WpfControlTestbench中。对您自己的控件执行相同操作,然后验证您的控件在Microsoft等每个场景中的行为是否相同。

控制尺寸和定位

很难编写一个内容使用所有可用空间的控件,因为可用空间取决于许多因素,例如主机提供的空间、边距、对齐方式等。孩子需要的屏幕宽度的简单公式如下所示:

Required Width = LeftMargin + LeftBorder + LeftPadding + Content Width + 
                 RightPadding + RightBorder + RightMargin

当然,所需的宽度通常与主机可以提供的宽度不同。

  • 当空间太少时会发生什么?空间太大?
  • 如果Width属性被设置会发生什么(注意它不是上面公式的一部分)?
  • 如果Width未定义会发生什么?
  • 如果HorizontalAlignment从Left变为Center、Right或Stretch会发生什么?
  • 发生Font.Size变化时会发生什么?
  • 父级控件或您的控制是否处理Margin?Padding?
答案
  • 空间太少:如果您的控件呈现在给定空间之外,则取决于剪辑是否显示。空间太大:取决于对齐方式。如果未拉伸,则父级(!)将相应地放置内容。如果拉伸,子级应该使用所有可用空间。
  • 设置Width时,您的控件应该完全使用该空间。如果对齐被拉伸,则不应发生拉伸,但父级将使您的控件居中。
  • Width何时未定义,您的控件应该自己确定它需要多少空间。
  • HorizontalAlignment从Left更改为Center或Right对您的控件无关紧要,渲染是相同的,但父级会以不同的方式放置。HorizontalAlignment 从Left更改为Stretch将要求您的控件使用所有可用大小进行渲染,而不仅仅是它认为应该使用的大小。
  • 当Font.Size(或Font.Family或 …)发生变化时,您的控件内容可能需要更多或更少的空间,即Measure(),Arrange()和Render()需要执行。
  • 控件的宿主处理Margin。您的控件需要处理Border和Padding。

如果您知道所有这些问题的答案,那么恭喜您。如果没有,很遗憾你很难在Microsoft的文档中找到答案。但作为控件的开发人员,您需要准确了解其工作原理。

要快速获得此类问题的答案,请使用WpfControlTestbench、更改一些值并查看Microsoft控件的反应。然后为您的控件实现相同的行为。TestBench 中的Show Template按钮也有助于更好地理解Microsoft控件,它显示ControlTemplate XAML。如果您真的想了解详细信息,还可以在GitHub - dotnet/wpf: WPF is a .NET Core UI framework for building Windows desktop applications.查看WPF源代码。但请注意,它可能很复杂。

由于获得正确的尺寸和定位需要检查许多场景,并且像素或多或少可能会有所不同,WpfControlTestbench因此可以轻松更改宽度、对齐方式等。它为可以用鼠标移动的Margin,Border和Padding显示虚线。即使是总可用空间也可以通过鼠标拖动分割线轻松更改。

测试标准属性

您还可以通过使用键盘输入值来更改属性,这可能更精确,但也更耗时。

如果您的控件继承自FrameworkElement,则WpfControlTestbench显示Width、Height、Alignment、Min、Max和Margin的属性,用户可以更改这些属性。DesiredWidth和RenderWidth是计算值,不能更改。如果您的控件继承自Control,则还会显示Border和Padding的属性。

您还可以更改颜色和字体:

您可以更改Background, Foreground(ie,Font)和Border的颜色。更改其他字体属性很有趣,因为您的控件所需的大小可能还取决于FontFamily和Font.Size。

单击重置将所有标准属性的值设置回窗口打开时的值。

标准测试

由于Control具有如此多的属性并且应该测试所有可能值的组合,因此需要执行许多测试。如果您手动设置这些值,您可以轻松地花费数小时,并且您可能会在很多天里一次又一次地这样做。但不要害怕,你也在这里覆盖WpfControlTestbench。它为标准属性值的所有有趣组合提供了111个预定义的测试设置。最重要的是,您可以在一两分钟内完成所有这些测试!

您只需继续单击下一步(Alt + N)。通常,快速浏览一下就会告诉您一切是否正常。当然,您也可以轻松地为特定于您的控件的属性添加自己的测试,这将在下面进行详细说明。

试用WpfControlTestbench

在继续阅读如何编写自己的测试窗口之前,这是一个乏味的阅读,我建议你从Github下载WpfControlTestbench并进行测试运行。我是用VS 2022和.NET 6编写的。如果你还没有使用VS 2022,也可以安装那个。您可以在您的PC上运行不同的VS版本,它们不会相互干扰。

启动WpfControlTestbench时,会出现一个小窗口:

左列是WPF提供的2个控件的2个测试,右列是我编写的控件。您可以编写自己的应用程序或将测试窗口添加到此窗口。我希望其他人会为其他WPF控件编写测试窗口,并在Github上与我们分享。看看TextBox测试,它相当复杂,但我只花了2天的时间来写。

准备在TestBench中使用的控件

如果您对如何编写自己的测试窗口的精巧细节感兴趣,请仅阅读本章和下一章。

在跟踪布局过程时,一个挑战是它发生在Measure()和Arrange()方法中。由于这些不是事件,因此TestBench无法跟踪它们的执行。为了使这成为可能,您必须从您的控件继承一个新类并覆盖方法,如MeasureOverride()。

该新类需要实现简单WpfControlTestbench接口ITraceName或IIsTracing支持跟踪:

/// 
/// Provides a name for tracing
/// 
public interface ITraceName {

  /// 
  /// Name to be used for tracing
  /// 
  string TraceName { get; }
}

/// 
/// Control can decide if it should get traced
/// 
public interface IIsTracing: ITraceName {

  /// 
  /// Controls if trace should get written
  /// 
  public bool IsTracing { get; set; }
}

追踪有一个难题要解决。跟踪应该显示控件的构造从哪里开始,然后设置哪些属性,最后显示控件构造完成的跟踪。这里的挑战是如何在构造函数执行之前编写跟踪?例如StackPanel的示例中,无法在其构造函数的开头添加跟踪指令。将开始跟踪写入继承类的构造函数也无济于事,因为它仅在StackPanel构造函数完成后执行。解决方案是使用以下继承:

YourControl=> YourControlWithConstructor=>YourControlTraced

public class YourControlWithConstructor: YourControl { 
  public YourControlWithConstructor(object? _) : base() { }
}

public class YourControlTraced: YourControlWithConstructor, ITraceName {

  public YourControlTraced() : this("YourControl") { }

  public YourControlTraced(string traceName) : 
         base(TraceWPFEvents.TraceCreateStart(traceName)) {
    TraceName = traceName;
    TraceWPFEvents.TraceCreateEnd(traceName);
   }
}

在XAML中,您可以像这样放置测试控件:

            
关注
打赏
1665926880
查看更多评论
0.0482s