DevDoc home page DevDoc background gradient
 
   
Имя Пароль Запомнить Зарегистрироваться

Автор: Кудинов Александр
Последняя модификация: 2007-03-05 20:54:19

Использование checkbox в списке ClistCtrl

Скачать исходники

Введение

Пожалуй, список - это один из самых популярных элементов управления в программах. Если не считать разные кнопки. Обычно списки строятся на базе CListCtrl. Он предоставляет разработчику множество опций. До версии 4.7 он не поддерживал чекбоксы для своих элементов. Для реализации этой функции можно было использовать класс CCheckListBox. Благодаря нововведению можно совмещать checkbox с остальными полезными функциями: Custom/Owner Draw, сортировка, поиск, рисование иконок рядом с элементами.

Как добавить CeckBox в CListCtrl?

Существует несколько путей для решения этой задачи. Наиболее распространенные:

  • Использовать режим Owner Draw и рисовать галочку самостоятельно. Это замечательный способ, если надо получить нестандартный внешний вид.
  • Использование Custom Draw. Метод имеет свои особенности, но в целом практически идентичен предыдущему варианту.
  • Можно рисовать иконку рядом с элементом, используя стандартные средства CListCtrl. При этом надо будет самостоятельно отслеживать нажатия на иконку, и менять ее внешний вид. Впрочем, собственную обработку надо выполнять и для предыдущих методов.
  • Использовать новые возможности CListCtrl.

По умолчанию в последнем случае изображение checkbox рисуется с помощью DrawFrameControl. Поэтому они всегда имеют один и тот же вид и очень часто не вписываются в дизайн программы. В конце статьи я дам несколько идей, как обойти это ограничение.

Первые 3 варианта я не буду рассматривать, т.к. они достаточно трудоемки и могут понадобиться в очень редких случаях. Итак, приступим.

Включение чекбоксов

Сразу после создания объекта CListCtrl, или экземпляра вашего класса на его основе, необходимо вызывать:

ListView_SetExtendedListViewStyle(m_wndView.m_hWnd, LVS_EX_CHECKBOXES);

Где m_wndView - экземпляр Вашего класса. Почему-то указание стилей при создании элемента управления не приводит к желаемому результату. Поэтому надо выполнять эту операцию до того, как в список будут добавляться элементы.

На этом вся работа и заканчивается. Элемент управления сделает остальную работу за вас. Он будет рисовать галочки рядом с элементами, а также менять их состояние по щелчку мыши.

Для эффективной работы очень полезно знать момент, когда пользователь меняет состояние элемента. Это можно сделать следующим образом.

Если вы создали свой класс на базе CListCtrl, как в примере к статье, то вам надо добавить в карту сообщений этого класса строку:

ON_NOTIFY_REFLECT(LVN_ITEMCHANGED, OnItemchangedLinksList)

Затем создать функцию следующего вида:

void CChildView::OnItemchangedLinksList(NMHDR* pNMHDR, LRESULT* pResult)
{
	NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
	*pResult = 0;
 
	if (pNMListView->uOldState == 0 && pNMListView->uNewState == 0)
		return;    // не было изменений
 
 
	// Старое состояние чекбокса
	BOOL bPrevState = (BOOL)(((pNMListView->uOldState & 
		LVIS_STATEIMAGEMASK)>>12)-1);  
	if (bPrevState < 0)    // При первом запуске состояние может быть не определено
		bPrevState = 0;	   // поэтому ставим его по умолчанию в false
 
	// Считать новое состояние
	BOOL bChecked = 
		(BOOL)(((pNMListView->uNewState & LVIS_STATEIMAGEMASK)>>12)-1);   
	if (bChecked < 0) // Новое состояние может быть неопределено. По умочанию тоже ставим false
		bChecked = 0; 
 
	if (bPrevState == bChecked) // Нет изменений
		return;
 
	//В этой точке bChecked содержит новое состояние для строки
}

Дальнейшее очевидно. В конце можете дописывать свой код, который будет выполнять некие действия в ответ на изменение состояния элементов.

Менять состояние элемента можно из программы с помощью метода CListCtrl::SetItemState следующим образом:

Bool bCheck = true;		//новое состояние
SetItemState(iItemIndex, UINT((int(bCheck) + 1) << 12),  LVIS_STATEIMAGEMASK);

Битовый сдвиг нужен, т.к. во флаге состояния элемента управления может быть несколько полей. Единица прибавляется, т.к. индексы состояний начинаются с 1.

См. информацию по LVITEM структуре в MSDN.

Получение состояния может быть выполнено аналогично.

Улучшение внешнего вида CListCtrl

После первого опыта со списком видно, что галочки рядом с элементами «скучные». Часто они не подходят к размеру шрифта и общему дизайну программы. Есть способ обойти это ограничение. К сожалению, в MSDN он описан очень плохо. Здесь я приведу только общие принципы реализации. Ничего сложного в этом нет, поэтому вы с легкостью сможете модифицировать пример к статье, чтобы он рисовал такие галочки, какие вам необходимы.

У CListCtrl есть метод SetImageList. Он позволяет устанавливать изображения иконок, которые будут использоваться при рисовании элементов. Список поддерживает 3 типа иконок:

  • Большие иконки
  • Маленькие иконки
  • Иконки состояния.

Последний тип с легкостью позволяет изменять изображения для иконок состояния. Т.е. надо сделать объект типа CImageList и добавить в него две иконки, которые бы соответствовали установленной и снятой галочке. Все! Теперь CListCtrl будет рисовать нужные вам элементы.

Ну и напоследок еще одна вкусность, которая почему-то замалчивается в документации. Я ее «вычислил», внимательно разглядывая структуру LVITEM. Надеюсь, вы последовали моему совету и сделали то же самое. Можно заметить, что для хранения состояния чекбокса используется член LVITEM ::state. Если быть конкретным, то биты с 12 по 15. Это несколько больше, чем надо для хранения значения true/false! Секрет прост – у каждого элемента может быть больше двух состояний. Причем, список «знает» только об одном – нулевом. Когда все биты равны нулю, то у элемента нет иконки состояния. Остальные значения могут быть произвольными.

Использовать эту особенность не просто, а очень просто. Надо просто создать необходимое количество иконок, поместить их в CImageList, а затем вызвать CListCtrl::SetImageList, чтобы установить новый список иконок состояния. После этого элемент управления будет циклически переключать состояния, и показывать соответствующие иконки.

Состояния можно ставить принудительно с помощью SetItemState: вместо булевого значения надо передавать индекс нужного состояния.

Помните, что индексы начинаются с 1.

Вы умеете обрабатывать смену состояний, и я думаю, что для вас не составит особого труда, чтобы состояния менялись не циклически, а по определенным правилам.

Если все вышеперечисленное использовать с Виртуальными списками – можно получить очень мощный и гибкий элемент управления. Виртуальные списки позволяют хранить индексы состояния отдельно от элемента управления. Это может дать ряд преимуществ, если надо управлять большим объемом данных и менять состояния по сложным законам.

Наслаждайтесь новыми возможностями!

Подпишитесь, чтобы получать все новые статьи с сайта первым!

Оценить статью Текущий рейтинг: 3.5263. Проголосовало 57 человек.

Коментарии к статье

Нет комментариев

Добавить комментарий

Вы можете добавлять свои комментарии, пожелания или вопросы к статье. Они будут видны всем пользователям, а также автору статьи. Войдите в систему под своим именем, чтобы другие пользователи могли видеть, кто автор сообщения. Форма для входа расположена вверху страницы. Если у Вас нет аккаунта на сайте - рекомендуем зарегистрироваться. Это займет всего пару минут.

Отправлять сообщения могут только зарегистрированные пользователи

Copyright (C) Kudinov Alexander, 2006-2012

Перепечатка материалов с данного сайта запрещена без письменного разрешения автора. При перепечатке обязательно указывать ссылку на оригинал.

Generation time: 0,152589082718 seconds