Wpf 带溢出的可滚动换行

Wpf 带溢出的可滚动换行,wpf,scrollviewer,word-wrap,Wpf,Scrollviewer,Word Wrap,我需要在可调整大小的列中显示一块文本。文本应以溢出方式换行,但对于给定的列大小,用户应能够水平滚动以查看溢出文本 我不相信这可以通过开箱即用的控制来实现;如果我错了,请告诉我。我已尝试使用自定义控件实现这一点: public class Sizer : ContentPresenter { static Sizer() { ContentPresenter.ContentProperty.OverrideMetadata(typeof(Sizer), new Fr

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

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

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();
    }
}
    //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);
    }
…我可以在一个简单的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>

这很管用。它正确地包装和显示文本,水平滚动条允许我查看溢出的文本。如果我将列向右(更大)调整大小,文本将正确地重新换行。但是,如果我将列向左(较小)调整大小,文本将不会重新换行。因此,例如,如果我向右调整列的大小,使所有文本都在一行上,那么无论后续如何调整大小,所有文本都将保持在一行上。这是一个不可接受的错误


我已经修改了很多代码,尽管我还没有找到一个解决方案的好策略。我不知道,也没有发现任何强制textblock重新包装其内容的机制。有什么建议吗?

我可以通过向自定义控件添加以下内容来实现此功能(有一些限制):

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();
    }
}
    //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);
    }
//存储第一个ScrollViewer祖先
private ScrollViewer parentSV=null;
//存储观察到的有效宽度的集合
专用分拣集包装点=新分拣集();
VisualParentChanged上受保护的覆盖无效(DependencyObject oldParent)
{
parentSV=this.FindParent();
base.OnVisualParentChanged(旧父级);
}
。。。和编辑度量超越列表:

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);
    }
受保护的覆盖尺寸测量覆盖(尺寸约束)
{
if(parentSV!=null)
{
var childWidth=Content==null?0.0:((FrameworkElement)Content);
var viewportWidth=parentSV.viewportWidth;
如果(childWidth>viewportWidth+5)
wrapPoints.Add(childWidth);
var pt=wrapPoints.FirstOrDefault(d=>d>viewportWidth);
if(pt

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

只是一个注释,但我已经看到,当您减小大小时,UI呈现不会重新测量。甚至从MSFT网站上找到了一些精心设计的东西,看起来很有希望。你有没有可能找到你读到的关于它的链接?没有,但是如果你搜索我的问题,我知道我是在网上问的。我得到了一个很好的答案,它只告诉我这是一条死胡同。我怀疑你指的是:“[VirtulizingStackPanel]的度量算法……记住了有史以来发现的最大尺寸,并强制所有未来的度量调用报告至少同样大的尺寸。”我的树中没有VSP,但如果ScrollContentPresenter做了类似的事情,就会产生我看到的行为。谢谢是的,就是这样,但我觉得我在其他方面遇到了麻烦。嘿,这只是一个评论。