带有溢出的可滚动包装

时间:2014-10-03 15:30:33

标签: wpf scrollviewer textwrapping

我需要在可调整大小的列中显示一个文本块。文本应该包含溢出,但是对于给定的列大小,用户应该能够水平滚动以查看溢出的文本。

我不相信这可以通过开箱即用的控制来实现;如果我错了,请告诉我。我尝试使用自定义控件实现此目的:

public class Sizer : ContentPresenter
{
    static Sizer()
    {
        ContentPresenter.ContentProperty.OverrideMetadata(typeof(Sizer), new FrameworkPropertyMetadata(ContentChanged)); ;
    }

    public Sizer() : base() {}        

    protected override Size MeasureOverride(Size constraint)
    {
        var childWidth = Content==null ? 0.0 : ((FrameworkElement)Content).RenderSize.Width;
        var newWidth = Math.Max(RenderSize.Width, childWidth);
        return base.MeasureOverride(new Size(newWidth, constraint.Height));
    }

    private static void ContentChanged(DependencyObject dep, DependencyPropertyChangedEventArgs args)
    {
        var @this = dep as Sizer;
        var newV = args.NewValue as FrameworkElement;
        var oldV = args.OldValue as FrameworkElement;
        if (oldV != null)
            oldV.SizeChanged -= @this.childSizeChanged;
        if(newV!=null)
            newV.SizeChanged += @this.childSizeChanged;
    }

    private void childSizeChanged(object sender, SizeChangedEventArgs e)
    {
        this.InvalidateMeasure();
    }
}

...我可以在一个简单的WPF应用程序中测试它,如下所示:

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="200"/>
        <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>

    <ScrollViewer Grid.Column="0" VerticalScrollBarVisibility="Disabled" HorizontalScrollBarVisibility="Auto" VerticalAlignment="Top">
        <local:Sizer>
            <local:Sizer.Content>
                <TextBlock Background="Coral" Text="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaa  aaaaaaaaaaaaaaaaaaa" 
                           TextWrapping="WrapWithOverflow" />
            </local:Sizer.Content>
        </local:Sizer>
    </ScrollViewer>
    <GridSplitter Grid.Column="1" Background="Black" VerticalAlignment="Stretch" HorizontalAlignment="Left" Width="5" />
</Grid>

这是一种时尚之后的作品。它正确地包装和显示文本,水平滚动条让我查看溢出的文本。如果我将列调整到右侧(更大),则文本会正确重新包装。但是,如果我将列调整到左侧(较小),则文本将不会重新换行。因此,例如,如果我向右调整列的大小以使所有文本都在一行上,则无论后续的重新调整大小,它都将保持在一行上。这是一个不可接受的错误。

我已经修改了这个代码很多,虽然我没有找到你称之为寻找解决方案的好策略。我不知道也无法发现强制文本块重新包装其内容的任何机制。有什么建议吗?

1 个答案:

答案 0 :(得分:0)

通过将以下内容添加到自定义控件中,我能够实现此功能(具有一些限制):

    //stores first ScrollViewer ancestor
    private ScrollViewer parentSV = null;

    //stores collection of observed valid widths
    private SortedSet<double> wrapPoints = new SortedSet<double>();

    protected override void OnVisualParentChanged(DependencyObject oldParent)
    {
        parentSV = this.FindParent<ScrollViewer>();
        base.OnVisualParentChanged(oldParent);
    }

...并编辑MeasureOverride列出:

protected override Size MeasureOverride(Size constraint)
    {
        if (parentSV != null)
        {
            var childWidth = Content == null ? 0.0 : ((FrameworkElement)Content).RenderSize.Width;
            var viewportWidth = parentSV.ViewportWidth;
            if (childWidth > viewportWidth + 5)
                wrapPoints.Add(childWidth);
            var pt = wrapPoints.FirstOrDefault(d => d > viewportWidth);
            if (pt < childWidth)
                childWidth = pt;

            return base.MeasureOverride(new Size(childWidth, constraint.Height));
         }
         else
             return base.MeasureOverride(constraint);
    }

我根本不喜欢这个。它不适用于WrapPanel(虽然这对我来说不是必需的用例),它偶尔会闪烁,我担心wrapPoints集合可能会变得非常大。但它做了我需要做的事情。