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

时间:2022-09-11 18:51:53

How can I make RichTextBox with no Margin, Border, Padding etc. ? In another words to display content in the same way as TextBlock does it ? I have tried this:

如何使RichTextBox没有边距,边框,填充等?换句话说,以与TextBlock相同的方式显示内容呢?我试过这个:

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

But the result produces is still not what I want:

但结果产生的仍然不是我想要的:

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

There is still some space before document content (and also maybe after, on the top or bottom of the document...). How can I remove it ?

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


If you are interested why I need this: I trying to make H.B.'s answer to my question Create guitar chords editor in WPF to work with kerning and I don't want to have unnatural space between characters.

如果你有兴趣为什么我需要这个:我试图让H.B.回答我的问题在WPF中创建吉他和弦编辑器来处理字距并且我不希望在字符之间有不自然的空间。


Edit

So it is not ControlTemplate at least not only that because following code will produce exactly the same result (as the one on the picture above):

所以至少不仅仅是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>

And I thought this will be question easy to answer... Interesting observation: when I have template set and I set PagePadding="0" on FlowDocument it displays layout that I want in the VisualStudio designer - until I run the demo. In the demo it is wrong again... And when I close the demo it is wrong again in the designer. This is a small bug of VS or is it actually set to the right layout for a while but then something changes value of PagePadding back to some wrong value ?

我觉得这个问题很容易回答......有趣的观察:当我有模板集并在FlowDocument上设置PagePadding =“0”时,它会在VisualStudio设计器中显示我想要的布局 - 直到我运行演示。在演示中再次出错......当我关闭演示时,设计师再次出错了。这是VS的一个小错误,或者它实际上设置为正确的布局一段时间但是有些东西会将PagePadding的值更改回某个错误的值?


Edit#2

Daniel Rose's edited answer is also not working for me. This is XAML:

丹尼尔罗斯的编辑答案也不适用于我。这是XAML:

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

And this is in code:

这是代码:

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

No changes to the result. Space remains.

结果没有变化。空间依旧。


Edit#3

After adding Two-Way binding as Daniel Rose suggested in his las edit it works. Still I don't really think it is very clear (to have dependency property because I need to keep PagePadding at 0 value). I think it is a hack - bug workaround. If somebody has better solution please share it.

添加双向绑定后,丹尼尔罗斯在他的拉斯编辑中提出它可行。我仍然不认为它非常清楚(具有依赖属性,因为我需要将PagePadding保持为0值)。我认为这是一个黑客攻击方法。如果有人有更好的解决方案,请分享。

Obviously "changing PagePadding" of FlowDocument to 0,5 is a bug. If somebody has MSDN account it would be nice if they reported this bug.

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

3 个解决方案

#1


20  

I know this is annoying as hell.

我知道这很烦人。

RichTextBox sets this PagePadding in it's CreateRenderScope(), ie when it gets attached to the visual tree. At this time all properties are usually already set and thus the PagePadding gets reset.

RichTextBox将此PagePadding设置在其CreateRenderScope()中,即当它连接到可视树时。此时所有属性通常都已设置,因此PagePadding将被重置。

What I'm about to show you is a more general form of how you can do this using an attached property. In my own code I do this usually more tightly because I know that a) the flowdocument does not change (not having to worry about registering the same handler twice) and b) the padding does not change (having the eventhandler just be ((FlowDocument)s).PagePadding = new Thickness(0.0);. For this being SO though I'll provide a general solution that you can just plug in.

我要向您展示的是一种更为通用的形式,您可以使用附加属性来实现此目的。在我自己的代码中,我通常更紧密地执行此操作,因为我知道a)flowdocument不会更改(不必担心两次注册相同的处理程序)和b)填充不会更改(事件处理程序只是((FlowDocument) )s).PagePadding = new Thickness(0.0);.虽然这是SO,但我会提供一个通用的解决方案,你可以插入。

The Solution:

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

original sourcecode commentary:

In the original source of RichTextBox.CreateRenderScope() the developers included this comment:

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

bug report

here is the bug report on Microsoft Connect

这是关于Microsoft Connect的错误报告

#2


3  

The whole thing as I previously wrote doesn't work. For some reason the PagePadding is being overwritten as "5,0". However, when I used data-binding, it worked properly. So simply databind to a Thickness of 0. For it to work, you have to two-way databind:

我之前写的所有内容都不起作用。出于某种原因,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>

Code behind:

代码背后:

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

#3


1  

Try this. It works for me.... It's a lot less headache than the alternatives here...

尝试这个。它对我有用......它比这里的替代品要少得多......

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

#1


20  

I know this is annoying as hell.

我知道这很烦人。

RichTextBox sets this PagePadding in it's CreateRenderScope(), ie when it gets attached to the visual tree. At this time all properties are usually already set and thus the PagePadding gets reset.

RichTextBox将此PagePadding设置在其CreateRenderScope()中,即当它连接到可视树时。此时所有属性通常都已设置,因此PagePadding将被重置。

What I'm about to show you is a more general form of how you can do this using an attached property. In my own code I do this usually more tightly because I know that a) the flowdocument does not change (not having to worry about registering the same handler twice) and b) the padding does not change (having the eventhandler just be ((FlowDocument)s).PagePadding = new Thickness(0.0);. For this being SO though I'll provide a general solution that you can just plug in.

我要向您展示的是一种更为通用的形式,您可以使用附加属性来实现此目的。在我自己的代码中,我通常更紧密地执行此操作,因为我知道a)flowdocument不会更改(不必担心两次注册相同的处理程序)和b)填充不会更改(事件处理程序只是((FlowDocument) )s).PagePadding = new Thickness(0.0);.虽然这是SO,但我会提供一个通用的解决方案,你可以插入。

The Solution:

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

original sourcecode commentary:

In the original source of RichTextBox.CreateRenderScope() the developers included this comment:

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

bug report

here is the bug report on Microsoft Connect

这是关于Microsoft Connect的错误报告

#2


3  

The whole thing as I previously wrote doesn't work. For some reason the PagePadding is being overwritten as "5,0". However, when I used data-binding, it worked properly. So simply databind to a Thickness of 0. For it to work, you have to two-way databind:

我之前写的所有内容都不起作用。出于某种原因,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>

Code behind:

代码背后:

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

#3


1  

Try this. It works for me.... It's a lot less headache than the alternatives here...

尝试这个。它对我有用......它比这里的替代品要少得多......

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