CTreeCtrl
Теперь, если пользователь делает двойной клик на элементе, который находится в верхней половине окна CTreeCtrl, то происходит множество сложных действий, создаются новые документы, запускаются дополнительные потоки итп. При этом меняется само содержимое дерева. Для этого работает такой код:
int iVScrollPos = GetScrollPos(SB_VERT);
int iHScrollPos = GetScrollPos(SB_HORZ);
//Изменение содержимого дерева
SetScrollPos(SB_VERT, iVScrollPos, FALSE);
SetScrollPos(SB_HORZ, iHScrollPos, FALSE);
SetRedraw(TRUE);
После всего совершенного действа почему то вертикальный скролбар улетает в самый низ дерева. При этом горизонтальный остается на месте. Это воспроизводится только в том случае, если пользователь кликает на элемент в верхней половине окна CTreeCtrl.
Может кто знает, в чем может быть проблема? Почему вертикальный скролбар не остается на месте? Приведенный код - единственное место, где программно устанавливается позиция скролбара - проверял - устанавливаются верные значения.
Ф-ия EnsureVisible() не лучше работает?
Проблема вся в том, что дерево может перестроиться очень сильно - в результате чего код перестройки работает следующим образом - дерево полностью уничтожается, затем строится новое с нуля, затем восстанавливается позиция скролбара.
Если же пользоваться EnsureVisible при такой логике, то указанный в EnsureVisible элемент может перескочить на самый верх, что не очень будет приятно выглядеть.
Причем, более того, конструкция с запоминанием\восстановлением позиции скролбара - работает в 90% случаев правильно. Описанный мною случай проявляется в тех 10%, когда идут громоздкие вычисления, запускаются другие потоки - в общем при определенных обстоятельствах.
То есть, можно сделать вывод, что какой-то код что то самопроизвольно портит... Вот я и хочу услышать идею, в какую сторону мне копать, чтобы отловить эту ошибку.
То есть, можно сделать вывод, что какой-то код что то самопроизвольно портит... Вот я и хочу услышать идею, в какую сторону мне копать, чтобы отловить эту ошибку.
Это не код портит, а TreeCtrl не знает настоящий размер ScrollRange.
В нижней части код нормально работает, только потому что размер скроллинга расчитан.
Это не код портит, а TreeCtrl не знает настоящий размер ScrollRange.
В нижней части код нормально работает, только потому что размер скроллинга расчитан.
Не совсем понятна идея. Как я уже написал, вначале мы запоминаем позицию скроллинга, например 50 пикселей. Затем полностью убиваем дерево - оно пустое - позиция скролинга я так понимаю тоже обнуляется. Затем полностью его пересоздаем (текущая позиция скроллинга равна 0), затем мы восстанавливаем старую позицию. При этом не понятно, почему дерево не знает свой ScrollRange?
Так же я прикрепил два варианта начальной позиции скролбара. В первом варианте, элемент на который кликаем дважды (выделен - SV_3Q) находится выше середины окна дерева - в этом варианте при двойном клике скролбар улетает в самый низ (там этот элемент вообще не виден, хотя селекция остается на нем). Во втором варианте этот же элемент находится чуть ниже середины окна дерева. И при прочих равных условиях двойной клик оставляет скролбар на месте...
Не совсем понятна идея.
Это была не идея. Где-то 5 лет тому назад у меня была подобная проблема с Builder-овским TreeCtrl.
И если хорошу помню, причина была в том, что TreeView не знал настоящий размер скроллинга(точнее высоту дерева).
Но можно проверить.
1 вариант. Сперва пойти в самый вниз, потом вверх, чтоб получился рис1 и так щелкнуть на SV_3Q.
2 вариант. После создания нового дерева сперва пойти вниз программно на последний элемент дерева и после этого установить Scroll pos.
Если все еще плохо будет позиционироваться, значит не то...
1 вариант. Сперва пойти в самый вниз, потом вверх, чтоб получился рис1 и так щелкнуть на SV_3Q.
2 вариант. После создания нового дерева сперва пойти вниз программно на последний элемент дерева и после этого установить Scroll pos.
Не прокатило ни то ни другое. Я провел "замеры".
1. Вариант, когда скролбар улетает вниз.
а) Запомненное положение скролбара - 19
б) Перед восстановлением положения скролбара функция GetScrollRange(SB_VERT, &iMin, &iMax); возвращает iMin = 0, iMax = 64
2. Вариант, когда все ок.
а) Положение - 10
б) iMin = 0, iMax = 64
Так что грешить на то, что не вычисляются размеры Scroll Range не нужно.
Не прокатило ни то ни другое. Я провел "замеры".
1. Вариант, когда скролбар улетает вниз.
а) Запомненное положение скролбара - 19
б) Перед восстановлением положения скролбара функция GetScrollRange(SB_VERT, &iMin, &iMax); возвращает iMin = 0, iMax = 64
2. Вариант, когда все ок.
а) Положение - 10
б) iMin = 0, iMax = 64
Так что грешить на то, что не вычисляются размеры Scroll Range не нужно.
Значит (возможно) не то. Теперь нет времени проверить.
Но ты после создания нового дерева перед SetSrollPos дал команду EnsureVisible(самый_нижний_элемент) ?
Значит (возможно) не то. Теперь нет времени проверить.
Но ты после создания нового дерева перед SetSrollPos дал команду EnsureVisible(самый_нижний_элемент) ?
Да, написал цикл вроде следующего:
HTREEITEM hNextItem = TVI_ROOT;
while (hNextItem)
{
while (hNextItem)
{
//Спускаемся вниз до самого последнего брата
hNextItem = GetNextItem(hLastItem, TVGN_NEXT);
if (hNextItem)
{
hLastItem = hNextItem;
}
}
//Как только братья кончились
//Спускаемся к первому ребенку последнего брата
hNextItem = GetNextItem(hLastItem, TVGN_CHILD);
if (hNextItem)
{
hLastItem = hNextItem;
}
//И повторяем процедуру
}
if (hLastItem)
{
EnsureVisible(hLastItem);
}
SetScrollPos(...)
SetRedraw(TRUE);
Invalidate(FALSE);
При этом проверил, что EnsureVisible(hLastItem); происходит на самом деле для самого последнего элемента дерева. Замечу, что от изменения позиции строк
SetRedraw(TRUE);
Invalidate(FALSE);
ничего не меняется.
ничего не меняется.
:(
странно. Если первое и второе дерево один к одному совпадают...