поддерживать позицию прокрутки дерева

<p>как я могу сохранить положение прокрутки древовидного элемента управления в приложении .net? например, у меня есть древовидный элемент управления, и я прохожу через него процесс добавления различных узлов, прикрепляя их к нижней части. во время этого процесса я могу просматривать дерево и просматривать разные узлы. проблема в том, что когда процесс завершается, древовидная структура прокручивается до самого дна.</p><p>похоже, что вызов treenode.expand () сбивает меня с толку. когда родительский узел раскрывается, он получает фокус.</p><p>это можно обойти? если я смотрю на конкретный узел во время работы процесса, я не хочу, чтобы он прыгал вокруг меня, когда процесс завершится.</p>

<p>я думаю, я понял это:</p><ol> <li>получить узел в верхней части дерева.</li> <li>разверните родительский узел.</li> <li>сделайте узел, который был ранее наверху видимым.</li> </ol><blockquote> <pre><code>if treenodeparent.isexpanded = false then dim currentnode as treenode = treeviewhosts.getnodeat(0, 0) treenodeparent.expand() currentnode.ensurevisible() end if </code></pre></blockquote><p>это лучший способ сделать это?</p>

<p>я не vb парень, но в c # я делаю это так:</p><p><strong>некоторые родные функции win32:</strong></p><pre><code>[dllimport("user32.dll", charset = charset.unicode)] public static extern int getscrollpos(intptr hwnd, int nbar); [dllimport("user32.dll", charset = charset.unicode)] public static extern int setscrollpos(intptr hwnd, int nbar, int npos, bool bredraw); private const int sb\_horz = 0x0; private const int sb\_vert = 0x1; </code></pre><p><strong>метод, который возвращает точку для текущей позиции прокрутки:</strong></p><pre><code>private point gettreeviewscrollpos(treeview treeview) { return new point( getscrollpos((int)treeview.handle, sb\_horz), getscrollpos((int)treeview.handle, sb\_vert)); } </code></pre><p><strong>метод установки положения прокрутки:</strong></p><pre><code>private void settreeviewscrollpos(treeview treeview, point scrollposition) { setscrollpos((intptr)treeview.handle, sb\_horz, scrollposition.x, true); setscrollpos((intptr)treeview.handle, sb\_vert, scrollposition.y, true); } </code></pre><p><strong>затем, когда вы обновите свое дерево, сделайте следующее:</strong></p><pre><code>beginupdate(); point scrollpos = gettreeviewscrollpos(treemain); // write your update code here settreeviewscrollpos(treemain, scrollpos); endupdate(); </code></pre>

<p>другой способ сохранить положение прокрутки без внешних функций - использовать свойство topnode дерева ...</p><p>topnode получает или устанавливает первый полностью видимый узел дерева в элементе управления представлением дерева.</p><p>если вы просто хотите расширить узел и сохранить его верхний узел:</p><pre><code>treenode topnode = m\_tree.topnode; treenode.expand(); m\_tree.topnode = topnode; </code></pre><p>в противном случае, если вы перестраиваете дерево (например, обновляете файловую структуру), вы можете использовать следующий метод ...</p><p>перед очисткой дерева сохраните полный путь к верхнему узлу:</p><pre><code>string topnodepath = null; treenode topnode = null; if (m\_tree.topnode != null) { topnodepath = m\_tree.topnode.fullpath; } m\_tree.clear(); </code></pre><p>после добавления узлов проверьте его fullpath по сравнению с topnodepath:</p><pre><code>nodes.add(node) if ((topnodepath != null) && (node.fullpath == topnodepath)) { topnode = node; } </code></pre><p>после добавления всех узлов обновите свойство дерева topnode:</p><pre><code>if (topnode != null) { m\_tree.topnode = topnode; } </code></pre><p>я использую подобную технику для выбранных и расширенных узлов. selectednode работает почти так же, как topnode, показанный выше. для расширенных узлов я использую рекурсивную функцию, чтобы пройти через дочерние узлы и добавить путь расширенных узлов в список. затем расширяет их на основе их пути после добавления детей.</p><p>конечно, если у вас много узлов с одинаковыми именами, это может не сработать :-)</p>

<p>я обнаружил, что лучше обернуть <code>settreeviewscrollposition(point)</code> с <code>beginupdate</code> и <code>endupdate</code>...</p><pre><code>private void treeviewxml1\_scroll(object sender, scrolleventargs e) { point point = treeviewxml1.gettreeviewscrollposition(); treeviewxml2.beginupdate(); treeviewxml2.settreeviewscrollposition(point); treeviewxml2.endupdate(); } private void treeviewxml2\_scroll(object sender, scrolleventargs e) { point point = treeviewxml2.gettreeviewscrollposition(); treeviewxml1.beginupdate(); treeviewxml1.settreeviewscrollposition(point); treeviewxml1.endupdate(); } </code></pre>

<p>у меня также была та же проблема, когда обновлялась сама прокрутка, но содержимое древовидной структуры не прокручивалось. это легко исправить, добавив <code>beginupdate()</code> и <code>endupdate()</code> вокруг <code>setscrollpos()</code>.</p><pre><code>this.hierarchytreeview.beginupdate(); setscrollpos(this.hierarchytreeview.handle, sb\_vert, 5, true); this.hierarchytreeview.endupdate(); </code></pre>

<p>это пересмотр прекрасного ответа от stefan koell в виде treeviewextension: (полное решение)</p><pre><code>using system; using system.runtime.interopservices; using system.windows; using system.windows.controls; using system.windows.interop; using system.windows.media; public static class treeviewextension { #region constants private const int scrollbarhorizontal = 0x0; private const int scrollbarvertical = 0x1; #endregion #region public methods and operators [dllimport("user32.dll", charset = system.runtime.interopservices.charset.auto)] public static extern int getscrollpos(int hwnd, int nbar); public static point scrollposition(this treeview treeview) { return new point( getscrollpos((int)treeview.handle(), scrollbarhorizontal), getscrollpos((int)treeview.handle(), scrollbarvertical)); } public static void scrollto(this treeview treeview, point scrollposition) { setscrollpos(treeview.handle(), scrollbarhorizontal, (int)scrollposition.x, true); setscrollpos(treeview.handle(), scrollbarvertical, (int)scrollposition.y, true); } [dllimport("user32.dll")] public static extern int setscrollpos(intptr hwnd, int nbar, int npos, bool bredraw); #endregion #region methods private static intptr handle(this visual treeview) { var handle = intptr.zero; var hwndsource = presentationsource.fromvisual(treeview) as hwndsource; if (hwndsource != null) { handle = hwndsource.handle; } return handle; } #endregion } </code></pre><p>возможно, это упростит вашу работу ;-)</p>

<p>это отлично работает. сохраните topnode и восстановите после:</p><pre><code>this.treeview.beginupdate(); treenode topnode = this.treeview.topnode; // your code this.treeview.sort(); this.treeview.selectednode = auxnode; this.treeview.topnode = topnode; this.treeview.endupdate(); </code></pre>

<pre><code>mytreeview.nodes[0].ensurevisible(); </code></pre>