问题描述
因此,如果项目在滚动区域的末尾被剪切/裁剪,那么拥有无铬集合看起来真的很愚蠢.
So, it looks really silly to have a chrome-less collection if the items are getting cut/cropped at the end of the scroll region.
我想为集合 (ItemsControl/ListBox) 创建一个虚拟化面板,它只绘制整个项目,而不是项目的片段.例如:
I want to create a virtualizing panel for collections (ItemsControl/ListBox) that only draws whole items, never pieces of items. For example:
______________
| |
|______________|
______________
| |
|______________|
______________
| |
我不希望显示第三个部分容器,除非有空间可以显示整个项目/容器.在示例中,由于空间不足,第三项被裁剪.
I don't want the 3rd partial container to be displayed unless there is room for the WHOLE item/container to be displayed. In the examples, the third item was cropped because of a lack of space.
有什么建议吗?我是否应该尝试重新发明轮子(构建我自己的 VirtualizingWholeItemPanel
)?
Any suggestions? Should I try to reinvent the wheel (build my own VirtualizingWholeItemPanel
)?
编辑:
Microsoft 澄清说 VirtualizingPanel.ScrollUnit
根本不打算执行此功能.看起来 VirtualizingPanel.ScrollUnit
的作用与 ScrollViewer
上的旧 CanContentScroll
非常相似.
Microsoft clarified that VirtualizingPanel.ScrollUnit
is not intended to perform this functionality at all. It appears that VirtualizingPanel.ScrollUnit
serves a very similar purpose to the old CanContentScroll
on ScrollViewer
.
推荐答案
我有一个辅助方法,用于确定控件在父容器中是部分可见还是完全可见.您可能可以将它与 Converter
一起使用来确定项目的可见性.
I have a helper method which I use to determine if a control is partially or completly visible within a parent container. You can probably use it with a Converter
to determine the items' visibility.
您的转换器要么需要根据 UI 项计算父容器(我的博客有一组 Visual Tree Helpers,如果您愿意,可以提供帮助),或者它可以是一个接受 UI 项和父容器的 MultiConverter
作为参数.
Your converter would either need to calculate the parent container from the UI item (My blog has a set of Visual Tree Helpers that could assist with this if you want), or it could be a MultiConverter
that accepts both the UI item and the parent container as parameters.
ControlVisibility ctrlVisibility=
WPFHelpers.IsObjectVisibleInContainer(childControl, parentContainer);
if (ctrlVisibility == ControlVisibility.Full
|| isVisible == ControlVisibility.FullHeightPartialWidth)
{
return Visibility.Visible;
}
else
{
return = Visibility.Hidden;
}
确定控件在其父控件中可见性的代码如下所示:
The code to determine a control's visibility within it's parent looks like this:
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;
}
编辑
做了一些测试,显然转换器在控件实际呈现之前运行.作为一个技巧,如果您使用 MultiConverter
并将控件的 ActualHeight
传递给它,它将起作用,这将强制转换器在控件呈现时重新评估.
Did some tests and apparently the converter gets run before controls are actually rendered. As a hack, it will work if you use a MultiConverter
and pass it the ActualHeight
of the control, which will force the converter to re-evaluate when the control gets rendered.
这是我使用的转换器:
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
I used the XAML you posted in your question, and just added an implicit style for ListBoxItem
in the .Resources
<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>
这篇关于不裁剪项目的虚拟化面板的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!