为什么在DrawingContext绘制后ActualWidth始终为0?

huangapple go评论60阅读模式
英文:

Why ActualWidth is always 0 after DrawingContext drew?

问题

最近,我一直在尝试重新编写TextBlock以进行字距调整。现在我几乎通过DrawingContext实现了这一点。

以下是我的代码:

public class STextBlock : Control
{
    public string Text
    {
        get { return (string)GetValue(TextProperty); }
        set { SetValue(TextProperty, value); }
    }

    // Using a DependencyProperty as the backing store for Text.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register("Text", typeof(string), typeof(STextBlock), new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.AffectsRender));

    public double LetterSpacing
    {
        get { return (double)GetValue(LetterSpacingProperty); }
        set { SetValue(LetterSpacingProperty, value); }
    }

    // Using a DependencyProperty as the backing store for LetterSpacing.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty LetterSpacingProperty =
        DependencyProperty.Register("LetterSpacing", typeof(double), typeof(STextBlock), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender));

    // 其他属性的定义,你可以根据需要继续添加

    protected override void OnRender(DrawingContext drawingContext)
    {
        base.OnRender(drawingContext);
        List<FormattedTextToDrawModel> FormattedTextToDrawList = new List<FormattedTextToDrawModel>();
        double PositionX = Padding.Left;
        double PositionY = Padding.Top;
        if (!string.IsNullOrEmpty(Text))
        {
            foreach (var i in Text)
            {
                var FT = new FormattedText(i.ToString(), CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, new Typeface(FontFamily, FontStyle, FontWeight, FontStretch), FontSize, Foreground);
                FormattedTextToDrawList.Add(new FormattedTextToDrawModel() { FormattedText = FT, DrawPoint = new Point(PositionX, PositionY) });
                PositionX += FT.Width + LetterSpacing;
                if (PositionX > ActualSize.Width)
                {
                    ActualSize.Width = PositionX;
                }
                if (PositionY + FT.Height > ActualSize.Height)
                {
                    ActualSize.Height = PositionY + FT.Height;
                }
            }
        }
        ActualSize.Width += Padding.Right;
        ActualSize.Height += Padding.Top;
        drawingContext.DrawRectangle(Background, new Pen(), new Rect(ActualSize));
        foreach (var i in FormattedTextToDrawList)
        {
            drawingContext.DrawText(i.FormattedText, i.DrawPoint);
        }
    }

    public struct FormattedTextToDrawModel
    {
        public FormattedText FormattedText { get; init; }
        public Point DrawPoint { get; init; }
    }
    Size ActualSize = new Size(0, 0);
}

你可以像这样测试上面的代码:

<Border VerticalAlignment="Center" HorizontalAlignment="Center">
<Test:STextBlock Margin="10" x:Name="TB" Text="123123" Padding="20" LetterSpacing="4" FontFamily="Comic Sans MS" FontSize="20" Background="Red"></Test:STextBlock>
</Border>

现在我有一个问题。在DrawingContext绘制背景和文本后,STextBlock自身以及其父元素的ActualWidth和ActualHeight仍然为0。这是什么问题?

有没有办法我可以自己设置ActualWidth和ActualHeight?

希望这些信息能帮助你解决问题。

英文:

Recently I have been trying to rewrite TextBlock for letterspacing. Now I almost achieved this by the DrawingContext.

Here is my code:

  public class STextBlock : Control
{
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
// Using a DependencyProperty as the backing store for Text.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register(&quot;Text&quot;, typeof(string), typeof(STextBlock), new FrameworkPropertyMetadata(&quot;&quot;, FrameworkPropertyMetadataOptions.AffectsRender));
public double LetterSpacing
{
get { return (double)GetValue(LetterSpacingProperty); }
set { SetValue(LetterSpacingProperty, value); }
}
// Using a DependencyProperty as the backing store for LetterSpacing.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty LetterSpacingProperty =
DependencyProperty.Register(&quot;LetterSpacing&quot;, typeof(double), typeof(STextBlock), new FrameworkPropertyMetadata(0.0, FrameworkPropertyMetadataOptions.AffectsRender));
public FontStyle FontStyle
{
get { return (FontStyle)GetValue(FontStyleProperty); }
set { SetValue(FontStyleProperty, value); }
}
// Using a DependencyProperty as the backing store for FontStyle.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty FontStyleProperty =
DependencyProperty.Register(&quot;FontStyle&quot;, typeof(FontStyle), typeof(STextBlock), new FrameworkPropertyMetadata(FontStyles.Normal, FrameworkPropertyMetadataOptions.AffectsRender));
public Brush Foreground
{
get { return (Brush)GetValue(ForegroundProperty); }
set { SetValue(ForegroundProperty, value); }
}
// Using a DependencyProperty as the backing store for Foreground.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty ForegroundProperty =
DependencyProperty.Register(&quot;Foreground&quot;, typeof(Brush), typeof(STextBlock), new FrameworkPropertyMetadata(new SolidColorBrush(Colors.Black), FrameworkPropertyMetadataOptions.AffectsRender));
public FontStretch FontStretch
{
get { return (FontStretch)GetValue(FontStretchProperty); }
set { SetValue(FontStretchProperty, value); }
}
// Using a DependencyProperty as the backing store for FontStretch.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty FontStretchProperty =
DependencyProperty.Register(&quot;FontStretch&quot;, typeof(FontStretch), typeof(STextBlock), new FrameworkPropertyMetadata(FontStretches.Normal, FrameworkPropertyMetadataOptions.AffectsRender));
public FontFamily FontFamily
{
get { return (FontFamily)GetValue(FontFamilyProperty); }
set { SetValue(FontFamilyProperty, value); }
}
// Using a DependencyProperty as the backing store for FontFamily.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty FontFamilyProperty =
DependencyProperty.Register(&quot;FontFamily&quot;, typeof(FontFamily), typeof(STextBlock), new FrameworkPropertyMetadata(new FontFamily(), FrameworkPropertyMetadataOptions.AffectsRender));
public double FontSize
{
get { return (double)GetValue(FontSizeProperty); }
set { SetValue(FontSizeProperty, value); }
}
// Using a DependencyProperty as the backing store for FontSize.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty FontSizeProperty =
DependencyProperty.Register(&quot;FontSize&quot;, typeof(double), typeof(STextBlock), new FrameworkPropertyMetadata(12.0, FrameworkPropertyMetadataOptions.AffectsRender));
public FontWeight FontWeight
{
get { return (FontWeight)GetValue(FontWeightProperty); }
set { SetValue(FontWeightProperty, value); }
}
// Using a DependencyProperty as the backing store for FontWeight.  This enables animation, styling, binding, etc...
public static readonly DependencyProperty FontWeightProperty =
DependencyProperty.Register(&quot;FontWeight&quot;, typeof(FontWeight), typeof(STextBlock), new FrameworkPropertyMetadata(FontWeights.Normal, FrameworkPropertyMetadataOptions.AffectsRender));
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);
List&lt;FormattedTextToDrawModel&gt; FormattedTextToDrawList = new List&lt;FormattedTextToDrawModel&gt;();
double PositionX = Padding.Left;
double PositionY = Padding.Top;            
if (!string.IsNullOrEmpty(Text))
{                            
foreach (var i in Text)
{
var FT = new FormattedText(i.ToString(), CultureInfo.CurrentUICulture, FlowDirection.LeftToRight, new Typeface(FontFamily, FontStyle, FontWeight, FontStretch), FontSize, Foreground);
FormattedTextToDrawList.Add(new FormattedTextToDrawModel() { FormattedText = FT, DrawPoint = new Point(PositionX, PositionY) });
PositionX += FT.Width + LetterSpacing;
if (PositionX &gt; ActualSize.Width)
{
ActualSize.Width = PositionX;
}
if (PositionY + FT.Height &gt; ActualSize.Height)
{
ActualSize.Height = PositionY + FT.Height;
}
}
}
ActualSize.Width += Padding.Right;
ActualSize.Height += Padding.Top;
drawingContext.DrawRectangle(Background, new Pen(), new Rect(ActualSize));
foreach (var i in FormattedTextToDrawList)
{
drawingContext.DrawText(i.FormattedText, i.DrawPoint);
}
}
public struct FormattedTextToDrawModel
{
public FormattedText FormattedText { get;init;}
public Point DrawPoint { get; init; }
}
Size ActualSize = new Size(0, 0);             
}

I test the code above like this:

&lt;Border VerticalAlignment=&quot;Center&quot; HorizontalAlignment=&quot;Center&quot;&gt;
&lt;Test:STextBlock Margin=&quot;10&quot; x:Name=&quot;TB&quot; Text=&quot;123123&quot; Padding=&quot;20&quot; LetterSpacing=&quot;4&quot; FontFamily=&quot;Comic Sans MS&quot; FontSize=&quot;20&quot; Background=&quot;Red&quot;&gt;&lt;/Seal:STextBlock&gt;
&lt;/Border&gt;

Now I have a problem. After the DrawingContext drew the background and text. The ActualWidth and ActualHeight not only of the STextBlock self but also of its parent is still being 0. What's wrong with it?

Is there any way I can set the ActualWidth and ActualHeight by myself?

答案1

得分: 0

实际宽度和高度是在测量和排列布局过程中内部计算的。

每个正在布局的控件都会被要求提供它所期望的大小。

它希望占用多少空间?

然后将其与父容器等提供的可用空间进行比较,它可能无法获得所需的空间。

举个例子。

一个文本块如果无法获得它所希望的宽度,将会导致文本被截断或溢出到下一行。

FrameworkElement.ActualWidth是只读的

注意仅可获取 {get;}

https://learn.microsoft.com/en-us/dotnet/api/system.windows.frameworkelement.actualwidth?view=windowsdesktop-7.0

你不直接设置它。

相反,你应该重写测量方法。我认为你已经在进行这些计算。

protected override Size MeasureOverride(Size constraint)
{
    Size desiredSize = new Size();
    // 计算文本希望的尺寸
    return desiredSize;
}
英文:

Actual width and height are calculated internally as part of the measure arrange layout pass.

Each control being laid out is asked for it's desired size.

How much space would it like?

That's then compared to what it can have from parent containers etc and it may not get what it desires.

As an example.

A textblock which cannot have the width it would like will have it's text truncated or it will overflow onto the next line.

FrameworkElement.ActualWidth is read only

Note only {get;}

https://learn.microsoft.com/en-us/dotnet/api/system.windows.frameworkelement.actualwidth?view=windowsdesktop-7.0

You don't set that directly yourself.

You should instead override measure. I think you've sort of done those calculations.

protected override Size MeasureOverride(Size constraint)
{
Size desiredSize = new Size();
// Calculate how big your text wants to be
return desiredSize;
}

huangapple
  • 本文由 发表于 2023年2月10日 13:49:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/75407396.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定