简体   繁体   English

Silverlight TreeView ScrollViewer问题

[英]Silverlight TreeView ScrollViewer Issue

Hey SO, got a question about the TreeView control in Silverlight. 嗨,所以,有一个关于Silverlight中TreeView控件的问题。

I have an application which dynamically adds elements to a treeview. 我有一个应用程序,可以动态地将元素添加到树视图中。 Some of the elements are long enough to require horizontal scrolling. 一些元素足够长,需要水平滚动。 When they are added to the treeview, my treeview remains correctly all the way scrolled left so you have to scroll to see the end of the item. 将它们添加到树形视图后,我的树形视图始终保持向左滚动的正确方式,因此您必须滚动才能看到项目的结尾。 However, if I click on one of my items (which the hides the treeview), and then use the 'back to results' button I implemented (note this only deals with visibility changes) the treeview becomes visible and it is automatically scrolled to the center. 但是,如果单击我的一项(隐藏树视图),然后使用我实现的“返回结果”按钮(请注意,这仅处理可见性更改),树视图将变为可见,并且会自动滚动到中央。

Does anyone know how I can get the treeview to scroll all the way left when I hit back to results? 有谁知道当我返回结果时如何使树状视图向左滚动?

I've tried messing with the treeview template: 我试过弄乱treeview模板:

<Style TargetType="controls:TreeView" x:Name="SCREW">
            <Setter Property="Background" Value="#FFFFFFFF" />
            <Setter Property="Foreground" Value="#FF000000" />
            <Setter Property="HorizontalContentAlignment" Value="Left" />
            <Setter Property="VerticalContentAlignment" Value="Top" />
            <Setter Property="Cursor" Value="Arrow" />
            <Setter Property="BorderThickness" Value="1" />
            <Setter Property="Padding" Value="1" />
            <Setter Property="BorderBrush" Value="#FF000000" />
            <Setter Property="IsTabStop" Value="True" />
            <Setter Property="TabNavigation" Value="Once" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="controls:TreeView" x:Name="SCREWTEMPLATE">
                        <Grid>
                            <VisualStateManager.VisualStateGroups>
                                <VisualStateGroup x:Name="CommonStates">
                                    <VisualState x:Name="Normal" />
                                    <VisualState x:Name="MouseOver" />
                                    <VisualState x:Name="Pressed" />
                                    <VisualState x:Name="Disabled" />
                                </VisualStateGroup>
                                <VisualStateGroup x:Name="FocusStates">
                                    <VisualState x:Name="Unfocused" />
                                    <VisualState x:Name="Focused" />
                                </VisualStateGroup>
                                <VisualStateGroup x:Name="ValidationStates">
                                    <VisualState x:Name="Valid" />
                                    <VisualState x:Name="InvalidUnfocused">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Validation" Storyboard.TargetProperty="Visibility">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="Visible" />
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                    <VisualState x:Name="InvalidFocused">
                                        <Storyboard>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="Validation" Storyboard.TargetProperty="Visibility">
                                                <DiscreteObjectKeyFrame KeyTime="0" Value="Visible" />
                                            </ObjectAnimationUsingKeyFrames>
                                            <ObjectAnimationUsingKeyFrames Storyboard.TargetName="ValidationToolTip" Storyboard.TargetProperty="IsOpen">
                                                <DiscreteObjectKeyFrame KeyTime="0">
                                                    <DiscreteObjectKeyFrame.Value>
                                                        <System:Boolean>True</System:Boolean>
                                                    </DiscreteObjectKeyFrame.Value>
                                                </DiscreteObjectKeyFrame>
                                            </ObjectAnimationUsingKeyFrames>
                                        </Storyboard>
                                    </VisualState>
                                </VisualStateGroup>
                            </VisualStateManager.VisualStateGroups>

                            <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="2">
                                <Border Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}" Margin="1">
                                    <ScrollViewer x:Name="ScrollViewer" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Background="{x:Null}" BorderBrush="Transparent" BorderThickness="0" IsTabStop="False" TabNavigation="Once" Loaded="ScrollViewer_Loaded">
                                        <ItemsPresenter Margin="5" />
                                    </ScrollViewer>
                                </Border>
                            </Border>

                            <Border x:Name="Validation" Grid.Column="1" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="#FFDB000C" CornerRadius="2" Visibility="Collapsed">
                                <ToolTipService.ToolTip>
                                    <ToolTip x:Name="ValidationToolTip" Placement="Right" PlacementTarget="{Binding RelativeSource={RelativeSource TemplatedParent}}" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" IsHitTestVisible="True" />
                                </ToolTipService.ToolTip>
                                <Grid Width="10" Height="10" HorizontalAlignment="Right" Margin="0,-4,-4,0" VerticalAlignment="Top" Background="Transparent">
                                    <Path Margin="-1,3,0,0" Fill="#FFDC000C" Data="M 1,0 L6,0 A 2,2 90 0 1 8,2 L8,7 Z" />
                                    <Path Margin="-1,3,0,0" Fill="#FFFFFFFF" Data="M 0,0 L2,0 L 8,6 L8,8" />
                                </Grid>
                            </Border>
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

But the problem with that is I don't know how to access the ScrollViewer from the code behind... so I can't call ScrollView.setScrollOffset(0d,0d) or anything like that. 但是问题是我不知道如何从后面的代码访问ScrollViewer ...所以我不能调用ScrollView.setScrollOffset(0d,0d)或类似的东西。

Any ideas? 有任何想法吗? Thanks a million. 太感谢了。

One last thing, I'd like to try to avoid implementing a new control that extends treeview. 最后一件事,我想避免实现扩展树视图的新控件。 I'm really hoping there is a way to access/modify and use functions associated with the control template from c# codebehind. 我真的希望有一种方法可以从背后的c#代码访问/修改和使用与控件模板关联的功能。

I'd just set up an attached property for this and create the logic you want in there. 我只是为此设置了一个附加属性,并在其中创建所需的逻辑。 Then you would decorate your treeview with the attached property. 然后,您将使用附加的属性装饰树视图。 We do something similar with other controls that contain scrollviewers: 我们对包含滚动查看器的其他控件执行类似的操作:

public class ScrollResetService
{
    public static DependencyProperty IsScrollResetProperty = DependencyProperty.RegisterAttached("IsScrollReset",
                                                                                                 typeof(bool),
                                                                                                 typeof(ScrollResetService),
                                                                                                 new PropertyMetadata(false,
                                                                                                      OnIsScrollResetChanged));
    public static void SetIsScrollReset(DependencyObject d, bool value)
    {
        d.SetValue(IsScrollResetProperty, value);
    }

    public static bool GetIsScrollReset(DependencyObject d)
    {
        return d.GetValue(IsScrollResetProperty) == null ? false : (bool)d.GetValue(IsScrollResetProperty);
    }

    private static void OnIsScrollResetChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var treeView = d as TreeView;
        bool isScrollReset;
        if (e.NewValue!= null && bool.TryParse(e.NewValue.ToString(), out isScrollReset) && treeView != null)
        {

            treeView.SelectedItemChanged += (sender, args) =>
                                               {
                                                   var scrolls =
                                                       treeView.GetAllLogicalChildrenOfType<IScrollInfo>();
                                                   scrolls.ForEach(i => i.SetVerticalOffset(0));
                                               };

        }
    }

}

public static class Extensions
    {

public static IEnumerable<T> GetAllLogicalChildrenOfType<T>(this FrameworkElement parent)
        {
            Debug.Assert(parent != null, "The parent cannot be null.");
            return parent.GetVisualChildren().Flatten(item => item.GetVisualChildren()).OfType<T>();
        }

public static IEnumerable<T> Flatten<T>(this IEnumerable<T> items, Func<T, IEnumerable<T>> childSelector)
        {
            if (items == null) return Enumerable.Empty<T>();
            return items.Concat(items.SelectMany(i => childSelector(i).Flatten(childSelector)));
        }

internal static IEnumerable<DependencyObject> GetVisualChildren(this DependencyObject parent)
        {
            int childCount = VisualTreeHelper.GetChildrenCount(parent);
            for (int counter = 0; counter < childCount; counter++)
            {
                yield return VisualTreeHelper.GetChild(parent, counter);
            }
        }
}

I put this code into a behavior (would also work in code behind) after handling the SelectedItemChanged event of the TreeView : 在处理TreeViewSelectedItemChanged事件后,我将此代码放入了一个行为中(也将在后面的代码中工作):

var offset = this.HorizontalScrollOffsetAfterSelect; // would be 0 if you don't want to make that adjustable
if (!Double.IsNaN(offset))
{
    var scrollViewer = trv.GetVisualDescendants().OfType<ScrollViewer>().FirstOrDefault();
    if (scrollViewer != null)
    {
        scrollViewer.ScrollToHorizontalOffset(offset);
        // and because that wasn't enough because of timing issues:
        var scrollBar = scrollViewer.GetVisualDescendants().OfType<ScrollBar>().Where(cur => cur.Orientation == Orientation.Horizontal).FirstOrDefault();
        if (scrollBar != null)
        {
            RoutedPropertyChangedEventHandler<double> handler = null;
            handler = (sender, e) =>
                {
                    scrollBar.ValueChanged -= handler;

                    scrollViewer.ScrollToHorizontalOffset(offset);
                };
            scrollBar.ValueChanged += handler;
        }
    }
}

Requires using System.Linq; 需要using System.Linq; and using System.Windows.Controls.Primitives; using System.Windows.Controls.Primitives; and the Toolkit ofc. 和工具包ofc。

Works well enough for me so far. 到目前为止对我来说已经足够好了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM