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

本文关键字:属性 自定义控件 文本 继承 | 更新日期: 2023-09-27 18:06:38

我有一个自定义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
}
}

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

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

1)我想在我的textblock文本周围有一个轮廓,用我想要的笔画厚度和颜色绘制。这里已经回答了:我如何扩展TextBlock来显示概述文本?。在文本块上使用dropshadoweeffect

2)我想控制轮廓到文本和我将要使用的笔刷的距离,而不仅仅是一个简单的颜色,等等。我基本上想在我的textblock上画任何我想要的东西,同时获得它的所有功能。所以你需要用你自己的图形来装饰TextBlock。然后使用装饰器。

3)最复杂的要求似乎是"一个控件做一切TextBlock做,但与笔画,我可以完全控制"。为此,有几个尝试:试图从FrameworkElement重新创建TextBlock,试图从TextBlock继承,我甚至复制了所有在TextBlock中使用的内部密封类的无数,并试图将其重写为一个开放的控件。只需继承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中装饰TextBlock的方法。对于这一点,看看这个: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>

这是我为这个特殊的So问题做的。它还不能用于生产,但它应该引导您在正确的道路上实现您的特定项目。好运!

TextBlock, TextBox和Label没有共同的基础,但它们都有相同的属性:TextElement。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); 
    }

这里的问题是TextBlock中的OnRender方法是sealed。这很糟糕,但肯定有很好的理由。一个我不知道的。

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

public class OutlinedText : TextBlock
{
    public OutlinedText()
    {
        LayoutUpdated += OutlinedText_LayoutUpdated;
    }
    void OutlinedText_LayoutUpdated(object sender, EventArgs e)
    {
        CreateText();
        //...
    }

这绝不是黄金票,但是LayoutUpdated经常被调用,应该能够处理您的文本渲染需求。

哦,这里有一些文档。

继承TextBlock:

public class OutlinedText : TextBlock, IAddChild

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

Blacklight Toolkit: StrokeTextBlock.cs

它继承自System.Windows.Controls.Control,它允许它有你正在寻找的属性,如FontFamily, FontSize, FontWeight…等

这是控件

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>

你可以从TextBlock继承,但是当你这样做的时候,你不需要实现IAddChild,因为TextBlock已经做到了这一点,在这里提到:在这个MSDN参考页面。

我建议的是创建WPF UserControl并将其继承从UserControl更改为TextBlock,然后您可以在类中扩展功能,我已经亲自测试过了,它工作得很好。如果你需要添加任何视觉定制,这通常可以通过Itemtemplate/ControlTemplate来完成。

这里有一篇文章展示了如何扩展WPF控件的另一种方法,这表明这是可能的:扩展WPF控件的基础知识

或者,这是另一种方法,使用用户控件将其内容转换为不同的颜色。

如果你想用TextBlock显示自定义笔画填充,那么这里有一个解决这个特定问题的方法。

这些方法中的至少一种或组合应该能够完成您想要完成的任务。

继承TextBox,并应用您自己的Template。不要把它设置成可编辑的来模仿TextBlock

您的Template将是Geometry。在控件或Loaded事件的构造函数中构造这个Geometry。例如,您可以从您的函数调用CreateText()。有各种各样的几何派生类,如LineGeometry, PathGeometry等。

**编辑一个可操作的样本**

继承自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。这非常简单,只需右键单击控件并edit-template> create-empty。