ItemsControl 内的 WPF 自定义控件

WPF Custom Control inside ItemsControl

我有一个自定义控件,大致如下所示:

public class MyTextBlockTest : Control
{
    static MyTextBlockTest()
    {
    }

    public static readonly DependencyProperty TextProperty =
         DependencyProperty.Register("Text", typeof(string),
         typeof(MyTextBlockTest), new FrameworkPropertyMetadata(""));

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

    protected override void OnRender(DrawingContext drawingContext)
    {
        base.OnRender(drawingContext);

        FontFamily font = new FontFamily("Times New Roman");
        Typeface typeface = new Typeface(font, FontStyles.Normal, FontWeights.Normal, FontStretches.Normal);
        FormattedText ft = new FormattedText(Text,
                                             System.Globalization.CultureInfo.CurrentCulture,
                                             System.Windows.FlowDirection.LeftToRight,
                                             typeface,
                                             15.0,
                                             Brushes.Black);

        var point = this.PointToScreen(new Point(0, 0));
        drawingContext.DrawText(ft, point);            
    }
}

我正在尝试在 ItemsControl:

中使用此控件
<ScrollViewer>
    <ItemsControl ItemsSource="{Binding BigList, ElementName=MainWindowView}" Margin="0,-1,0,1">
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <!--<TextBlock Text="{Binding}"/>-->
                <controls:MyTextBlockTest Text="{Binding}" />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</ScrollViewer>

如果我启用被注释掉的TextBlock,列表显示正常;但是,对于我的自定义控件,WPF 没有将文本放在正确的位置;更具体地说,它似乎只是将所有元素打印在彼此之上。

this.PointToScreen 调用是我最初的想法,给它一个提示,但我不确定(即使这有效,但它没有)这是否会对 ScrollViewer 产生有利的反应。

我意识到我可以简单地将此控件基于 TextBlock 控件,但我特别想将它基于较低级别的控件作为性能实验。请有人指点我一种将自定义控件正确放置在屏幕上的方法吗?

您的最小自定义 TextBlock 至少还应覆盖 MeasureOverride 方法以 return 其大小。否则控件的宽度和高度为零,并且 ItemsControl 中的所有项目都绘制在彼此之上。

因此 Text 属性 应该用 FrameworkPropertyMetadataOptions.AffectsMeasure 标志注册。

public class MyTextBlock : FrameworkElement
{
    private FormattedText formattedText;

    public static readonly DependencyProperty TextProperty =
        DependencyProperty.Register(
            "Text", typeof(string), typeof(MyTextBlock),
            new FrameworkPropertyMetadata(
                string.Empty,
                FrameworkPropertyMetadataOptions.AffectsMeasure,
                (o, e) => ((MyTextBlock)o).TextPropertyChanged((string)e.NewValue)));

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

    private void TextPropertyChanged(string text)
    {
        var typeface = new Typeface(
            new FontFamily("Times New Roman"),
            FontStyles.Normal, FontWeights.Normal, FontStretches.Normal);

        formattedText = new FormattedText(
            text, CultureInfo.CurrentCulture,
            FlowDirection.LeftToRight, typeface, 15, Brushes.Black);
    }

    protected override Size MeasureOverride(Size availableSize)
    {
        return formattedText != null
            ? new Size(formattedText.Width, formattedText.Height)
            : new Size();
    }

    protected override void OnRender(DrawingContext drawingContext)
    {
        if (formattedText != null)
        {
            drawingContext.DrawText(formattedText, new Point());
        }
    }
}