如何滚动以便在展开扩展器后可以看到控件

时间:2017-03-01 18:43:52

标签: c# wpf xaml

我在wpf usercontrol中有几个Expander控件。 当我扩展扩展器时,我想自动滚动,以便在可能的情况下可以看到整个扩展器。或至少所以新扩展的扩展器尽可能“向上”。

当我现在在底部时,它会在窗口边缘下方展开,我无法在扩展器中看到控件而无需手动滚动它们。

我想从XAML而不是代码隐藏中做到这一点。 有可能吗? 我正在使用MVVM模式。

我假设我必须在扩展器上使用触发器,但我不知道如何预先形成BringToView功能

高级布局:

grep

2 个答案:

答案 0 :(得分:3)

处理Expanded事件并调用BringIntoView()方法:

private void Expander_Expanded(object sender, RoutedEventArgs e)
{
    ((Expander)sender).BringIntoView();
}

请注意,您应该使用StackPanel或其他内容而不是Grid

<ScrollViewer Name="sv">
    <UserControl>
        <StackPanel>
            <Expander>
                <Border Height="1000" Background="Red"/>
            </Expander>
            <Expander>
                <Border Height="1000" Background="Blue"/>
            </Expander>
            <Expander Expanded="Expander_Expanded">
                <Border Height="1000" Background="Green"/>
            </Expander>
        </StackPanel>
    </UserControl>
</ScrollViewer>

修改

您可能想要使用此附加属性:

public static class ExpanderEx
{
    public static readonly DependencyProperty BringIntoViewOnExpandProperty =
                           DependencyProperty.RegisterAttached("BringIntoViewOnExpand", 
                                typeof(bool), typeof(ExpanderEx), 
                                new PropertyMetadata(false, OnBringIntoViewOnExpandChanged));


    public static bool GetBringIntoViewOnExpand(DependencyObject obj)
    {
        return (bool)obj.GetValue(BringIntoViewOnExpandProperty);
    }
    public static void SetBringIntoViewOnExpand(DependencyObject obj, bool value)
    {
        obj.SetValue(BringIntoViewOnExpandProperty, value);
    }  
    private static void OnBringIntoViewOnExpandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is Expander)
        {
            Expander obj = (Expander)d;
            if (e.NewValue.Equals(true)) 
                obj.Expanded += Obj_Expanded; 
            else
                obj.Expanded -= Obj_Expanded;
        }
    }

    private static void Obj_Expanded(object sender, RoutedEventArgs e)
    {
        ((Expander)sender).BringIntoView();
    }
}

和Xaml:

<ScrollViewer Name="sv">
    <UserControl>
        <StackPanel>
            <Expander local:ExpanderEx.BringIntoViewOnExpand="True">
                <Border Height="1000" Background="Red"/>
            </Expander>
            <Expander local:ExpanderEx.BringIntoViewOnExpand="True">
                <Border Height="1000" Background="Blue"/>
            </Expander>
            <Expander local:ExpanderEx.BringIntoViewOnExpand="True">
                <Border Height="1000" Background="Green"/>
            </Expander>
        </StackPanel>
    </UserControl>
</ScrollViewer>

答案 1 :(得分:2)

为其Expander事件提供每个Expanded此处理程序。看起来您正在UserControl内定义扩展器,因此转到ScrollViewer是个问题。一种选择是为您的UserControl提供ScrollViewer依赖项属性,所有者将绑定到包含它的任何ScrollViewer。我不喜欢那样。相反,我写了一个辅助函数,它查找可视树并找到最近的父ScrollViewer,如果有的话。

private void Expander_Expanded(object sender, RoutedEventArgs e)
{
    var scrollViewer = GetNearestScrollViewerParent();

    if (scrollViewer == null)
        return;

    var expander = (Expander)sender;

    UIElement container = VisualTreeHelper.GetParent(expander) as UIElement;
    Point relativeLocation = expander.TranslatePoint(new Point(0, 0), container);

    scrollViewer.ScrollToVerticalOffset(relativeLocation.Y);
}

public ScrollViewer GetNearestScrollViewerParent()
{
    for (var parent = VisualTreeHelper.GetParent(this); 
         parent != null; 
         parent = VisualTreeHelper.GetParent(parent))
    {
        if (parent is ScrollViewer)
            return parent as ScrollViewer;
    }

    return null;
}

XAML:

<UserControl.Resources>
    <Style TargetType="Expander" BasedOn="{StaticResource {x:Type Expander}}">
        <EventSetter Event="Expanded" Handler="Expander_Expanded" />
    </Style>
</UserControl.Resources>