ScrollIntoView - 项目不应从视图中消失

2023-12-26

从一开始我就想说,我将向能够帮助我解决问题的人颁发 200 赏金。

这是我的简单代码(带有 WPF 的 C#):

namespace WpfApplication1
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    /// 

    public partial class Window1 : Window
    {
        string fixedItem;

        public Window1()
        {
            InitializeComponent();
            listBox1.ItemContainerGenerator.ItemsChanged += new System.Windows.Controls.Primitives.ItemsChangedEventHandler(list_changes);

        }



        private void list_changes(object sender, System.Windows.Controls.Primitives.ItemsChangedEventArgs e)
        {
            listBox1.ScrollIntoView(fixedItem);
        }

        private void listBox1_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {      
            fixedItem = (string)listBox1.SelectedItem;        
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            listBox1.Items.Add("item0");
            listBox1.Items.Add("item1");
            listBox1.Items.Add("item2");
            listBox1.Items.Add("item3");
            listBox1.Items.Add("item4");
            listBox1.Items.Add("item5");
            listBox1.Items.Add("item6");

        }

        private void button2_Click(object sender, RoutedEventArgs e)
        {
            listBox1.Items.Insert(0, "item7");
            listBox1.Items.Insert(0, "item8");
            listBox1.Items.Insert(0, "item9");
            listBox1.Items.Insert(0, "item10");
            listBox1.Items.Insert(0, "item11");
            listBox1.Items.Insert(0, "item12");
            listBox1.Items.Insert(0, "item13");
            listBox1.Items.Insert(0, "item14");
            listBox1.Items.Insert(0, "item15");
        }

        private void button3_Click(object sender, RoutedEventArgs e)
        {
            listBox1.Items.Insert(0, "item16");
            listBox1.Items.Insert(0, "item17");
            listBox1.Items.Insert(0, "item18");
            listBox1.Items.Insert(0, "item19");
            listBox1.Items.Insert(0, "item20");
            listBox1.Items.Insert(0, "item21");
            listBox1.Items.Insert(0, "item22");
            listBox1.Items.Insert(0, "item23");
            listBox1.Items.Insert(0, "item24");
        }
    }
}

首先,我只制作了 3 个按钮来在列表框中插入一些文本。假设我单击按钮 1 和按钮 2,我将得到以下列表:

item15
item14
item13
item12
......
item7
item0
item1
.....
item6

之后,我想单击“项目 12”,然后当我单击按钮 3 时,我希望我的“项目 12”在生成文本时保留在同一位置(列表中的第四个位置)。
简而言之,每次我单击项目时,我希望它在生成文本时保持在完全相同的位置。

那么有人知道如何做到这一点吗?我是否需要使用 ScrollViewer 对象来处理 VerticallOffset 和 ViewportHeigth ?当我单击该项目然后生成文本时,我发布了这个简单的代码,它将将该项目移动到底部(可见位置)并保留在那里。但我根本不想动它。

Edit:

好的,我尝试了这段代码here http://blogs.msdn.com/b/delay/archive/2009/04/19/fewer-gotchas-to-getcha-enhancing-the-scrollintoviewcentered-method-for-wpf-s-listbox.aspx:

FrameworkElement container = listRadioItems.ItemContainerGenerator.ContainerFromItem(fixedItem) as FrameworkElement;

            if (null != container)
            {
                if (ScrollViewer.GetCanContentScroll(listBox))
                {
                    IScrollInfo scrollInfo = VisualTreeHelper.GetParent(container) as IScrollInfo;
                    if (null != scrollInfo)
                    {
                        StackPanel stackPanel = scrollInfo as StackPanel;
                        VirtualizingStackPanel virtualizingStackpanel = scrollInfo as VirtualizingStackPanel;

                        int index = listBox.ItemContainerGenerator.IndexFromContainer(container);

                        if (((null != stackPanel) && (Orientation.Horizontal == stackPanel.Orientation)) || ((null != virtualizingStackpanel) && (Orientation.Horizontal == virtualizingStackpanel.Orientation)))
                        {
                            scrollInfo.SetHorizontalOffset(index - Math.Floor(scrollInfo.ViewportWidth / 2));
                        }
                        else
                        {
                            scrollInfo.SetVerticalOffset(index - Math.Floor(scrollInfo.ViewportHeight / 2));
                        }
                    }
                }
                else
                {
                    Rect rect = new Rect(new Point(), container.RenderSize);

                    FrameworkElement constrainingParent = container;
                    do
                    {
                        constrainingParent = VisualTreeHelper.GetParent(constrainingParent) as FrameworkElement;
                    } while ((null != constrainingParent) && (listBox != constrainingParent) && !(constrainingParent is ScrollContentPresenter));

                    if (null != constrainingParent)
                    {
                        rect.Inflate(Math.Max((constrainingParent.ActualWidth - rect.Width) / 2, 0), Math.Max((constrainingParent.ActualHeight - rect.Height) / 2, 0));
                    }

                    container.BringIntoView(rect);
                }
            }

它对我的作用是将所选项目居中,但滚动向下,只是有时它也会居中。我的问题是,只有有时所选项目才会从视图中消失。

如果我能让项目和滚动都居中,那就太好了。但首先要关心的是该项目不应从视图中消失。


下面是一些代码,用于获取 ListBox 中的第一个可见项目,将这些项目添加到您指定的任何索引,然后在呈现所有新项目后使用调度程序将列表滚动回第一个可见项目。

The ScrollIntoViewTop()扩展方法与您在原始问题中发布的链接相同,但我更改了它以将该项目保留在列表顶部。

The WPFHelpers.IsObjectVisibleInContainer()方法是我过去用来测试对象在另一个容器中是否完全或部分可见的方法。为了获取第一个可见项目,我只需循环遍历 ListBox 项目,获取与每个项目关联的 ListBoxItem 容器,然后检查该容器是否可见。第一个返回 true 的是 ListBox 中的第一个可见项。

这是完整的代码:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
    }
    private void button1_Click(object sender, RoutedEventArgs e)
    {
        var firstVisibleItem = GetFirstVisibleItem(listBox1);

        listBox1.Items.Insert(0, "item0");
        listBox1.Items.Insert(0, "item1");
        listBox1.Items.Insert(0, "item2");
        listBox1.Items.Insert(0, "item3");
        listBox1.Items.Insert(0, "item4");
        listBox1.Items.Insert(0, "item5");
        listBox1.Items.Insert(0, "item6");

        if (firstVisibleItem != null)
        {
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Loaded,
                new Action(delegate()
                {
                    listBox1.ScrollIntoViewTop(firstVisibleItem);
                }));
        }
    }

    private void button2_Click(object sender, RoutedEventArgs e)
    {
        var firstVisibleItem = GetFirstVisibleItem(listBox1);

        listBox1.Items.Insert(0, "item7");
        listBox1.Items.Insert(0, "item8");
        listBox1.Items.Insert(0, "item9");
        listBox1.Items.Insert(0, "item10");
        listBox1.Items.Insert(0, "item11");
        listBox1.Items.Insert(0, "item12");
        listBox1.Items.Insert(0, "item13");
        listBox1.Items.Insert(0, "item14");
        listBox1.Items.Insert(0, "item15");

        if (firstVisibleItem != null)
        {
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Loaded,
                new Action(delegate()
                {
                    listBox1.ScrollIntoViewTop(firstVisibleItem);
                }));
        }
    }

    private void button3_Click(object sender, RoutedEventArgs e)
    {
        var firstVisibleItem = GetFirstVisibleItem(listBox1);

        listBox1.Items.Insert(0, "item16");
        listBox1.Items.Insert(0, "item17");
        listBox1.Items.Insert(0, "item18");
        listBox1.Items.Insert(0, "item19");
        listBox1.Items.Insert(0, "item20");
        listBox1.Items.Insert(0, "item21");
        listBox1.Items.Insert(0, "item22");
        listBox1.Items.Insert(0, "item23");
        listBox1.Items.Insert(0, "item24");

        if (firstVisibleItem != null)
        {
            Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Loaded,
                new Action(delegate()
                {
                    listBox1.ScrollIntoViewTop(firstVisibleItem);
                }));
        }
    }

    private object GetFirstVisibleItem(ListBox listBox)
    {
        foreach (var item in listBox.Items)
        {
            var itemContainer = (ListBoxItem)listBox.ItemContainerGenerator.ContainerFromItem(item);

            if (WPFHelpers.IsObjectVisibleInContainer(itemContainer, listBox) == ControlVisibility.Full)
            {
                return item;
            }
        }

        return null;
    }
}

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

public class WPFHelpers
{
    /// <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: Hidden, Partial or Visible</returns>
    public static ControlVisibility IsObjectVisibleInContainer(FrameworkElement child, UIElement parent)
    {
        GeneralTransform childTransform = child.TransformToAncestor(parent);
        //Rect childSize = childTransform.TransformBounds(new Rect(new Point(0, 0), child.RenderSize));
        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 (result.Height == childSize.Height && result.Width == childSize.Width)
        {
            return ControlVisibility.Full;
        }
        if (result.Height == childSize.Height)
        {
            return ControlVisibility.FullHeightPartialWidth;
        }
        if (result.Width == childSize.Width)
        {
            return ControlVisibility.FullWidthPartialHeight;
        }
        return ControlVisibility.Partial;
    }
}

/// <summary>
/// Class implementing helpful extensions to ListBox.
/// </summary>
public static class ListBoxExtensions
{
    /// <summary>
    /// Causes the object to scroll into view centered.
    /// </summary>
    /// <param name="listBox">ListBox instance.</param>
    /// <param name="item">Object to scroll.</param>
    //[SuppressMessage("Microsoft.Design", "CA1011:ConsiderPassingBaseTypesAsParameters",
    //    Justification = "Deliberately targeting ListBox.")]
    public static void ScrollIntoViewTop(this ListBox listBox, object item)
    {
        Debug.Assert(!VirtualizingStackPanel.GetIsVirtualizing(listBox),
            "VirtualizingStackPanel.IsVirtualizing must be disabled for ScrollIntoViewCentered to work.");

        // Get the container for the specified item
        var container = listBox.ItemContainerGenerator.ContainerFromItem(item) as FrameworkElement;
        if (null != container)
        {
            if (ScrollViewer.GetCanContentScroll(listBox))
            {
                // Get the parent IScrollInfo
                var scrollInfo = VisualTreeHelper.GetParent(container) as IScrollInfo;
                if (null != scrollInfo)
                {
                    // Need to know orientation, so parent must be a known type
                    var stackPanel = scrollInfo as StackPanel;
                    var virtualizingStackPanel = scrollInfo as VirtualizingStackPanel;
                    Debug.Assert((null != stackPanel) || (null != virtualizingStackPanel),
                        "ItemsPanel must be a StackPanel or VirtualizingStackPanel for ScrollIntoViewCentered to work.");

                    // Get the container's index
                    var index = listBox.ItemContainerGenerator.IndexFromContainer(container);

                    // Center the item by splitting the extra space
                    if (((null != stackPanel) && (Orientation.Horizontal == stackPanel.Orientation)) ||
                        ((null != virtualizingStackPanel) && (Orientation.Horizontal == virtualizingStackPanel.Orientation)))
                    {
                        //scrollInfo.SetHorizontalOffset(index - Math.Floor(scrollInfo.ViewportWidth / 2));
                        scrollInfo.SetHorizontalOffset(index);
                    }
                    else
                    {
                        //scrollInfo.SetVerticalOffset(index - Math.Floor(scrollInfo.ViewportHeight / 2));
                        scrollInfo.SetVerticalOffset(index);
                    }
                }
            }
            else
            {
                // Get the bounds of the item container
                var rect = new Rect(new Point(), container.RenderSize);

                // Find constraining parent (either the nearest ScrollContentPresenter or the ListBox itself)
                FrameworkElement constrainingParent = container;
                do
                {
                    constrainingParent = VisualTreeHelper.GetParent(constrainingParent) as FrameworkElement;
                } while ((null != constrainingParent) &&
                         (listBox != constrainingParent) &&
                         !(constrainingParent is ScrollContentPresenter));

                if (null != constrainingParent)
                {
                    // Inflate rect to fill the constraining parent
                    rect.Inflate(
                        Math.Max((constrainingParent.ActualWidth - rect.Width) / 2, 0),
                        Math.Max((constrainingParent.ActualHeight - rect.Height) / 2, 0));
                }

                // Bring the (inflated) bounds into view
                container.BringIntoView(rect);
            }
        }
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ScrollIntoView - 项目不应从视图中消失 的相关文章

  • 使用 lambda 表达式注册类型

    我想知道如何在 UnityContainer 中实现这样的功能 container RegisterType
  • WPF DataGrid 验证/绑定模式错误

    我创建了一个非常简单的新项目 仅测试 Microsoft WPF DataGrid 行为 不涉及其他 我只使用标准的 DataGrid
  • GetType() 在 Type 实例上返回什么?

    我在一些调试过程中遇到了这段代码 private bool HasBaseType Type type out Type baseType Type originalType type GetType baseType GetBaseTyp
  • 为什么pow函数比简单运算慢?

    从我的一个朋友那里 我听说 pow 函数比简单地将底数乘以它的指数的等价函数要慢 例如 据他介绍 include
  • JNI 将 Char* 2D 数组传递给 JAVA 代码

    我想从 C 代码通过 JNI 层传递以下指针数组 char result MAXTEST MAXRESPONSE 12 12 8 3 29 70 5 2 42 42 在java代码中我写了以下声明 public static native
  • 函数参数的默认参数是否被视为该参数的初始值设定项?

    假设我有这样的函数声明 static const int R 0 static const int I 0 void f const int r R void g int i I 根据 dcl fct default 1 如果在参数声明中指
  • C# 数据表更新多行

    我如何使用数据表进行多次更新 我找到了这个更新 1 行 http support microsoft com kb 307587 my code public void ExportCSV string SQLSyntax string L
  • unordered_map 中字符串的 C++ 哈希函数

    看起来 C 标准库中没有字符串的哈希函数 这是真的 在任何 c 编译器上使用字符串作为 unordered map 中的键的工作示例是什么 C STL提供模板专业化 http en cppreference com w cpp string
  • File.AppendText 尝试写入错误的位置

    我有一个 C 控制台应用程序 它作为 Windows 任务计划程序中的计划任务运行 此控制台应用程序写入日志文件 该日志文件在调试模式下运行时会创建并写入应用程序文件夹本身内的文件 但是 当它在任务计划程序中运行时 它会抛出一个错误 指出访
  • 告诉 Nancy 将枚举序列化为字符串

    Nancy 默认情况下在生成 JSON 响应时将枚举序列化为整数 我需要将枚举序列化为字符串 有一种方法可以通过创建来自定义 Nancy 的 JSON 序列化JavaScript 原始转换器 https github com NancyFx
  • 为什么可以通过ref参数修改readonly字段?

    考虑 class Foo private readonly string value public Foo Bar ref value private void Bar ref string value value hello world
  • 启动时的 Excel 加载项

    我正在使用 Visual C 创建 Microsoft Excel 的加载项 当我第一次创建解决方案时 它包含一个名为 ThisAddIn Startup 的函数 我在这个函数中添加了以下代码 private void ThisAddIn
  • 为什么我的单选按钮不起作用?

    我正在 Visual C 2005 中开发 MFC 对话框应用程序 我的单选按钮是 m Small m Medium 和 m Large 它们都没有在我的 m Summary 编辑框中显示应有的内容 可能出什么问题了 这是我的代码 Pizz
  • 高效列出目录中的所有子目录

    请参阅迄今为止所采取的建议的编辑 我正在尝试使用 WinAPI 和 C 列出给定目录中的所有目录 文件夹 现在我的算法又慢又低效 使用 FindFirstFileEx 打开我正在搜索的文件夹 然后我查看目录中的每个文件 使用 FindNex
  • 在屏幕上获取字符

    我浏览了 NCurses 函数列表 似乎找不到返回已打印在屏幕上的字符的函数 每个字符单元格中存储的字符是否有可访问的值 如果没有的话Windows终端有类似的功能吗 我想用它来替换屏幕上某个值的所有字符 例如 所有a s 具有不同的特征
  • String.Empty 与 "" [重复]

    这个问题在这里已经有答案了 可能的重复 String Empty 和 有什么区别 https stackoverflow com questions 151472 what is the difference between string
  • 这个可变参数模板示例有什么问题?

    基类是 include
  • 堆栈是向上增长还是向下增长?

    我在 C 中有这段代码 int q 10 int s 5 int a 3 printf Address of a d n int a printf Address of a 1 d n int a 1 printf Address of a
  • 如何在richtextbox中使用多颜色[重复]

    这个问题在这里已经有答案了 我使用 C windows 窗体 并且有 richtextbox 我想将一些文本设置为红色 一些设置为绿色 一些设置为黑色 怎么办呢 附图片 System Windows Forms RichTextBox有一个
  • Objective-C / C 给出枚举默认值

    我在某处读到过关于给枚举默认值的内容 如下所示 typedef enum MarketNavigationTypeNone 0 MarketNavigationTypeHeirachy 1 MarketNavigationTypeMarke

随机推荐