Автоматическое масштабирование содержимого 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