7690


WPFアプリケーションにてViewboxを使用し文字列(TextBlockやTextBox）を縮小表示しようとしています。  
文字数が少なかったり、改行コードが適当な位置に挿入されている場合は期待どおりに縮小されます。（下イメージの①・②）  
ですが１行の文字数が大きい場合だと、折り返しが行われず１行にすべてが収まるよう縮小されてしまいます。

[code]

    <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="400" Width="400">
    <Grid>
        <Grid.Resources>
            <Style TargetType="Border">
                <Setter Property="Margin" Value="10" />
                <Setter Property="BorderBrush" Value="Black" />
                <Setter Property="BorderThickness" Value="1" />
            </Style>
            <Style TargetType="Viewbox">
                <Setter Property="StretchDirection" Value="DownOnly" />
                <Setter Property="VerticalAlignment" Value="Top" />
                <Setter Property="HorizontalAlignment" Value="Left" />
            </Style>
            <Style TargetType="TextBlock">
                <Setter Property="FontSize" Value="20" />
                <Setter Property="TextWrapping" Value="Wrap" />
            </Style>
        </Grid.Resources>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
            <RowDefinition />
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" VerticalAlignment="Center" Text="①" />
        <Border Grid.Row="0" Grid.Column="1">
            <Viewbox>
                <TextBlock>
                    標準サイズ
                </TextBlock>
            </Viewbox>
        </Border>
        <TextBlock Grid.Row="1" VerticalAlignment="Center" Text="②" />
        <Border Grid.Row="1" Grid.Column="1">
            <Viewbox>
                <TextBlock>
                    表示領域にあわせて
                    <LineBreak />
                    縮小された
                    <LineBreak />
                    サイズ
                    <LineBreak />
                    です。
                </TextBlock>
            </Viewbox>
        </Border>
        <TextBlock Grid.Row="2" VerticalAlignment="Center" Text="③" />
        <Border Grid.Row="2" Grid.Column="1">
            <Viewbox>
                <TextBlock>
                    ああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああ
                    <LineBreak />
                    いいいいいいいいいいいい
                    <LineBreak />
                    ううううううううううううううううううううううううううううううううう
                </TextBlock>
            </Viewbox>
        </Border>
        <TextBlock Grid.Row="3" VerticalAlignment="Center" Text="④" />
        <Border Grid.Row="3" Grid.Column="1" x:Name="bd">
            <Viewbox>
                <TextBlock Width="540">
                    ああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああああ
                    <LineBreak />
                    いいいいいいいいいいいい
                    <LineBreak />
                    ううううううううううううううううううううううううううううううううう
                </TextBlock>
            </Viewbox>
        </Border>
    </Grid>
    
[/code]

TextBlockは縮小のみ行いたいため、ViewBoxのStretchDirectionをDownOnlyに。  
字体のバランスを崩したくないため、Stretchは既定(Uniform）としています。

![画像の説明をここに入力](https://i.stack.imgur.com/d31NB.png)

③のようになってしまうのが困っております。  
④のようなイメージが期待値となりますが、④は適宜Widthを調整しています。  
入力される文字数・改行位置などが想定できないため、XAML上でWidthを調整するのではなく内容に応じて自動で④のように縮小表示したいと考えております。

良い方法があれば是非アドバイスをお願いします。

XAMLファイルのサンプルコードが少々長くなってしまいました、ご容赦ください。

環境:Windows8.1 + .net framework 4.5.2


CC BY-SA 3.0
2015-03-09T15:23:58.670
7670
2015-03-10T03:59:10.500
2015-03-09T15:57:05.587
7740
7740
question
wpf xaml
Viewbox内でTextWrappingを設定した場合に折り返しをしたい


仕組みとしては、

  * `{Binding RelativeSource={RelativeSource AncestorType=Border}, Path=ActualWidth}`
  * `{Binding RelativeSource={RelativeSource AncestorType=Border}, Path=ActualHeight}`
  * 表示テキスト

この3点を参照する`MultiBinding`と`IMultiValueConveter`で計算できるのではないかと思います。  
コンバーターの実装は幅を変えつつ`FormattedText`で`Size`を測って最適値を求めるような感じでしょうかね。


CC BY-SA 3.0
2015-03-09T21:53:10.710
7678
2015-03-09T21:53:10.710
5750
7670
answer


Viewbox内にできるだけ大きな文字ですべて表示すると解釈します。

[code]

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:WpfApplication1"
            Title="MainWindow" Height="350" Width="525">
        <DockPanel>
            <Slider DockPanel.Dock="Top" x:Name="sl" Minimum="10" Maximum="100"/>
            <ComboBox DockPanel.Dock="Top" x:Name="combo">
                <ComboBox.ItemsSource>
                    <Binding>
                        <Binding.Source>
                            <CollectionViewSource >
                                <CollectionViewSource.Source>
                                    <ObjectDataProvider  MethodName="GetValues" ObjectType="{x:Type sys:Enum}" xmlns:sys="clr-namespace:System;assembly=mscorlib">
                                        <ObjectDataProvider.MethodParameters>
                                            <x:TypeExtension TypeName="StretchDirection"/>
                                        </ObjectDataProvider.MethodParameters>
                                    </ObjectDataProvider>
                                </CollectionViewSource.Source>
                            </CollectionViewSource>
                        </Binding.Source>
                    </Binding>
    
                </ComboBox.ItemsSource>
            </ComboBox>
            <local:ViewboxEx  Stretch="Uniform" StretchDirection="{Binding Path=SelectedItem,ElementName=combo}" >
                <TextBox Text="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA&#13;&#10;bbbbbbbbbb" 
                         TextWrapping="Wrap" FontSize="{Binding Path=Value,ElementName=sl}"/>
            </local:ViewboxEx>
        </DockPanel>
    </Window>
    
[/code]

* * *
[code]

    class ViewboxEx : Viewbox
    {
        protected override Size MeasureOverride(Size availableSize)
        {
            FrameworkElement child = this.Child as FrameworkElement;
            if (child == null)
            {
                return base.MeasureOverride(availableSize);
            }
            child.Width = double.NaN;
            child.Height = double.NaN;
            switch (this.StretchDirection)
            {
                case StretchDirection.Both:
                    child.ClearValue(FrameworkElement.MinWidthProperty);
                    child.ClearValue(FrameworkElement.MaxWidthProperty);
                    break;
                case StretchDirection.DownOnly:
                    child.MinWidth = availableSize.Width;//DownOnly
                    child.ClearValue(FrameworkElement.MaxWidthProperty);
                    break;
                case StretchDirection.UpOnly:
                    child.ClearValue(FrameworkElement.MinWidthProperty);
                    child.MaxWidth = availableSize.Width;//DownOnly
                    break;
                default:
                    break;
            }
            Size sz = base.MeasureOverride(availableSize);
            if (sz.Width == 0 || sz.Height == 0)
            {
            }
            else
            {
                Size csz = Child.DesiredSize;
                double thisRatio = availableSize.Width / availableSize.Height;
                double childRatio = child.DesiredSize.Width / child.DesiredSize.Height;
                if (childRatio != thisRatio)
                {
                    double div = 1;
                    child.Width = child.DesiredSize.Height * thisRatio;
                    child.Height = double.NaN;
                    sz = base.MeasureOverride(availableSize);
                    for (int i = 0; i < 10; i++)
                    {
                        childRatio = child.DesiredSize.Width / child.DesiredSize.Height;
                        if (childRatio < thisRatio)
                        {
                            child.Width = child.DesiredSize.Width + csz.Width / div;
                        }
                        else if (childRatio > thisRatio)
                        {
                            child.Width = Math.Max(0, child.DesiredSize.Width - csz.Width / div);
                        }
                        else if (childRatio == thisRatio)
                        {
                            break;
                        }
                        sz = base.MeasureOverride(availableSize);
                        div *= 2;
                    }
                }
            }
            return sz;
        }
    }
    
[/code]


CC BY-SA 3.0
2015-03-10T03:59:10.500
7690
2015-03-10T03:59:10.500
4926
7670
answer