【问题标题】:How does a WPF Label Render a String as Text?WPF 标签如何将字符串呈现为文本?
【发布时间】:2016-03-12 00:46:31
【问题描述】:

我正在尝试扩展 Label 控件以支持抚摸文本(有点像 this 问题的答案,但我想扩展 Label 来做到这一点,而不是编写一个全新的(ish)控件)。

我已经取得了一定的成功,但我碰壁了,我无法找到标签如何呈现文本。如果我能弄清楚这是怎么发生的(并覆盖它),那么我可以在我的扩展 Label 类中添加一些属性(一个画笔和一个双精度)并设置好。

这是我目前所拥有的:

public class StrokedLabel : Label {
    public static readonly DependencyProperty
        StrokeProperty = DependencyProperty.Register(
            "Stroke",
            typeof( Brush ),
            typeof( StrokedLabel ),
            new PropertyMetadata(
                Brushes.Red,
                ( S, E ) => ( S as StrokedLabel ).InvalidateVisual( ) ) ),
        StrokeWidthProperty = DependencyProperty.Register(
            "StrokeWidth",
            typeof( double ),
            typeof( StrokedLabel ),
            new PropertyMetadata(
                2.0D,
                ( S, E ) => ( S as StrokedLabel ).InvalidateVisual( ) ) );

    /// <summary>
    /// Get or Set Stroke Brush.
    /// </summary>
    public Brush Stroke {
        get { return this.GetValue( StrokeProperty ) as Brush; }
        set { this.SetValue( StrokeProperty, value ); }
    }

    /// <summary>
    /// Get or Set Stroke Width
    /// </summary>
    public double StrokeWidth {
        get { return ( double )this.GetValue( StrokeWidthProperty ); }
        set { this.SetValue( StrokeWidthProperty, value ); }
    }

    protected override void OnRender( DrawingContext drawingContext ) {
        if ( !( this.Content is string ) )
            base.OnRender( drawingContext );
        else {
            drawingContext.DrawGeometry(
                this.Foreground,
                new Pen(
                    this.Stroke,
                    this.StrokeWidth ),
                new FormattedText(
                    this.Content.ToString( ),
                    CultureInfo.CurrentUICulture,
                    this.FlowDirection,
                    new Typeface(
                        this.FontFamily,
                        this.FontStyle,
                        this.FontWeight,
                        this.FontStretch ),
                    this.FontSize,
                    this.Foreground ).BuildGeometry( new Point( 0.0D, 0.0D ) ) );
        }
    }
}

我已经尝试跟踪继承链,并且我已经做到了 UIElement,它定义了 OnRender 方法,但我找不到在继承链中实际使用该方法的位置(或者如果它甚至涉及)。

我觉得文本是在 ContentControl 中呈现的(Label 直接从该控件继承),但我找不到在该控件中呈现文本的位置。是在 Content Control 中渲染,还是在 Content Control 继承的(Control 类)中渲染?

在哪个类中(以及通过哪个方法)呈现为文本的字符串?

【问题讨论】:

  • 已经很久了,我现在无法对其进行测试,但是:我认为它是通过内容控制功能完成的,该功能试图通过其默认模板选择器 (can be overridden) 解析内容模板。如果选择器找到string,它会返回一个包含TextBlock 的模板。我认为您可以通过Application.Resources 设置TextBlock 的样式,这适用于模板边界。
  • 等等,很明显,这是一个AccessText,因为您希望字符串中所述键前面的“_”给出的访问键的下划线。

标签: c# wpf inheritance text label


【解决方案1】:

由于我有点好奇,我再次正确地查找了这个。以下是静态 ContentPresenter 构造函数中发生的情况:

// Default template for strings when hosted in ContentPresener with RecognizesAccessKey=true
template = new DataTemplate();
text = CreateAccessTextFactory();
text.SetValue(AccessText.TextProperty, new TemplateBindingExtension(ContentProperty));
template.VisualTree = text;
template.Seal();
s_AccessTextTemplate = template;
// Default template selector
s_DefaultTemplateSelector = new DefaultSelector();

它创建了一个 AccessText 模板,该模板存储在一个私有字段中,带有一个内部属性访问器(以及一些其他默认模板)和默认模板选择器。

当一个模板被解析时,ChooseTemplate 被调用,其中包含:

...
// if that failed, try the default TemplateSelector
if (template == null)
{
    template = DefaultTemplateSelector.SelectTemplate(content, this);
}

DefaultSelectorSelectTemplate 方法中,如果没有找到模板,则调用如下:

...
if ((s = item as string) != null)
    template = ((ContentPresenter)container).SelectTemplateForString(s);

SelectTemplateForString:

...
if (this.RecognizesAccessKey && s.IndexOf(AccessText.AccessKeyMarker) > -1)
{
    template = (String.IsNullOrEmpty(format)) ?
        AccessTextContentTemplate : FormattingAccessTextContentTemplate;
}

所以这里要么选择在构造函数中创建的静态模板,要么选择使用ContentStringFormat 值动态创建的模板。

如何更改取决于您,但我个人并不喜欢乱用默认功能(尤其是如果它影响键盘可访问性)。

【讨论】:

  • 但是标签不继承内容展示者。标签继承ContentControl。你的意思是ContentControl 还是ContentPresenter
  • @Will:无法完全追溯,目前手头没有工具,但我认为ContentControl 的默认模板使用ContentPresenter 来表示其与内容相关的属性。几乎所有控件都是如此,例如TabViews,它使用一个控件作为选定选项卡的内容。 (对于标签本身和其他基于列表的内容,使用ItemsPresenter。)
  • @Will:如果您想深入了解生成的内容,可以使用Snoop 之类的内容。你也可以手动使用VisualTreeHelper,我以前用它来写一个穷人版的Snoop。
  • 谢谢。我会看看它。我正在使用 dnSpy 但是哦。我的。上帝。我从中学到的唯一一件事就是我不认识杰克 $#!t...
  • @Will:嗯,WPF 相当庞大和复杂,但在我看来也是一个框架的杰作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-14
  • 2022-01-25
  • 2012-12-22
  • 2017-12-28
  • 2016-03-27
相关资源
最近更新 更多