那种行为在没有明确设置
Vertical
方向的
WrapPanel
的
Height
/
MinHeight
或
Horizontal
方向的
Width
/
MinWidth
时是不可能实现的。只有当这个滚动视图包裹的
FrameworkElement
超出了视口范围,
ScrollViewer
才会显示滚动条。
你可以创建自己的包裹面板,根据其子元素计算其最小尺寸。
或者,你可以实现一个
Behavior<WrapPanel>
或一个附加属性。这并不像你期望的那样简单,只需添加几个XAML标记。
我们已经通过附加属性解决了这个问题。让我给你一个我们所做的想法。
static class ScrollableWrapPanel
{
public static readonly DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(ScrollableWrapPanel), new PropertyMetadata(false, IsEnabledChanged));
static void IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var panel = (WrapPanel)d;
if (!panel.IsInitialized)
{
panel.Initialized += PanelInitialized;
}
}
static void PanelInitialized(object sender, EventArgs e)
{
var panel = (WrapPanel)sender;
DependencyPropertyDescriptor.FromProperty(
WrapPanel.OrientationProperty,
typeof(WrapPanel))
.AddValueChanged(panel, OrientationChanged);
panel.Unloaded += PanelUnloaded;
OrientationChanged(panel, EventArgs.Empty);
}
static void OrientationChanged(object sender, EventArgs e)
{
var panel = (WrapPanel)sender;
if (panel.Orientation == Orientation.Vertical)
{
BindingOperations.ClearBinding(panel, WrapPanel.MinWidthProperty);
var converter = new MaxValueConverter();
var minHeightBiding = new MultiBinding { Converter = converter };
foreach (var child in panel.Children.OfType<FrameworkElement>())
{
minHeightBiding.Bindings.Add(new Binding("ActualHeight") { Mode = BindingMode.OneWay, Source = child });
}
BindingOperations.SetBinding(panel, WrapPanel.MinHeightProperty, minHeightBiding);
BindingOperations.ClearBinding(panel, WrapPanel.WidthProperty);
var binding = new Binding("ViewportHeight")
{
RelativeSource = new RelativeSource { Mode = RelativeSourceMode.FindAncestor, AncestorType = typeof(ScrollViewer)}
};
BindingOperations.SetBinding(panel, WrapPanel.HeightProperty, binding);
}
else
{
}
}
static void PanelUnloaded(object sender, RoutedEventArgs e)
{
var panel = (WrapPanel)sender;
panel.Unloaded -= PanelUnloaded;
DependencyPropertyDescriptor.FromProperty(WrapPanel.OrientationProperty, typeof(WrapPanel))
.RemoveValueChanged(panel, OrientationChanged);
}
private class MaxValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
return values.Cast<double>().Max();
}
}
}
也许这不是最简单的方法,需要写比几个XAML标记更多的代码行数,但它可以无缝运行。
但是在错误处理方面要小心。在样例代码中,我省略了所有检查和异常处理。
使用方法很简单:
<ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
<WrapPanel Orientation="Vertical" local:ScrollableWrapPanel.IsEnabled="True">
<!
</WrapPanel>
</ScrollViewer
DockPanel.Dock="Bottom"
的DockPanel。 - Ortund