Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Visual C++/MFC/WTL > Выделение нескольких элементов CTreeCtrl


Автор: EvgenyTS 26.2.2009, 20:39
Может подскажете как выделить несколько элементов в CTreeCtrl для последующего их использования?

Автор: Earnest 27.2.2009, 08:42
Tree View, в отличие от List View, не поддерживает выделение нескольких элементов. Т.е. поставить выделение-то можно (программно) - установить состояние TVS_SELECTED, но вот поддержка одновременного выделения и перебор - это задача программиста. Я делаю так: 1) завожу массив выделенных в текущий момент элементов и сама им управляю 2) ловлю TVN_SELCHANGED, где проверяю, нажат ли SHIFT / CTRL (чтобы множественное выделение делалось стандартным способом) и в зависимости от этого обновляю свою коллекцию выделенных элементо (при этом все старое выделение снимаю, а новое ставлю). 

Автор: EvgenyTS 27.2.2009, 09:44
если можешь кинь маленький примерчик. Мне нужно выделить только первые родительские узлы. Спасибо

Автор: Earnest 27.2.2009, 15:23
Код

// это в карту обработчиков
ON_NOTIFY_REFLECT (TVN_SELCHANGED,  OnSelChanged)

// ловим изменение выделения (поддержка множественного выделения)
void CLayerCtrl::OnSelChanged (NMHDR* pNMHDR, LRESULT* pResult) 
{
    *pResult=0;
    NMTREEVIEW* pnmtv = reinterpret_cast<NMTREEVIEW*>(pNMHDR);
    HTREEITEM hItem = pnmtv->itemNew.hItem;
    
    if (GetKeyState(VK_CONTROL) < 0)
      SelectItem (m_hStartItem = hItem, Toggle);
    else if (m_hStartItem && GetKeyState(VK_SHIFT) < 0)    // выделяем последовательность элементов
      SelectItems (m_hStartItem, hItem);
   else                                                                    // выделяем только текущий элемент
      SelectItems (m_hStartItem=hItem, hItem);
}

void CLayerCtrl::SelectItem (HTREEITEM hItem, int nSelect)
{
   if (nSelect == Toggle) 
      nSelect = ItemSelected (hItem) ? Unselect : Select;  

   if (nSelect == Select) 
   {
      Insert (m_Selection, hItem); // добавляем в m_Selection
    SetItemState (hItem, TVIS_SELECTED, TVIS_SELECTED);
        if (m_Selection.size() == 1) 
            CTreeCtrl::SelectItem (hItem); 
   }
   else
   {
      Erase (m_Selection, hItem); // удаляем из m_Selection
    SetItemState (hItem, 0, TVIS_SELECTED);
   }
}

// выделить последовательность между заданными элементами включительно
void CLayerCtrl::SelectItems (HTREEITEM hItemFrom, HTREEITEM hItemTo)
{
   // снимаем старое выделение
   // m_Selection - это отсортированный вектор HTREEITEM
   for (CSelection::iterator it = m_Selection.begin(); it != m_Selection.end(); ++it)
    SetItemState (*it, 0, TVIS_SELECTED);
   m_Selection.clear();

   // новое выделение: определяем, кто первый
   if (!hItemFrom) hItemFrom = hItemTo;
   if (hItemFrom != hItemTo)
   {
      RECT rFrom,rTo;
      if (!GetItemRect (hItemFrom, &rFrom, false))
         hItemFrom = hItemTo;
      else 
      {
         // hItemTo всегда должен быть видимым: это последний клик! 
         VERIFY (GetItemRect (hItemTo, &rTo, false));
         if (rFrom.top > rTo.top) std::swap (hItemFrom, hItemTo);
      }
   }
   
   // перебираем в порядке видимости и выделяем
   for (HTREEITEM hItem = hItemFrom; hItem!=0; hItem = GetNextVisibleItem (hItem))
   {
      SelectItem (hItem, Select);
      if (hItem == hItemTo) break;
   }
}

bool CLayerCtrl::ItemSelected(HTREEITEM hItem) const
{
   return std::binary_search (m_Selection.begin(), m_Selection.end(), hItem);
}


Автор: EvgenyTS 27.2.2009, 16:02
Спасибо. Разбираюсь...
вот элемент m_hStartItem. Что за он. Не могу найти объявление. Я подразумеваю что HTREEITEM. Но что он принимает?

Автор: Earnest 27.2.2009, 17:48
Это первый выделенный элемент. Запоминаем его,  когда первый раз нажимаем с SHIFT. По-моему, из кода это ясно.

Автор: EvgenyTS 27.2.2009, 18:19
Ясно.
Вот создаю векстор HTREEITEM
vector<HTREEITEM>m_Selection;
при компил. пишет
error C2027: use of undefined type '_TREEITEM' Как правильно использовать?

Автор: Earnest 28.2.2009, 11:31
Может, написал неправильно? И вообще, что за детский сад? smile 

Автор: Tor887 14.11.2011, 12:11
Цитата(Earnest @ 27.2.2009,  15:23)
Код

// это в карту обработчиков
ON_NOTIFY_REFLECT (TVN_SELCHANGED,  OnSelChanged)

// ловим изменение выделения (поддержка множественного выделения)
void CLayerCtrl::OnSelChanged (NMHDR* pNMHDR, LRESULT* pResult) 
{
    *pResult=0;
    NMTREEVIEW* pnmtv = reinterpret_cast<NMTREEVIEW*>(pNMHDR);
    HTREEITEM hItem = pnmtv->itemNew.hItem;
    
    if (GetKeyState(VK_CONTROL) < 0)
      SelectItem (m_hStartItem = hItem, Toggle);
    else if (m_hStartItem && GetKeyState(VK_SHIFT) < 0)    // выделяем последовательность элементов
      SelectItems (m_hStartItem, hItem);
   else                                                                    // выделяем только текущий элемент
      SelectItems (m_hStartItem=hItem, hItem);
}

void CLayerCtrl::SelectItem (HTREEITEM hItem, int nSelect)
{
   if (nSelect == Toggle) 
      nSelect = ItemSelected (hItem) ? Unselect : Select;  

   if (nSelect == Select) 
   {
      Insert (m_Selection, hItem); // добавляем в m_Selection
    SetItemState (hItem, TVIS_SELECTED, TVIS_SELECTED);
        if (m_Selection.size() == 1) 
            CTreeCtrl::SelectItem (hItem); 
   }
   else
   {
      Erase (m_Selection, hItem); // удаляем из m_Selection
    SetItemState (hItem, 0, TVIS_SELECTED);
   }
}

// выделить последовательность между заданными элементами включительно
void CLayerCtrl::SelectItems (HTREEITEM hItemFrom, HTREEITEM hItemTo)
{
   // снимаем старое выделение
   // m_Selection - это отсортированный вектор HTREEITEM
   for (CSelection::iterator it = m_Selection.begin(); it != m_Selection.end(); ++it)
    SetItemState (*it, 0, TVIS_SELECTED);
   m_Selection.clear();

   // новое выделение: определяем, кто первый
   if (!hItemFrom) hItemFrom = hItemTo;
   if (hItemFrom != hItemTo)
   {
      RECT rFrom,rTo;
      if (!GetItemRect (hItemFrom, &rFrom, false))
         hItemFrom = hItemTo;
      else 
      {
         // hItemTo всегда должен быть видимым: это последний клик! 
         VERIFY (GetItemRect (hItemTo, &rTo, false));
         if (rFrom.top > rTo.top) std::swap (hItemFrom, hItemTo);
      }
   }
   
   // перебираем в порядке видимости и выделяем
   for (HTREEITEM hItem = hItemFrom; hItem!=0; hItem = GetNextVisibleItem (hItem))
   {
      SelectItem (hItem, Select);
      if (hItem == hItemTo) break;
   }
}

bool CLayerCtrl::ItemSelected(HTREEITEM hItem) const
{
   return std::binary_search (m_Selection.begin(), m_Selection.end(), hItem);
}


Если смогу достучаться ,)

Проблемка: при ctrl вроде в список добавляется, а вот выделить не получается.. При этом пробовал вместо TVIS_SELECTED -> TVIS_BOLD - работает....
Хм, интересно, что с шифтом работает....    smile 

Автор: Earnest 14.11.2011, 13:12
Не пойму в чем проблема

Автор: Tor887 14.11.2011, 19:47
В ф-ии SelectItem ()
Делаем SetItemState(hItem, TVIS_SELECTED, TVIS_SELECTED), при этом hItem - текуший, т.е. в случае нажатого ctrl-а уже после переключения, а прошлый уже отключился!
Я попробовал запоминать старый и здесь делать etItemState(hOldItem, TVIS_SELECTED, TVIS_SELECTED), но вылазят некоторые другие геморы...  smile 
Но помаленьку продвигаюсь, вроде... ,)

Автор: Tor887 15.11.2011, 09:41
Вот еще какая штука: т.к. всё это сделано как реакция на изменение выделения - OnSelChanged - соответственно нельзя изменить состояние последнего выделенного элемента. Например выделили несколько элементов - не удастся снять выделение с последнего, потому что не вызывается OnSelChanged.
Пробовал решить эту проблему через OnNMClick, но там почему-то не работает фишка с вытаскиванием хендла на элемент..

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)