Обновление CTreeCtrl
От: Аноним  
Дата: 10.07.12 12:44
Оценка:
Всем доброго времени суток!

При разработке MFC приложения возник следующий вопрос: есть элемент управления дерево CTreeCtrl:

m_ctrlTree CTreeCtrl;

и установлен таймер:

SetTimer(ID_TIMER, 1000, NULL);


благодаря которому через определенные моменты времени вызывается метод InsertItem для вставки новых элементов в дерево:

::OnTimer(UINT uTime)
{
    m_ctrlTree.InsertItem(L"New element", TVI_ROOT);
}

С течением времени, когда элементов становится много, появляется вертикальный scroll bar с ползунком, который всегда находится внизу полосы прокрутки. Даже если мне с помощью мыши переместить его наверх, чтобы посмотреть ранее введенные элементы, после очередного InsertItem он все равно опускается вниз. Каким образом я могу поменять такое поведение? Мне необходимо, чтобы ползунок сохранял свою предыдущую позицию (если я переместился наверх, чтобы посмотреть на самый первый элемент, то я там и должен оставаться, вне зависимости от того, добавились ли новые элементы или нет). Пробовал сделать через GetScrollPos и SetScrollPos, не помогает.

Спасибо.
Re: Обновление CTreeCtrl
От: MasterZiv СССР  
Дата: 10.07.12 14:33
Оценка: -1
On 07/10/2012 04:44 PM, Аноним 226 wrote:
> m_ctrlTree.InsertItem(L"New element", TVI_ROOT);

HTREEITEM hSomeItemToBeFocused = ...;

m_ctrlTree.SetItemState( hSomeItemToBeFocused, TVIS_SELECTED, TVIS_SELECTED);
Posted via RSDN NNTP Server 2.1 beta
Re[2]: Обновление CTreeCtrl
От: Аноним  
Дата: 11.07.12 07:35
Оценка:
Здравствуйте, MasterZiv, Вы писали:

MZ>On 07/10/2012 04:44 PM, Аноним 226 wrote:

>> m_ctrlTree.InsertItem(L"New element", TVI_ROOT);

MZ>HTREEITEM hSomeItemToBeFocused = ...;


MZ>m_ctrlTree.SetItemState( hSomeItemToBeFocused, TVIS_SELECTED, TVIS_SELECTED);


Насколько я понял, переменной hSomeItemToBeFocused необходимо присвоить значение, возвращаемое InsertItem, после чего вызывается SetItemState. Если я правильно понял, то у меня это к сожалению не работает.
Если можно, объясните, пожалуйста, цель использования SetItemState со значениями TVIS_SELECTED?

Чтобы было более понятно, покажу свою проблему подробнее:

Итак в Tree Control постепенно появляются различные элементы (тут добавляются ip адреса вместо "New element"):
Начало

По мере увеличения их числа (после каждого InsertItem)всегда отображаются последние введенные элементы:
Заполнение

Если я хочу посмотреть самый первый элемент — ip 10.0.23.126, то поднимаюсь наверх:
В начало

И тут же при следующем InsertItem (он вызывается каждую секунду) мне опять отображается часть дерева с только что появившемся ip:
Конец дерева

Таким образом, верхнюю часть дерева я практически не успеваю просмотреть...

Надеюсь, объяснил подробно.

Спасибо.
Re: Обновление CTreeCtrl
От: Hawk Россия  
Дата: 11.07.12 16:07
Оценка:
Здравствуйте, Аноним, Вы писали:

> Пробовал сделать через GetScrollPos и SetScrollPos, не помогает.


Т.е., в месте, где добавляется item, написано что-то вроде:

int nOldPos = m_ctrlTree.GetScrollPos( SB_VERT );

m_ctrlTree.InsertItem( L"New element", TVI_ROOT );

m_ctrlTree.SetScrollPos( SB_VERT, nOldPos, TRUE );

И это не работает?

Не видя кода, что-то подсказать сложно. Но есть подозрение, что вызов SetScrollPos() кто-то перебивает. Попробуй, ради эксперимента, вызов SetScrollPos() поставить в обработчик PostMessage(). Т.е. сделать что-то вроде:
...
UINT MYAPP_RESTORE_SCROLLBAR_POS_NOTIFY = ::RegisterWindowMessage( _T( "MYAPP_RESTORE_SCROLLBAR_POS_NOTIFY" ) );
...
void CMyAppDialog::InsertTreeItem()
{
...
    int nOldPos = m_ctrlTree.GetScrollPos( SB_VERT );

    m_ctrlTree.InsertItem( L"New element", TVI_ROOT );

    PostMessage( MYAPP_RESTORE_SCROLLBAR_POS_NOTIFY, 0, ( LPARAM )nOldPos );
...
}

...
ON_REGISTERED_MESSAGE( MYAPP_RESTORE_SCROLLBAR_POS_NOTIFY, OnRestoreScrollBarPosNotify )
...

void CMyAppDialog::OnRestoreScrollBarPosNotify( WPARAM, LPARAM lp )
{
    m_ctrlTree.SetScrollPos( SB_VERT, lp, TRUE );
}

АФАИК, обработчики post-сообщений вызываются в последнюю очередь, после всех обработчиков send-сообщений (подтверждение сейчас сходу не найду, но практика это показывает). Таким образом, есть надежда, что SetScrollPos() никто не перебьет и он будет вызван самым последним, когда дерево уже окончательно сформировано и все обработчики сделали свое дело.
Re[3]: Обновление CTreeCtrl
От: MasterZiv СССР  
Дата: 11.07.12 16:15
Оценка:
> Насколько я понял, переменной hSomeItemToBeFocused необходимо присвоить
> значение, возвращаемое InsertItem, после чего вызывается SetItemState. Если я
> правильно понял, то у меня это к сожалению не работает.

Нет, неправильно понял.
Тебе надо добавлять итемы вниз, а устанавливать текущей какой-то из верхниих.
Например, первый дочерний к корневому.
Хотя я может быть ничего не понял.

> Если можно, объясните, пожалуйста, цель использования SetItemState со значениями

> TVIS_SELECTED?
>

Ну выбрать другой итем.

> И тут же при следующем InsertItem (он вызывается каждую секунду) мне опять

> отображается часть дерева с только что появившемся ip:
> Конец дерева <http://files.rsdn.ru/97456/4.jpg&gt;

До инсёрта запоминай текущий выбранный итем. НА который смотрят.
Вставляй, и делай после вставки выбранным его.

> Таким образом, верхнюю часть дерева я практически не успеваю просмотреть...


Не очень ещё понятно как это так ты и вставляешь одновременно итемы,
и пытаешься что-то смотреть. По таймеру что ли это происходит ?
Posted via RSDN NNTP Server 2.1 beta
Re[2]: Обновление CTreeCtrl
От: MasterZiv СССР  
Дата: 11.07.12 16:21
Оценка:
е видя кода, что-то подсказать сложно. Но есть подозрение, что вызов
> SetScrollPos() кто-то перебивает.

Да там просто новая вставленая итема наверняка имеет состояние SELECTED , и оно
подматывает само до неё. Можно ещё не создавать с состоянием SELECTED.

> ...

> UINT MYAPP_RESTORE_SCROLLBAR_POS_NOTIFY = ::RegisterWindowMessage( _T("MYAPP_RESTORE_SCROLLBAR_POS_NOTIFY" ) );

Зачем применяешь RegisterWindowMessage ?
RegisterWindowMessage нужно только когда регистрируемое сообщение пересылается
между разными приложениями. Для посылки сообщений внутри приложения это не нужно.

И даже вредно, потому что ON_REGISTERED_MESSAGE немного по-другому работает в
MFC, чем простое сообщение.

> АФАИК, обработчики post-сообщений вызываются в последнюю очередь, после всех

> обработчиков send-сообщений (подтверждение сейчас сходу не найду, но практика
> это показывает).

Они не в последнюю очередь. Они просто в очередь. POST. А SEND обрабатывается
минуя очередь сообщений непосредственно вызовом функции окна, без
постановки сообщения в очередь.

Таким образом, есть надежда, что SetScrollPos() никто не
> перебьет и он будет вызван самым последним, когда дерево уже окончательно
> сформировано и все обработчики сделали свое дело.

Не, не выйдет. SetScrollPos() -- чисто синхронный вызов.
Posted via RSDN NNTP Server 2.1 beta
Re[2]: Обновление CTreeCtrl
От: MasterZiv СССР  
Дата: 11.07.12 16:27
Оценка:
> Не видя кода, что-то подсказать сложно. Но есть подозрение, что вызов
> SetScrollPos() кто-то перебивает. Попробуй, ради эксперимента, вызов
> SetScrollPos() поставить в обработчик PostMessage(). Т.е. сделать что-то вроде:
>
> ...
> UINT MYAPP_RESTORE_SCROLLBAR_POS_NOTIFY = ::RegisterWindowMessage( _T("MYAPP_RESTORE_SCROLLBAR_POS_NOTIFY" ) );
> ...
> void CMyAppDialog::InsertTreeItem()
> {
> ...
> int nOldPos = m_ctrlTree.GetScrollPos( SB_VERT );
>
> m_ctrlTree.InsertItem( L"New element", TVI_ROOT );
>
> PostMessage( MYAPP_RESTORE_SCROLLBAR_POS_NOTIFY, 0, ( LPARAM )nOldPos );
> ...
> }
>
> ...
> ON_REGISTERED_MESSAGE( MYAPP_RESTORE_SCROLLBAR_POS_NOTIFY, OnRestoreScrollBarPosNotify )
> ...
>
> void CMyAppDialog::OnRestoreScrollBarPosNotify( WPARAM, LPARAM lp )
> {
> m_ctrlTree.SetScrollPos( SB_VERT, lp, TRUE );
> }

А, сорри, выйдет, но не факт, что будет работать. Это отложит вызов SetScrollPos
на потом. Но не факт, что ему поможет.
Posted via RSDN NNTP Server 2.1 beta
Re[4]: Обновление CTreeCtrl
От: Аноним  
Дата: 12.07.12 07:42
Оценка:
Здравствуйте, MasterZiv, Вы писали:

MZ>Не очень ещё понятно как это так ты и вставляешь одновременно итемы,

MZ>и пытаешься что-то смотреть. По таймеру что ли это происходит ?

Сейчас покажу. Вот мой код (в упрощенном варианте):

#define ID_TIMER_1 1

HTREEITEM hRoot, hFchild;
...
BEGIN_MESSAGE_MAP(CForexampleDlg, CDialogEx)
    ...
    ON_WM_TIMER() // Для таймера
    ...
END_MESSAGE_MAP()
...
BOOL CForexampleDlg::OnInitDialog()
{
        ...
        // TODO: Add extra initialization here
    hRoot = m_ctrlTree.InsertItem(L"First element", TVI_ROOT);
    Test = false;
        ...
}
...
void CForexampleDlg::OnTimer( UINT uTime) // Обработчик таймера
{
    hFchild = m_ctrlTree.InsertItem(L"New element", hRoot);
    m_ctrlTree.InsertItem(L"New Fchild", hFchild);
    m_ctrlTree.Expand(hRoot, TVE_EXPAND);
    m_ctrlTree.Expand(hFchild, TVE_EXPAND); // Если её закомментировать, все работает
}
void CForexampleDlg::OnBnClickedStart()
{
    if (!Test)                 
    {
        Test=true;              
        SetTimer(ID_TIMER_1,1000,NULL);  
    }
    else                       
    {
        Test=false;               
        KillTimer(ID_TIMER_1);       
    }
}


При этом — если не разворачивать ветку с hFchild, то все прекрасно работает.

Для наглядности сделал небольшую анимацию: Gif
Видно, что после 6-го добавления я поднимаюсь наверх к корневому элементу, но 7 InsertItem опять опускает ползунок вниз.
Re[5]: Обновление CTreeCtrl
От: MasterZiv СССР  
Дата: 12.07.12 08:08
Оценка:
On 07/12/2012 11:42 AM, Аноним 226 wrote:

> MZ>Не очень ещё понятно как это так ты и вставляешь одновременно итемы,

> MZ>и пытаешься что-то смотреть. По таймеру что ли это происходит ?
>
> Сейчас покажу. Вот мой код (в упрощенном варианте):

> При этом — если не разворачивать ветку с hFchild, то все прекрасно работает.


А нафига ж её разворачивать, если она только что вставлена и в ней ничего нет ?

>

> Для наглядности сделал небольшую анимацию: Gif <http://files.rsdn.ru/97456/1.gif&gt;

Круто. Все бы так repro делали...

> Видно, что после 6-го добавления я поднимаюсь наверх к корневому элементу, но 7

> InsertItem опять опускает ползунок вниз.

Ну я понял, вставляется, разворачивается и туда прокручивается.
Но всё равно я так и не понял этого:

> MZ>Не очень ещё понятно как это так ты и вставляешь одновременно итемы,

> MZ>и пытаешься что-то смотреть.

НАФИГА и вставлять, и пытаться смотреть одновременно ? Вставь ВСЕ, затем
смотри. ЭТо ж тебе не мультики, это TreeCtrl.
Posted via RSDN NNTP Server 2.1 beta
Re[6]: Обновление CTreeCtrl
От: Den_Kos  
Дата: 12.07.12 09:50
Оценка:
Здравствуйте, MasterZiv, Вы писали:

MZ>А нафига ж её разворачивать, если она только что вставлена и в ней ничего нет ?


Как же ничего нет? В ней элемент New hFchild. Если бы я не разворачивал, было бы следующее:Don't expand

MZ>Круто. Все бы так repro делали...


Спасибо

MZ>НАФИГА и вставлять, и пытаться смотреть одновременно ? Вставь ВСЕ, затем

MZ>смотри. ЭТо ж тебе не мультики, это TreeCtrl.

В этом то и проблема. По прежним скринам было видно, что вставляются ip адреса, и дерево постоянно пополняется новыми ip, просто в режиме реального времени происходит анализ сети. Дождаться, пока все закончится — не могу, это можно сказать бесконечный процесс... Только лишь из-за этого и возник вопрос — как это можно исправить?
Re[7]: Обновление CTreeCtrl
От: Аноним  
Дата: 13.07.12 10:20
Оценка:
Ну так получается никаких вариантов нет?
Re[8]: Обновление CTreeCtrl
От: SuperSmile  
Дата: 13.07.12 17:24
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Ну так получается никаких вариантов нет?


Если через скролл не получается, можно попробовать запоминать преред Expand GetFirstVisibleItem и возвращать через EnsureVisible, обернув в SetRedraw(), ибо будет промигивать.
Re[8]: Обновление CTreeCtrl
От: MasterZiv СССР  
Дата: 17.07.12 08:41
Оценка:
On 07/13/2012 02:20 PM, Аноним 226 wrote:

> Ну так получается никаких вариантов нет?


Не знаю.
Вообще, плохая идея. Плохой режим работы.

А так ещё можно вместо TreeView использовать ListView (SysListView32 который).
в режиме дерева (там теперь вроде бы есть, а если и нет -- можно самому сделать,
делается).
Вот его можно в виде виртуального ListView делать и там режим такой работы
легде изобразить. По крайней мере я так делал (обновляемые в реальном времени
таблицы). Ну и вообще он помощнее Tree немного, там больше свобод.
Posted via RSDN NNTP Server 2.1 beta
Re[9]: Обновление CTreeCtrl
От: Аноним  
Дата: 25.07.12 07:43
Оценка:
Здравствуйте, MasterZiv, Вы писали:

MZ>On 07/13/2012 02:20 PM, Аноним 226 wrote:


>> Ну так получается никаких вариантов нет?


MZ>Не знаю.

MZ>Вообще, плохая идея. Плохой режим работы.

Ну какой есть, ничего не поделаешь...

Ладно, буду пробовать. Спасибо за ответы.
Re[10]: Обновление CTreeCtrl
От: VladFein США  
Дата: 31.07.12 14:53
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Ладно, буду пробовать. Спасибо за ответы.


Похоже, что Expand() имеет побочный эфект: перемещает этот элемент в видимую область.
К счастью, вызов этой функции можно избежать, указав EXPANDED в вызове InsertItem().
Вместо
hFchild = m_ctrlTree.InsertItem(L"New element", hRoot);

используйте форму со всеми параметрами
hFchild = m_ctrlTree.InsertItem(TVIF_TEXT | TVIF_STATE, s, 0, 0, TVIS_EXPANDED, TVIS_EXPANDED, 0, TVI_ROOT, TVI_LAST);
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.