WPF:如何使RichTextBox看起来像TextBlock?

18

我该如何创建没有边距、边框、内边距等的RichTextBox?换句话说,如何像TextBlock一样显示内容?我已经尝试过以下方法:

<RichTextBox Margin="0" Padding="0" Grid.Row="0" BorderThickness="0" >
    <FlowDocument >
        <Paragraph>LLL</Paragraph>
    </FlowDocument>
</RichTextBox>
<TextBlock>LLL</TextBlock>

但是结果仍然不是我想要的内容:

enter image description here

文档内容之前(也可能在文档顶部或底部)仍有一些空间。我该如何删除它?


如果你想知道我为什么需要这个:我正在尝试让H.B.的答案对我的问题在WPF中创建吉他和弦编辑器进行修改,以便与字距一起使用,并且我不想在字符之间产生不自然的空格。

编辑

所以它不仅仅是ControlTemplate,因为以下代码将产生与上图完全相同的结果:

<RichTextBox Margin="0" Padding="0" Grid.Row="0" BorderThickness="0">
    <RichTextBox.Template>
        <ControlTemplate>
            <ScrollViewer Padding="0" Margin="0" x:Name="PART_ContentHost"/>
        </ControlTemplate>
    </RichTextBox.Template>
    <FlowDocument PagePadding="0">
        <Paragraph Padding="0" Margin="0" >LLL</Paragraph>
    </FlowDocument>
</RichTextBox>

我原以为这将是一个容易回答的问题...有趣的观察:当我设置模板并在FlowDocument上设置PagePadding="0"时,在VisualStudio设计器中它显示了我想要的布局,但一旦运行演示,它就又变错了...当我关闭演示时,设计器中又变错了。这是VS的一个小bug还是实际上已经正确设置了一段时间,但后来某些东西将PagePadding的值更改回了错误的值?


编辑#2

Daniel Rose的修改答案对我也不起作用。这是XAML:

<FlowDocument PagePadding="{Binding PagePadding}">
    <Paragraph x:Name="paragraph" Padding="0" 
        TextIndent="0"  Margin="0,0,0,0" >hello</Paragraph>
</FlowDocument>

这是代码:

public static DependencyProperty PagePaddingProperty =
            DependencyProperty.Register("PagePadding", typeof(Thickness),   typeof(EditableTextBlock),
            new PropertyMetadata(new Thickness(0)));

public Thickness PagePadding {
    get { return (Thickness)GetValue(PagePaddingProperty); }
    set { SetValue(PagePaddingProperty, value); }
}

结果没有改变。空间仍然存在。


编辑#3

按照Daniel Rose在他最后的编辑中建议的添加双向绑定,它可以工作。但我仍然认为这不是很清楚(因为我需要将PagePadding保持为0值而使用依赖属性)。我认为这是一种hack- bug解决方法。如果有更好的解决方案,请分享。

显然,“更改 FlowDocument PagePadding ”为 0.5 是一个错误。如果有人有MSDN账户,如果他们报告此错误将会很好。

4个回答

23
我知道这很烦人。
RichTextBox在其CreateRenderScope()方法中设置了PagePadding,即当其附加到视觉树时。此时通常已经设置了所有属性,因此PagePadding会被重置。
我将展示一种更通用的使用附加属性的方法。在我的代码中,通常会更紧密地执行此操作,因为我知道a)flowdocument不会改变(无需担心两次注册相同的处理程序),并且b)填充不会改变(只需使事件处理程序成为<((FlowDocument)s).PagePadding = new Thickness(0.0);>)。虽然对于SO来说,我提供了一个通用解决方案,您可以直接插入使用。

解决方案:

        <RichTextBox BorderThickness="0" Margin="0" Padding="0">
            <FlowDocument local:FlowDocumentPagePadding.PagePadding="0">
                <Paragraph>
                    <Run>text</Run>
                </Paragraph>
            </FlowDocument>
        </RichTextBox>

public static class FlowDocumentPagePadding
{
    public static Thickness GetPagePadding(DependencyObject obj)
    {
        return (Thickness)obj.GetValue(PagePaddingProperty);
    }
    public static void SetPagePadding(DependencyObject obj, Thickness value)
    {
        obj.SetValue(PagePaddingProperty, value);
    }
    public static readonly DependencyProperty PagePaddingProperty =
        DependencyProperty.RegisterAttached("PagePadding", typeof(Thickness), typeof(FlowDocumentPagePadding), new UIPropertyMetadata(new Thickness(double.NegativeInfinity),(o, args) =>
            {
                var fd = o as FlowDocument;
                if (fd == null) return;
                var dpd = DependencyPropertyDescriptor.FromProperty(FlowDocument.PagePaddingProperty, typeof(FlowDocument));
                dpd.RemoveValueChanged(fd, PaddingChanged);
                fd.PagePadding = (Thickness) args.NewValue;
                dpd.AddValueChanged(fd, PaddingChanged);
            }));
    public static void PaddingChanged(object s, EventArgs e)
    {
        ((FlowDocument)s).PagePadding = GetPagePadding((DependencyObject)s);
    }
}

原始源代码注释:

RichTextBox.CreateRenderScope()的原始源代码中,开发人员包含了以下注释:

// Set a margin so that the BiDi Or Italic caret has room to render at the edges of content.
// Otherwise, anti-aliasing or italic causes the caret to be partially clipped.
renderScope.Document.PagePadding = new Thickness(CaretElement.CaretPaddingWidth, 0, CaretElement.CaretPaddingWidth, 0);

错误报告

这是关于Microsoft Connect上的错误报告


@Markus Hütter:另外两点说明:首先,我猜这是一种错误,对吧?如果您有MSDN帐户(我没有),您是否考虑向Microsoft发送错误报告?也许他们会在.NET 5中修复它。其次,我倾向于不会很快接受即使是好的答案。很少有人会阅读已接受的问题。我想给你一个机会获得更多的赞和其他人提供一些额外的信息或甚至提供他们的解决方案。 - Rasto
1
@drasto 感谢您的友善评论。我也没有 MSDN 账户。从外部来看,这似乎是一个错误,但由于他们在 CreateRenderScope 中明确包含了这个声明,我认为这种行为是有意的。但是,我也认为应该有人将其报告为错误。 - Markus Hütter
@Markus Hütter:我很快就会评论你对我的另一个问题的回答,但那个问题需要更长的评论,不幸的是我现在非常忙 - 在吉他编辑器中的工作必须推迟,直到我完成更紧急的工作。非常抱歉并感谢您的答案。 - Rasto
1
@drasto,我稍微改进了代码,并在我的答案中添加了更多信息。此外,这个 bug 已经被报告了,所以我在 bug 报告中添加了一个链接作为解决方法。 - Markus Hütter
1
"Microsoft Connect已经停用",看起来我们的漏洞现在都消失了? - Evgeny Gorbovoy
显示剩余3条评论

3

之前我写的整个东西都不起作用。由于某种原因,PagePadding被覆盖为“5,0”。然而,当我使用数据绑定时,它可以正常工作。所以只需将其数据绑定到厚度为0的值即可。为了使其正常工作,您必须进行双向数据绑定:

<Window
    x:Class="WpfApplication1.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="MainWindow"
    Height="350"
    Width="525">
    <StackPanel Orientation="Vertical">
        <RichTextBox BorderThickness="0" Margin="0" Padding="0" >
            <FlowDocument PagePadding="{Binding PagePadding, Mode=TwoWay}">
                <Paragraph>LLL</Paragraph>
            </FlowDocument>
        </RichTextBox>
        <TextBlock>LLL</TextBlock>
    </StackPanel>
</Window>

后台代码:

namespace WpfApplication1
{
    using System.ComponentModel;
    using System.Windows;

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : INotifyPropertyChanged
    {
        public MainWindow()
        {
            InitializeComponent();
            this.DataContext = this;
        }

        private Thickness pagePadding;

        public Thickness PagePadding
        {
            get
            {
                return this.pagePadding;
            }
            set
            {
                this.pagePadding = value;
                this.Changed("PagePadding");
            }
        }

        private void Changed(string name)
        {
            var handlers = this.PropertyChanged;
            if (handlers != null)
            {
                handlers.Invoke(this, new PropertyChangedEventArgs(name));
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

2

试试这个。对我来说很有效...它比这里的其他方法要简单得多...

<RichTextBox Padding="-5,0,-5,0">
   <FlowDocument />
</RichTextBox>

0

实际上这不是一个错误。这种行为旨在改善显示 双向文本斜体插入符号。

看看 .Net 4.8 源代码,在RichTextBox.cs文件中:

// Allocates the initial render scope for this control.
internal override FrameworkElement CreateRenderScope()
{
    FlowDocumentView renderScope = new FlowDocumentView();
    renderScope.Document = this.Document;

    // Set a margin so that the BiDi Or Italic caret has room to render at the edges of content.
    // Otherwise, anti-aliasing or italic causes the caret to be partially clipped.
    renderScope.Document.PagePadding = new Thickness(CaretElement.CaretPaddingWidth, 0, CaretElement.CaretPaddingWidth, 0);

    // We want current style to ignore all properties from theme style for renderScope.
    renderScope.OverridesDefaultStyle = true;

    return renderScope;
}

还有在CaretElement.cs文件中定义的CaretElement.CaretPaddingWidth

// Caret padding width to ensure the visible caret for Bidi and Italic.
// Control(TextBox/RichTextBox) must have the enough padding to display
// BiDi and Italic caret indicator.
internal const double CaretPaddingWidth = 5.0;

为了更加有效,让我们看一下以下三个控件:TextBlockTextBoxRichTextBox

<Grid>
    <Grid.RowDefinitions>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="Auto"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <TextBlock FontStyle="Italic" FontSize="48">LLL in TextBlock</TextBlock>
    <TextBox Grid.Row="1" FontStyle="Italic" FontSize="48" BorderThickness="0">LLL in TextBox</TextBox>        
    <RichTextBox Grid.Row="2" FontStyle="Italic" FontSize="48"  BorderThickness="0" Height="Auto"  >
        <FlowDocument PagePadding="0" >
            <Paragraph TextIndent="0">LLL in RichTextBox</Paragraph>
        </FlowDocument>
    </RichTextBox>
</Grid>

这些控件,当使用相同的字体大小/样式、填充和边距时,会显示如下截图所示:

enter image description here

很容易看到TextBox也有一些额外的间距,这是通过显示插入符号来实现的。但是由于在RichTextBox中插入符号具有更多的视觉效果,因此为其保留了更多的空间。

对我有效的解决方案就是简单地设置<RichTextBox Padding="-5,0,0,0">,就像Steve在帖子中提出的那样。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接