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

时间:2015-08-05 09:45:51

标签: c# .net wpf inheritance

我有一个自定义的wpf控件。它基本上是一个文本块,能够对文本应用填充和描边。 它已经被一个类继承了。 问题是它没有像fontfamily这样的文本块属性。 我想用textblock继承这个控件,所以它可以使用它的所有属性。 自定义控制代码如下所示

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
}
}

7 个答案:

答案 0 :(得分:3)

首先,我们需要了解要求,从问题以及各种答案和评论中,我列出了几个:

1)我希望在我的文本块文本周围有一个轮廓,用我想要的笔触粗细和颜色绘制。这已在此回答:How can I extend a TextBlock to display Outlined Text?。在文本块上使用DropShadowEffect。

2)我想控制轮廓到我要使用的文字和画笔的距离,而不仅仅是简单的颜色等。我基本上想要在我的文本块上绘制任何我想要的东西,同时获得它的所有功能。因此,您需要使用自己的图形来装饰TextBlock。然后使用Adorner

3)最复杂的要求似乎是&#34;一个控件,可以完成TextBlock所做的一切,但是我可以完全控制一个笔画&#34;。为此,有几次尝试:尝试从FrameworkElement重新创建TextBlock,尝试从TextBlock继承,我甚至复制了TextBlock中使用的所有内部密封类的miriad,并尝试将其重写为开放控件。只需从TextBlock继承并在其中添加Adorner代码。

作为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
                 )
            );
    }

我希望它可以帮助别人。

另外,我的建议是不要使用从TextBlock继承的控件,而是找到一种从XAML中装饰TextBlocks的方法。为此,请看一下:http://www.codeproject.com/Articles/54472/Defining-WPF-Adorners-in-XAML如果可以将其封装到附加属性中,则可以将strokedtext作为样式添加到所需的任何文本块上。我是这样做的:

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;
        });
    }
}

使用示例:

<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>

所以这就是我为这个特殊的问题所做的。它不是生产准备好的,但它应该引导你走向你的特定项目的正确道路。祝你好运!

答案 1 :(得分:2)

TextBlock,TextBox和Label没有共同的基础,但它们都具有相同的属性:TextElement.FontSize,TextElement.FontFamily等......

TextElement属性是附加属性。这很简单。

例如,查看TextBlock的源代码。这是他们使用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); 
    }

答案 2 :(得分:1)

此处的问题是OnRender中的TextBlock方法是sealed。它很糟糕,但必须有充分的理由。一个我不知道的。

另一种方法是订阅LayoutUpdated事件,并在更新布局时调用CreateText()方法。这是一个例子:

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

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

        //...
    }

这绝不是金票,但LayoutUpdated经常被调用,应该能够处理您的文本呈现要求。

哦,这里有一些documentation

答案 3 :(得分:0)

继承自TextBlock

public class OutlinedText : TextBlock, IAddChild

答案 4 :(得分:0)

几年前遇到了这个整洁的工具包,他们有一个StrokeTextBlock。我将它用于我的银光项目已超过5年了。他们也有WPF版本。代码是很多要发布在这里所以这里是链接:(我很惊讶地看到仍然存在于codeplex。

Blacklight Toolkit: StrokeTextBlock.cs

它继承自System.Windows.Controls.Control,它允许它拥有您所寻找的属性,例如FontFamilyFontSizeFontWeight ...等等

以下是控件的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>

答案 5 :(得分:0)

继承自TextBox,并应用您自己的Template。不要将其模仿为TextBlock

您的Template将是Geometry。在控件或Loaded事件的构造函数中构造此Geometry。例如,您可以从您的ctor拨打CreateText()。有各种几何派生类可用,如LineGeometryPathGeometry等。

**在处理可行的样本后编辑**

继承自Label

将您的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;
    }

您可以考虑完全删除父Label的ControlTemplate。通过右键单击控件和编辑模板&gt;,这非常简单。创建空。

答案 6 :(得分:0)

您可以继承TextBlock,但是当您执行此操作时,您不需要实现IAddChild,因为TextBlock已经执行了此操作,如下所述:in this MSDN reference page.

我建议创建WPF UserControl并将其继承从UserControl更改为TextBlock,然后您可以扩展您班级的功能,我亲自测试了这个和它工作正常。 如果您需要添加任何可视自定义,通常可以通过Itemtemplate / ControlTemplate来完成。

这是一篇文章,其他方法展示了如何扩展WPF控件,该控件显示可能:Basics of extending WPF control

或者,这是另一种使用用户控件that transforms its content to different color的方法。

如果您希望使用TextBlock显示自定义笔触填充,则此处为a solution to that specific problem

这些方法或组合中的至少一种应该能够得到你想要的东西。

相关问题