WPF: Как сделать RichTextBox похожим на TextBlock?

Как я могу сделать RichTextBox без полей, границ, отступов и т. д.? Другими словами, отображать контент так же, как TextBlock Является ли? Я попробовал это:

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

Но результат производит все еще не то, что я хочу:

До содержимого документа еще есть место (а также, возможно, после, вверху или внизу документа...). Как я могу удалить это?


Если вам интересно, зачем мне это нужно: я пытаюсь дать ответ HB на мой вопрос. Создайте редактор гитарных аккордов в 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>

И я подумал, что на этот вопрос будет легко ответить... Интересное наблюдение: когда у меня есть набор шаблонов, и я устанавливаю PagePadding="0" на FlowDocument он отображает макет, который я хочу в конструкторе VisualStudio - пока я не запустил демонстрацию. В демоверсии это опять не так... А когда я закрываю демо, в дизайнере снова неправильно. Это небольшая ошибка в VS или она на какое-то время настроена на правильный макет, но затем что-то меняет значение PagePadding вернуться к какому-то неправильному значению?


Изменить #2

Отредактированный ответ Даниэля Роуза также не работает для меня. Это 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

После добавления двухсторонней привязки, как предложил Даниэль Роуз в своем редакторе las, это работает. Тем не менее, я не думаю, что это очень ясно (иметь свойство зависимости, потому что мне нужно сохранить PagePadding в 0 значение). Я думаю, что это обходной путь. Если у кого-то есть лучшее решение, поделитесь им.

Очевидно, "меняется PagePadding "из FlowDocument в 0,5 это ошибка Если у кого-то есть учетная запись MSDN, было бы хорошо, если бы они сообщили об этой ошибке.

4 ответа

Решение

Я знаю, что это чертовски раздражает.

RichTextBox устанавливает этот PagePadding в это CreateRenderScope()т.е. когда он привязывается к визуальному дереву. В настоящее время все свойства обычно уже установлены, и, таким образом, PagePadding сбрасывается.

Я собираюсь показать вам более общую форму того, как вы можете сделать это, используя прикрепленное свойство. В моем собственном коде я обычно делаю это более строго, потому что я знаю, что а) потоковый документ не меняется (не нужно беспокоиться о регистрации одного и того же обработчика дважды) и б) заполнение не меняется (если обработчик событий просто ((FlowDocument)s).PagePadding = new Thickness(0.0);, Поскольку это так, хотя я предоставлю общее решение, которое вы можете просто подключить.

Решение:

        <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

Все, что я писал ранее, не работает. По какой-то причине 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;
    }
}

Попробуй это. Это работает для меня.... Это гораздо меньше головной боли, чем альтернативы здесь...

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

На самом деле это не ошибка. Такое поведение предназначено для улучшения отображения биди и Italic индикатора каретки.

Посмотрите исходный код .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.CaretPaddingWidthопределение в файле CaretElement.cs :

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

Для большей достоверности рассмотрим следующие три элемента управления: TextBlock, и :

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

Эти элементы управления при использовании одного и того же размера / стиля шрифта, отступов и полей отображаются, как на скриншоте ниже:

Как легко увидеть TextBoxтакже есть некоторый дополнительный интервал, предназначенный для отображения индикатора каретки. Но из-за RichTextBox индикатор каретки имеет больше визуальных эффектов, для него отведено больше места.

Решение, которое сработало для меня, просто установлено <RichTextBox Padding="-5,0,0,0">, как предложено в посте Стива .

Другие вопросы по тегам