虚拟化面板不会裁剪项目

5

因此,如果项目在滚动区域的末尾被剪切/裁剪,那么拥有无边框集合看起来非常愚蠢。

我想为集合(ItemsControl/ListBox)创建一个虚拟面板,它只绘制整个项目,而不是项目的部分。例如:

 ______________
|              |
|______________|
 ______________
|              |
|______________|
 ______________
|              |

enter image description here

我希望只有当能完整显示该项/容器时,才显示第三个局部容器。在示例中,由于空间不足,第三项被裁剪。

有什么建议吗? 我应该尝试重新发明轮子(构建自己的 VirtualizingWholeItemPanel )吗?

编辑:

微软澄清, VirtualizingPanel.ScrollUnit 根本没有执行此功能的意图。看起来 VirtualizingPanel.ScrollUnit ScrollViewer 上的旧 CanContentScroll 非常相似。


你是否更改了 VirtualizingPanel.ScrollUnit 的设置?因为默认情况下它被设置为 Item,这正是你要寻找的。也许可以发布一些代码和截图,由于这是开发者预览版,如果确实是一个 bug,你可以提交一个故障工单到 Microsoft Connect? - myermian
我明确地将 ScrollUnit 设置为 Item。我将编辑并添加我的示例代码。 - Josh G
据我所知,这是虚拟化的“特性”。为了提高速度,当显示容器被重复使用时,宽度不会重新评估。你可以尝试水平内容对齐 = stretch。或者你可以迭代字符串列表并显式地设置宽度以适应最大值。或者你可以设置一个固定的宽度,你认为可以处理大部分情况,并打开文本换行。 - paparazzo
尝试移除两个附加属性CacheLength...也许它们在干扰? - myermian
1
也许你应该使用微软提供给你的新信息更新你的问题,该信息在你的Microsoft Connect bug report中提供 :) - BoltClock
显示剩余7条评论
1个回答

4

我有一个帮助方法,用于确定控件在父容器中部分或完全可见。您可以使用它与Converter一起使用来确定项目的可见性。

您的转换器需要从UI项计算父容器(如果您愿意,我的博客有一组Visual Tree Helpers可以帮助您完成这个任务),或者它可以是接受UI项和父容器作为参数的MultiConverter

ControlVisibility ctrlVisibility= 
    WPFHelpers.IsObjectVisibleInContainer(childControl, parentContainer);

if (ctrlVisibility == ControlVisibility.Full 
    || isVisible == ControlVisibility.FullHeightPartialWidth)
{
    return Visibility.Visible;
}
else
{
    return = Visibility.Hidden;
}

确定控件在其父级中可见性的代码如下:

public enum ControlVisibility
{
    Hidden,
    Partial,
    Full,
    FullHeightPartialWidth,
    FullWidthPartialHeight
}


/// <summary>
/// Checks to see if an object is rendered visible within a parent container
/// </summary>
/// <param name="child">UI element of child object</param>
/// <param name="parent">UI Element of parent object</param>
/// <returns>ControlVisibility Enum</returns>
public static ControlVisibility IsObjectVisibleInContainer(
    FrameworkElement child, UIElement parent)
{
    GeneralTransform childTransform = child.TransformToAncestor(parent);
    Rect childSize = childTransform.TransformBounds(
        new Rect(new Point(0, 0), new Point(child.ActualWidth, child.ActualHeight)));

    Rect result = Rect.Intersect(
        new Rect(new Point(0, 0), parent.RenderSize), childSize);

    if (result == Rect.Empty)
    {
        return ControlVisibility.Hidden;
    }
    if (Math.Round(result.Height, 2) == childSize.Height 
        && Math.Round(result.Width, 2) == childSize.Width)
    {
        return ControlVisibility.Full;
    }
    if (result.Height == childSize.Height)
    {
        return ControlVisibility.FullHeightPartialWidth;
    }
    if (result.Width == childSize.Width)
    {
        return ControlVisibility.FullWidthPartialHeight;
    }
    return ControlVisibility.Partial;
}

编辑

进行了一些测试,显然转换器在控件实际呈现之前就已经运行。作为一种hack方法,如果您使用MultiConverter并将其传递给控件的ActualHeight,则会在控件呈现时强制重新评估转换器,这样它就可以正常工作。

以下是我使用的转换器:

public class TestConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        FrameworkElement child = values[0] as FrameworkElement;
        var parent = VisualTreeHelpers.FindAncestor<ListBox>(child);

        ControlVisibility ctrlVisibility =
            VisualTreeHelpers.IsObjectVisibleInContainer(child, parent);

        if (ctrlVisibility == ControlVisibility.Full
            || ctrlVisibility == ControlVisibility.FullHeightPartialWidth)
        {
            return Visibility.Visible;
        }
        else
        {
            return Visibility.Hidden;
        }
    }

    public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture)
    {
        return null;
    }
}

我使用了你在问题中发布的XAML代码,并在.Resources中添加了一个ListBoxItem的隐式样式。

<ListBox.Resources>
    <Style TargetType="{x:Type ListBoxItem}">
        <Setter Property="Visibility">
            <Setter.Value>
                <MultiBinding Converter="{StaticResource Converter}">
                    <Binding RelativeSource="{RelativeSource Self}" />
                    <Binding RelativeSource="{RelativeSource Self}" Path="ActualHeight" />
                </MultiBinding>
            </Setter.Value>
        </Setter>
    </Style>
</ListBox.Resources>

ControlVisibility 可以转换为 [Flags] 枚举。这并不会带来什么重大变化,只是一个建议。 - myermian
1
@m-y:我认为没有理由将其作为标志枚举。该值永远不应等于多个可能性中的任何一个。据我所知,标志枚举用于使用布尔&、|将多个值连接在一起。 - Josh G
@m-y 脚本的原始目的是在按钮列表超出允许宽度时,用下拉菜单替换它们,并且一些模板允许部分可见项目,而其他模板则不允许。我认为枚举比标志更具描述性,Josh正确指出控件永远不会返回多个ControlVisibility值。 - Rachel
半打或其他,这不是一个要求。这只是建议与.NET代码的行为相一致:例如,看看CompareOptions,即使有可能某些组合没有意义,它也被标记为Flags。无论如何都可以工作,就像传入5而不是(2 + 3)一样。 - myermian
@JoshG 奇怪的是,DataBindRender 之后处理,因此 ActualHeight/ActualWidth 应该有一个值。过去我总是在对象的 LoadedSizeChanged 事件中使用它,但现在我很好奇它是否能在转换器中工作。 - Rachel
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接