Автоматическое масштабирование содержимого WPF TextBox/RichTextBox в соответствии с доступным пространством

У меня есть следующая проблема:

Я хочу настроить RichTextBox, который автоматически может изменять размер содержимого внутри, чтобы соответствовать максимуму доступного пространства, при этом не меняя макет содержимого (например, размеры шрифта, отступ и т. Д.).

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

До сих пор я установил LayoutTransform внутри AdornerDecorator шаблона RichTextBox, который обернут внутри ScrollViewer, чтобы я мог вызвать метод code-behind для вычисления Scaling.

Изначально RichTextBox не должен масштабироваться, когда весь контент помещается в ViewPort. Как только возникает необходимость включить вертикальную полосу прокрутки, я меняю ScaleFactor.

Это работает довольно хорошо, если только это не относится к TextWrapping (так что масштабирование X и Y не приведет к переносу текста в разных позициях и, кроме того, к изменению высоты). Есть идеи?

Я создал небольшую демонстрацию, чтобы сделать вещи немного понятнее:

<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Window.Resources>
    <ScaleTransform x:Key="ScaleTransform" ScaleY="{Binding ScaleFactor}"
                                           ScaleX="{Binding ScaleFactor}"/>
</Window.Resources>
<Grid>
    <RichTextBox AcceptsTab="True" 
                 Background="Transparent" 
                 BorderBrush="Transparent" 
                 VerticalScrollBarVisibility="Auto"
                 HorizontalScrollBarVisibility="Disabled"
                 HorizontalAlignment="Stretch" 
                 VerticalAlignment="Stretch">
        <RichTextBox.Template>
            <ControlTemplate TargetType="{x:Type TextBoxBase}">
                <Border CornerRadius="2" 
                        Background="{TemplateBinding Background}" 
                        BorderThickness="{TemplateBinding BorderThickness}" 
                        BorderBrush="{TemplateBinding BorderBrush}">
                    <ScrollViewer ScrollChanged="ScrollViewer_ScrollChanged">
                        <AdornerDecorator x:Name="PART_ContentHost" Focusable="False"
                                          LayoutTransform="{StaticResource ScaleTransform}"/>
                    </ScrollViewer>
                </Border>
            </ControlTemplate>
        </RichTextBox.Template>
    </RichTextBox>
</Grid>

И код позади:

public partial class Window1 : Window, INotifyPropertyChanged
{
    public Window1()
    {
        InitializeComponent();
        this.DataContext = this;
    }

    private double scaleFactor = 1.0;
    public double ScaleFactor
    {
        get { return scaleFactor; }
        set
        {
            scaleFactor = value;
            OnPropertyChanged("ScaleFactor");
        }
    }

    private void ScrollViewer_ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        if (e.ExtentHeightChange == 0)
            return;

        if (e.Source.GetType() == typeof(RichTextBox))
            return;

        var missingHeight = e.ExtentHeightChange;
        var heightWithoutScaling = e.ExtentHeight / ScaleFactor;

        if (e.ViewportHeight <= heightWithoutScaling)
        {
            ScaleFactor = ((e.ViewportHeight / heightWithoutScaling));
        }

    }

    #region INotifyPropertyChanged Members

    /// <summary>
    /// Raised when a property on this object has a new value
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Raises this object's PropertyChanged event.
    /// </summary>
    /// <param name="propertyName">The property that has a new value.</param>
    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
        {
            var e = new PropertyChangedEventArgs(propertyName);
            handler(this, e);
        }
    }

    #endregion

0 ответов

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