简体   繁体   English

如何在c#中将文本块属性继承到自定义控件

[英]How to inherit a textblock properties to a custom control in c#

I have a custom wpf control. 我有一个自定义的wpf控件。 It is basically a textblock which has an ability to apply fill and stroke to the text. 它基本上是一个文本块,能够对文本应用填充和描边。 It is already inherited by a class. 它已经被一个类继承了。 The problem is that it does not have some textblock properties like fontfamily. 问题是它没有像fontfamily这样的文本块属性。 I want to inherit this control with textblock so it can use its all properties. 我想用textblock继承这个控件,所以它可以使用它的所有属性。 The custom control code is given below 自定义控制代码如下

namespace CustomXaml
{
public class OutlinedText : FrameworkElement, IAddChild
{
#region Private Fields

private Geometry _textGeometry;

#endregion

#region Private Methods

/// <summary>
/// Invoked when a dependency property has changed. Generate a new FormattedText object to display.
/// </summary>
/// <param name="d">OutlineText object whose property was updated.</param>
/// <param name="e">Event arguments for the dependency property.</param>
private static void OnOutlineTextInvalidated(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    ((OutlinedText)d).CreateText();
}

#endregion


#region FrameworkElement Overrides

/// <summary>
/// OnRender override draws the geometry of the text and optional highlight.
/// </summary>
/// <param name="drawingContext">Drawing context of the OutlineText control.</param>
protected override void OnRender(DrawingContext drawingContext)
{
    CreateText();
    // Draw the outline based on the properties that are set.
    drawingContext.DrawGeometry(Fill, new Pen(Stroke, StrokeThickness), _textGeometry);

}

/// <summary>
/// Create the outline geometry based on the formatted text.
/// </summary>
public void CreateText()
{
    FontStyle fontStyle = FontStyles.Normal;
    FontWeight fontWeight = FontWeights.Medium;

    if (Bold == true) fontWeight = FontWeights.Bold;
    if (Italic == true) fontStyle = FontStyles.Italic;

    // Create the formatted text based on the properties set.
    FormattedText formattedText = new FormattedText(
        Text,
        CultureInfo.GetCultureInfo("en-us"),                
        FlowDirection.LeftToRight,
        new Typeface(Font, fontStyle, fontWeight, FontStretches.Normal),                
        FontSize,
        Brushes.Black // This brush does not matter since we use the geometry of the text. 
        );

    // Build the geometry object that represents the text.
    _textGeometry = formattedText.BuildGeometry(new Point(0, 0));




    //set the size of the custome control based on the size of the text
    this.MinWidth = formattedText.Width;
    this.MinHeight = formattedText.Height;

}

#endregion

#region DependencyProperties

/// <summary>
/// Specifies whether the font should display Bold font weight.
/// </summary>
public bool Bold
{
    get
    {
        return (bool)GetValue(BoldProperty);
    }

    set
    {
        SetValue(BoldProperty, value);
    }
}

/// <summary>
/// Identifies the Bold dependency property.
/// </summary>
public static readonly DependencyProperty BoldProperty = DependencyProperty.Register(
    "Bold",
    typeof(bool),
    typeof(OutlinedText),
    new FrameworkPropertyMetadata(
        false,
        FrameworkPropertyMetadataOptions.AffectsRender,
        new PropertyChangedCallback(OnOutlineTextInvalidated),
        null
        )
    );

/// <summary>
/// Specifies the brush to use for the fill of the formatted text.
/// </summary>
public Brush Fill
{
    get
    {
        return (Brush)GetValue(FillProperty);
    }

    set
    {
        SetValue(FillProperty, value);
    }
}

/// <summary>
/// Identifies the Fill dependency property.
/// </summary>
public static readonly DependencyProperty FillProperty = DependencyProperty.Register(
    "Fill",
    typeof(Brush),
    typeof(OutlinedText),
    new FrameworkPropertyMetadata(
        new SolidColorBrush(Colors.LightSteelBlue),
        FrameworkPropertyMetadataOptions.AffectsRender,
        new PropertyChangedCallback(OnOutlineTextInvalidated),
        null
        )
    );

/// <summary>
/// The font to use for the displayed formatted text.
/// </summary>
public FontFamily Font
{
    get
    {
        return (FontFamily)GetValue(FontProperty);
    }

    set
    {
        SetValue(FontProperty, value);
    }
}

/// <summary>
/// Identifies the Font dependency property.
/// </summary>
public static readonly DependencyProperty FontProperty = DependencyProperty.Register(
    "Font",
    typeof(FontFamily),
    typeof(OutlinedText),
    new FrameworkPropertyMetadata(
        new FontFamily("Arial"),
        FrameworkPropertyMetadataOptions.AffectsRender,
        new PropertyChangedCallback(OnOutlineTextInvalidated),
        null
        )
    );

/// <summary>
/// The current font size.
/// </summary>
public double FontSize
{
    get
    {
        return (double)GetValue(FontSizeProperty);
    }

    set
    {
        SetValue(FontSizeProperty, value);
    }
}

/// <summary>
/// Identifies the FontSize dependency property.
/// </summary>
public static readonly DependencyProperty FontSizeProperty = DependencyProperty.Register(
    "FontSize",
    typeof(double),
    typeof(OutlinedText),
    new FrameworkPropertyMetadata(
         (double)48.0,
         FrameworkPropertyMetadataOptions.AffectsRender,
         new PropertyChangedCallback(OnOutlineTextInvalidated),
         null
         )
    );


/// <summary>
/// Specifies whether the font should display Italic font style.
/// </summary>
public bool Italic
{
    get
    {
        return (bool)GetValue(ItalicProperty);
    }

    set
    {
        SetValue(ItalicProperty, value);
    }
}

/// <summary>
/// Identifies the Italic dependency property.
/// </summary>
public static readonly DependencyProperty ItalicProperty = DependencyProperty.Register(
    "Italic",
    typeof(bool),
    typeof(OutlinedText),
    new FrameworkPropertyMetadata(
         false,
         FrameworkPropertyMetadataOptions.AffectsRender,
         new PropertyChangedCallback(OnOutlineTextInvalidated),
         null
         )
    );

/// <summary>
/// Specifies the brush to use for the stroke and optional hightlight of the formatted text.
/// </summary>
public Brush Stroke
{
    get
    {
        return (Brush)GetValue(StrokeProperty);
    }

    set
    {
        SetValue(StrokeProperty, value);
    }
}

/// <summary>
/// Identifies the Stroke dependency property.
/// </summary>
public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register(
    "Stroke",
    typeof(Brush),
    typeof(OutlinedText),
    new FrameworkPropertyMetadata(
         new SolidColorBrush(Colors.Teal),
         FrameworkPropertyMetadataOptions.AffectsRender,
         new PropertyChangedCallback(OnOutlineTextInvalidated),
         null
         )
    );

/// <summary>
///     The stroke thickness of the font.
/// </summary>
public ushort StrokeThickness
{
    get
    {
        return (ushort)GetValue(StrokeThicknessProperty);
    }

    set
    {
        SetValue(StrokeThicknessProperty, value);
    }
}

/// <summary>
/// Identifies the StrokeThickness dependency property.
/// </summary>
public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register(
    "StrokeThickness",
    typeof(ushort),
    typeof(OutlinedText),
    new FrameworkPropertyMetadata(
         (ushort)0,
         FrameworkPropertyMetadataOptions.AffectsRender,
         new PropertyChangedCallback(OnOutlineTextInvalidated),
         null
         )
    );

/// <summary>
/// Specifies the text string to display.
/// </summary>
public string Text
{
    get
    {
        return (string)GetValue(TextProperty);
    }

    set
    {
        SetValue(TextProperty, value);
    }
}

/// <summary>
/// Identifies the Text dependency property.
/// </summary>
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
    "Text",
    typeof(string),
    typeof(OutlinedText),
    new FrameworkPropertyMetadata(
         "",
         FrameworkPropertyMetadataOptions.AffectsRender,
         new PropertyChangedCallback(OnOutlineTextInvalidated),
         null
         )
    );

public void AddChild(Object value)
{

}

public void AddText(string value)
{
    Text = value;
}

#endregion
}
}

First we need to understand the requirements and, from the question as well as from various answers and comments, I list several: 首先,我们需要了解要求,从问题以及各种答案和评论中,我列出了几个:

1) I want to have an outline around my textblock text, drawn with my desired stroke thickness and color. 1)我希望在我的文本块文本周围有一个轮廓,用我想要的笔划粗细和颜色绘制。 That has been answered here: How can I extend a TextBlock to display Outlined Text? 这里已经回答: 如何扩展TextBlock以显示Outlined Text? . Use DropShadowEffect on the textblock. 在文本块上使用DropShadowEffect。

2) I want to control the outline up to the distance from the text and the brush that I am going to use, not just a simple color, etc. I basically want to draw anything I want on my textblock, while getting all of its functionality. 2)我想控制轮廓到我要使用的文本和画笔的距离,而不仅仅是简单的颜色等。我基本上想要在我的文本块上绘制任何我想要的东西,同时获得所有的功能。 So you need to adorn the TextBlock with your own graphics. 因此,您需要使用自己的图形来装饰TextBlock。 Then use an Adorner . 然后使用Adorner

3) The most complex requirement seems to be "a control that does everything a TextBlock does, but with a stroke that I can control completely". 3)最复杂的要求似乎是“一个控件,可以完成TextBlock所做的一切,但是我可以完全控制一个笔划”。 For this there have been several attempts: trying to recreate TextBlock from FrameworkElement, trying to inherit from TextBlock, I even copied all the miriad of internal sealed classes that are used in TextBlock and tried to rewrite it as an open control. 为此,有几次尝试:尝试从FrameworkElement重新创建TextBlock,尝试从TextBlock继承,我甚至复制了TextBlock中使用的所有内部密封类的miriad,并尝试将其重写为开放控件。 Just inherit from TextBlock and add the Adorner code inside. 只需从TextBlock继承并在其中添加Adorner代码。

As a solution for 3), here is the code that I made to replicate the original code, which may now be changed as desired, and using a TextBlock: 作为3)的解决方案,这里是我复制原始代码的代码,现在可以根据需要进行更改,并使用TextBlock:

public class StrokeAdorner : Adorner
{
    private TextBlock _textBlock;

    private Brush _stroke;
    private ushort _strokeThickness;

    public Brush Stroke
    {
        get
        {
            return _stroke;
        }

        set
        {
            _stroke = value;
            _textBlock.InvalidateVisual();
            InvalidateVisual();
        }
    }

    public ushort StrokeThickness
    {
        get
        {
            return _strokeThickness;
        }

        set
        {
            _strokeThickness = value;
            _textBlock.InvalidateVisual();
            InvalidateVisual();
        }
    }

    public StrokeAdorner(UIElement adornedElement) : base(adornedElement)
    {
        _textBlock = adornedElement as TextBlock;
        ensureTextBlock();
        foreach (var property in TypeDescriptor.GetProperties(_textBlock).OfType<PropertyDescriptor>())
        {
            var dp = DependencyPropertyDescriptor.FromProperty(property);
            if (dp == null) continue;
            var metadata = dp.Metadata as FrameworkPropertyMetadata;
            if (metadata == null) continue;
            if (!metadata.AffectsRender) continue;
            dp.AddValueChanged(_textBlock, (s, e) => this.InvalidateVisual());
        }
    }

    private void ensureTextBlock()
    {
        if (_textBlock == null) throw new Exception("This adorner works on TextBlocks only");
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        ensureTextBlock();
        base.OnRender(drawingContext);
        var formattedText = new FormattedText(
            _textBlock.Text,
            CultureInfo.CurrentUICulture,
            _textBlock.FlowDirection,
            new Typeface(_textBlock.FontFamily, _textBlock.FontStyle, _textBlock.FontWeight, _textBlock.FontStretch),
            _textBlock.FontSize,
             Brushes.Black // This brush does not matter since we use the geometry of the text. 
        );

        formattedText.TextAlignment = _textBlock.TextAlignment;
        formattedText.Trimming = _textBlock.TextTrimming;
        formattedText.LineHeight = _textBlock.LineHeight;
        formattedText.MaxTextWidth = _textBlock.ActualWidth - _textBlock.Padding.Left - _textBlock.Padding.Right;
        formattedText.MaxTextHeight = _textBlock.ActualHeight - _textBlock.Padding.Top;// - _textBlock.Padding.Bottom;
        while (formattedText.Extent==double.NegativeInfinity)
        {
            formattedText.MaxTextHeight++;
        }

        // Build the geometry object that represents the text.
        var _textGeometry = formattedText.BuildGeometry(new Point(_textBlock.Padding.Left, _textBlock.Padding.Top));
        var textPen = new Pen(Stroke, StrokeThickness);
        drawingContext.DrawGeometry(Brushes.Transparent, textPen, _textGeometry);
    }

}


    public class StrokeTextBlock:TextBlock
    {
        private StrokeAdorner _adorner;
        private bool _adorned=false;

        public StrokeTextBlock()
        {
            _adorner = new StrokeAdorner(this);
            this.LayoutUpdated += StrokeTextBlock_LayoutUpdated;
        }

        private void StrokeTextBlock_LayoutUpdated(object sender, EventArgs e)
        {
            if (_adorned) return;
            _adorned = true;
            var adornerLayer = AdornerLayer.GetAdornerLayer(this);
            adornerLayer.Add(_adorner);
            this.LayoutUpdated -= StrokeTextBlock_LayoutUpdated;
        }

        private static void strokeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var stb = (StrokeTextBlock)d;
            stb._adorner.Stroke = e.NewValue as Brush;
        }

        private static void strokeThicknessChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var stb = (StrokeTextBlock)d;
            stb._adorner.StrokeThickness = DependencyProperty.UnsetValue.Equals(e.NewValue)?(ushort)0:(ushort)e.NewValue;
        }

        /// <summary>
        /// Specifies the brush to use for the stroke and optional hightlight of the formatted text.
        /// </summary>
        public Brush Stroke
        {
            get
            {
                return (Brush)GetValue(StrokeProperty);
            }

            set
            {
                SetValue(StrokeProperty, value);
            }
        }

        /// <summary>
        /// Identifies the Stroke dependency property.
        /// </summary>
        public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register(
            "Stroke",
            typeof(Brush),
            typeof(StrokeTextBlock),
            new FrameworkPropertyMetadata(
                 new SolidColorBrush(Colors.Teal),
                 FrameworkPropertyMetadataOptions.AffectsRender,
                 new PropertyChangedCallback(strokeChanged),
                 null
                 )
            );

        /// <summary>
        ///     The stroke thickness of the font.
        /// </summary>
        public ushort StrokeThickness
        {
            get
            {
                return (ushort)GetValue(StrokeThicknessProperty);
            }

            set
            {
                SetValue(StrokeThicknessProperty, value);
            }
        }

        /// <summary>
        /// Identifies the StrokeThickness dependency property.
        /// </summary>
        public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register(
            "StrokeThickness",
            typeof(ushort),
            typeof(StrokeTextBlock),
            new FrameworkPropertyMetadata(
                 (ushort)0,
                 FrameworkPropertyMetadataOptions.AffectsRender,
                 new PropertyChangedCallback(strokeThicknessChanged),
                 null
                 )
            );
    }

I hope it helps people. 我希望它能帮助别人。

Also, my advice is not to use a control that inherits from TextBlock, but instead find a way to adorn TextBlocks from XAML. 另外,我的建议是不要使用从TextBlock继承的控件,而是找到一种从XAML中装饰TextBlocks的方法。 For that, take a look at this: http://www.codeproject.com/Articles/54472/Defining-WPF-Adorners-in-XAML If one can encapsulate it into an attached property, then one can add the strokedtext as a Style on whatever textblocks you want. 为此,请看一下: http//www.codeproject.com/Articles/54472/Defining-WPF-Adorners-in-XAML如果可以将它封装到附加属性中,那么可以将strokedtext添加为您想要的任何文本块的样式。 Here is how I did it: 我是这样做的:

public static class Adorning
{
    public static Brush GetStroke(DependencyObject obj)
    {
        return (Brush)obj.GetValue(StrokeProperty);
    }
    public static void SetStroke(DependencyObject obj, Brush value)
    {
        obj.SetValue(StrokeProperty, value);
    }
    // Using a DependencyProperty as the backing store for Stroke. This enables animation, styling, binding, etc...  
    public static readonly DependencyProperty StrokeProperty =
    DependencyProperty.RegisterAttached("Stroke", typeof(Brush), typeof(Adorning), new PropertyMetadata(Brushes.Transparent, strokeChanged));

    private static void strokeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var stroke= e.NewValue as Brush;
        ensureAdorner(d,a=>a.Stroke=stroke);
    }

    private static void ensureAdorner(DependencyObject d, Action<StrokeAdorner> action)
    {
        var tb = d as TextBlock;
        if (tb == null) throw new Exception("StrokeAdorner only works on TextBlocks");
        EventHandler f = null;
        f = new EventHandler((o, e) =>
          {
              var adornerLayer = AdornerLayer.GetAdornerLayer(tb);
              if (adornerLayer == null) throw new Exception("AdornerLayer should not be empty");
              var adorners = adornerLayer.GetAdorners(tb);
              var adorner = adorners == null ? null : adorners.OfType<StrokeAdorner>().FirstOrDefault();
              if (adorner == null)
              {
                  adorner = new StrokeAdorner(tb);
                  adornerLayer.Add(adorner);
              }
              tb.LayoutUpdated -= f;
              action(adorner);
          });
        tb.LayoutUpdated += f;
    }

    public static double GetStrokeThickness(DependencyObject obj)
    {
        return (double)obj.GetValue(StrokeThicknessProperty);
    }
    public static void SetStrokeThickness(DependencyObject obj, double value)
    {
        obj.SetValue(StrokeThicknessProperty, value);
    }
    // Using a DependencyProperty as the backing store for StrokeThickness. This enables animation, styling, binding, etc...  
    public static readonly DependencyProperty StrokeThicknessProperty =
    DependencyProperty.RegisterAttached("StrokeThickness", typeof(double), typeof(Adorning), new PropertyMetadata(0.0, strokeThicknessChanged));

    private static void strokeThicknessChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        ensureAdorner(d, a =>
        {
            if (DependencyProperty.UnsetValue.Equals(e.NewValue)) return;
            a.StrokeThickness = (ushort)(double)e.NewValue;
        });
    }
}

An example of use: 使用示例:

<TextBlock Text="Some text that needs to be outlined" Grid.Row="2"
                    local:Adorning.Stroke="Aquamarine" local:Adorning.StrokeThickness="2"
                     FontSize="30">
    <TextBlock.Foreground>
        <LinearGradientBrush EndPoint="0.504,1.5" StartPoint="0.504,0.03">
            <GradientStop Color="#FFFFC934" Offset="0"/>
            <GradientStop Color="#FFFFFFFF" Offset="0.567"/>
        </LinearGradientBrush>
    </TextBlock.Foreground>
</TextBlock>

So this is what I made for this particular SO question. 所以这就是我为这个特殊的问题所做的。 It is not production ready, but it should steer you on the right path towards your particular project. 它不是生产准备好的,但它应该引导你走向你的特定项目的正确道路。 Good luck! 祝好运!

TextBlock, TextBox and Label does not have common base, but all of them have the same properties: TextElement.FontSize, TextElement.FontFamily, etc... TextBlock,TextBox和Label没有共同的基础,但它们都具有相同的属性:TextElement.FontSize,TextElement.FontFamily等...

TextElement properties are attached properties. TextElement属性是附加属性。 It's simple as that. 这很简单。

Look at source codes of TextBlock for example. 例如,查看TextBlock的源代码。 This is how they use TextElement properties: 这是他们使用TextElement属性的方式:

    /// <summary> 
    /// DependencyProperty for <see cref="FontFamily" /> property.
    /// </summary>
    [CommonDependencyProperty]
    public static readonly DependencyProperty FontFamilyProperty = 
            TextElement.FontFamilyProperty.AddOwner(typeof(TextBlock));

    /// <summary> 
    /// The FontFamily property specifies the name of font family.
    /// </summary> 
    [Localizability(LocalizationCategory.Font)]
    public FontFamily FontFamily
    {
        get { return (FontFamily) GetValue(FontFamilyProperty); } 
        set { SetValue(FontFamilyProperty, value); }
    } 

    /// <summary>
    /// DependencyProperty setter for <see cref="FontFamily" /> property. 
    /// </summary>
    /// <param name="element">The element to which to write the attached property.</param>
    /// <param name="value">The property value to set</param>
    public static void SetFontFamily(DependencyObject element, FontFamily value) 
    {
        if (element == null) 
        { 
            throw new ArgumentNullException("element");
        } 

        element.SetValue(FontFamilyProperty, value);
    }

    /// <summary>
    /// DependencyProperty getter for <see cref="FontFamily" /> property. 
    /// </summary> 
    /// <param name="element">The element from which to read the attached property.</param>
    public static FontFamily GetFontFamily(DependencyObject element) 
    {
        if (element == null)
        {
            throw new ArgumentNullException("element"); 
        }

        return (FontFamily)element.GetValue(FontFamilyProperty); 
    }

The issue here is that the OnRender method in TextBlock is sealed . 这里的问题是TextBlock中的OnRender方法是sealed It sucks, but there must be a good reason for it. 它很糟糕,但必须有充分的理由。 One which I am not aware of. 一个我不知道的。

An alternative would be to subscribe to the LayoutUpdated event and call your CreateText() method when the layout is updated. 另一种方法是订阅LayoutUpdated事件,并在更新布局时调用CreateText()方法。 Here's an example: 这是一个例子:

public class OutlinedText : TextBlock
{
    public OutlinedText()
    {
        LayoutUpdated += OutlinedText_LayoutUpdated;
    }

    void OutlinedText_LayoutUpdated(object sender, EventArgs e)
    {
        CreateText();

        //...
    }

This is by no means the golden ticket, however LayoutUpdated gets called often and should be able to handle your text rendering requirements. 这绝不是金票,但是经常调用LayoutUpdated并且应该能够处理您的文本呈现要求。

Oh, and here is some documentation for it. 哦,这里有一些文档

TextBlock继承:

public class OutlinedText : TextBlock, IAddChild

Came across this neat toolkit years ago and they have a StrokeTextBlock . 几年前遇到过这个整洁的工具包,他们有一个StrokeTextBlock I used it for my silverlight projects for more than 5 years now. 我将它用于我的银光项目已超过5年了。 They also have a WPF version. 他们也有WPF版本。 The code is a lot to post here so here is the link: (which i was surprised to see still existed on codeplex. 代码在这里发布很多,所以这里是链接:(我很惊讶地看到仍然存在于codeplex上。

Blacklight Toolkit: StrokeTextBlock.cs Blacklight Toolkit:StrokeTextBlock.cs

It inherits from System.Windows.Controls.Control Which allows it to have the properties you were looking for like FontFamily , FontSize , FontWeight ...etc 它继承自System.Windows.Controls.Control它允许它拥有你正在寻找的属性,如FontFamilyFontSizeFontWeight ......等

Here is the generic.xaml for the control 这是控件的generic.xaml

<!-- StrokeTextBlock style -->
<Style TargetType="local:StrokeTextBlock">
    <Setter Property="Text" Value="StrokeTextBlock" />
    <Setter Property="StrokeOpacity" Value="1" />
    <Setter Property="Stroke" Value="#ffffffff" />
    <Setter Property="StrokeThickness" Value="1" />
    <Setter Property="Foreground" Value="#ff000000" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="local:StrokeTextBlock">
                <Grid>
                    <ItemsControl x:Name="PART_ItemsControl" 
                          VerticalAlignment="Top" HorizontalAlignment="Left" 
                          Opacity="{TemplateBinding StrokeOpacity}">
                        <ItemsControl.ItemsPanel>
                            <ItemsPanelTemplate>
                                <Grid  />
                            </ItemsPanelTemplate>
                        </ItemsControl.ItemsPanel>
                    </ItemsControl>

                    <TextBlock x:Name="PART_TextBlock"
                               TextWrapping="{TemplateBinding TextWrapping}" 
                               Foreground="{TemplateBinding Foreground}"
                               FontSize="{TemplateBinding FontSize}" 
                               FontFamily="{TemplateBinding FontFamily}" 
                               FontWeight="{TemplateBinding FontWeight}"
                               VerticalAlignment="Top" HorizontalAlignment="Left"
                               UseLayoutRounding="False"
                               LineHeight="{TemplateBinding LineHeight}"
                               Text="{TemplateBinding Text}" />

                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Inherit from TextBox , and apply your own Template . TextBox继承,并应用您自己的Template Don't make it editable to mimick it as a TextBlock . 不要将其模仿为TextBlock

Your Template would be a Geometry . 你的Template将是一个Geometry Construct this Geometry in the constructor of your control or Loaded event. 在控件或Loaded事件的构造函数中构造此Geometry For example, you can call CreateText() from your ctor. 例如,您可以从您的ctor调用CreateText() There are various Geometry derived classes available like LineGeometry , PathGeometry etc. 有各种几何派生类可用,如LineGeometryPathGeometry等。

** EDIT after working on a workable sample ** **在处理可行样本后编辑**

Inherit from Label . Label继承。

Change your CreateText() to : CreateText()更改为:

   public void CreateText()
    {
        FontStyle fontStyle = FontStyles.Normal;
        FontWeight fontWeight = FontWeights.Medium;

        //if (FontWeight == FontWeights.Bold) fontWeight = FontWeights.Bold;
        // if (FontStyle == FontStyles.Italic) fontStyle = FontStyles.Italic;

        // Create the formatted text based on the properties set.
        FormattedText formattedText = new FormattedText(
            Text,
            CultureInfo.GetCultureInfo("en-us"),
            FlowDirection.LeftToRight,
            new Typeface(FontFamily, FontStyle, FontWeight, FontStretches.Normal, new FontFamily("Arial")),
            FontSize,
            Brushes.Black // This brush does not matter since we use the geometry of the text. 
            );

        // Build the geometry object that represents the text.
        _textGeometry = formattedText.BuildGeometry(new Point(4, 4));

        //set the size of the custome control based on the size of the text
        this.MaxWidth = formattedText.Width + 100;
        this.MaxHeight = formattedText.Height + 10;
    }

And you can consider to completely remove the ControlTemplate of the parent Label . 您可以考虑完全删除父Label的ControlTemplate。 This is very easy by right-clicking the control and edit-template > create-empty. 右键单击控件并编辑模板> create-empty,这非常简单。

You can inherit from TextBlock , but when you do this, you don't need to implement IAddChild , because TextBlock already does this, as mentioned here: in this MSDN reference page. 您可以从TextBlock继承,但是当您执行此操作时,您不需要实现IAddChild ,因为TextBlock已经执行此操作,如此处所述: 在此MSDN参考页中。

What I would recommend is to create WPF UserControl and change it's inheritance from UserControl to TextBlock , then you can just extend the functionality in your class, I have personally tested this and it works fine. 我建议创建WPF UserControl并将其继承从UserControl更改为TextBlock ,然后您可以扩展您的类中的功能,我亲自测试了它,它工作正常。 If you need to add any visual customisation, this can usually be done through a Itemtemplate / ControlTemplate . 如果您需要添加任何可视化自定义,通常可以通过Itemtemplate / ControlTemplate来完成。

Here is an article with another method that shows how to extend a WPF control which shows that it is possible: Basics of extending WPF control 这是一篇文章,其他方法展示了如何扩展WPF控件,这表明它是可能的: 扩展WPF控件的基础知识

Alternatively this is another method using a user control that transforms its content to different color . 或者,这是使用用户控件的另一种方法,该用户控件将其内容转换为不同的颜色

If you are looking to display a custom stroke fill with the TextBlock, then here is a solution to that specific problem . 如果您希望使用TextBlock显示自定义笔触填充,则此处是针对该特定问题的解决方案

At least one of these methods or a combination should be able to get what you want done. 这些方法或组合中的至少一种应该能够得到你想要的东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM