[MFC/CListCtrl] rozróżnienie kliknięcia wiersz-checkbox

0

Mam listę zrobioną na podstawie CListCtrl (chodzi o kolorowanie wierszy) i mam checkboxy na liście

żeby nie było zaznaczania na kliku, przeniosłem akcję na zdarzenie dblClick - dzięki temu aby dać focusa liście nie trzeba od razu zaznaczać elementu listy

ale na liście mam checkboxy i i chciałbym, aby kliknięcie w checkbox (tylko w checkbox) działało jak dblclik dla listy, jako że z MFC zaczynam zabawę to część kodu skopiowałem część sam napisałem i zaciąłem się w tym miejscu.

Zrobione jest to w następujący sposób
1-jest klasa dziedzicząca po CListCtrl, w której jest metoda DrawItem

void CListControlColor::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
   CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
	CRect rcItem(lpDrawItemStruct->rcItem);
	UINT uiFlags = ILD_TRANSPARENT;

	CImageList* pImageList;
	int nItem = lpDrawItemStruct->itemID;
	BOOL bFocus = (GetFocus() == this);
	COLORREF clrTextSave = pDC->GetTextColor ();
   COLORREF clrBkSave = pDC->GetBkColor ();
	COLORREF clrImage = GetItemBkColor(lpDrawItemStruct->itemID);
	static _TCHAR szBuff[MAX_PATH];

// get item data

	LV_ITEM lvi;
	lvi.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE | LVIF_PARAM;
	lvi.iItem = nItem;
	lvi.iSubItem = 0;
	lvi.pszText = szBuff;
	lvi.cchTextMax = sizeof(szBuff);
	lvi.stateMask = 0xFFFF;     // get all state flags
	GetItem(&lvi);

	BOOL bSelected = (bFocus || (GetStyle() & LVS_SHOWSELALWAYS)) && lvi.state & LVIS_SELECTED;
	bSelected = bSelected || (lvi.state & LVIS_DROPHILITED);

// set colors if item is selected

	CRect rcAllLabels;
	GetItemRect(nItem, rcAllLabels, LVIR_BOUNDS);

	CRect rcLabel;
	GetItemRect(nItem, rcLabel, LVIR_LABEL);

	rcAllLabels.left = rcLabel.left;

	if (bSelected)
	{
      CBrush brBkgnd ( GetSysColor ( COLOR_HIGHLIGHT ) );
		pDC->SetTextColor(::GetSysColor(COLOR_HIGHLIGHTTEXT));
		pDC->SetBkColor(::GetSysColor(COLOR_HIGHLIGHT));
    	pDC->FillRect(rcAllLabels, &brBkgnd);
      
	}
	else
	{
      pDC->SetTextColor(GetItemTextColor(lpDrawItemStruct->itemID));
      
		COLORREF	crBkgnd = GetItemBkColor(lpDrawItemStruct->itemID);

		if ( crBkgnd == CLR_DEFAULT )
			crBkgnd = GetSysColor ( COLOR_WINDOW );
			
      CBrush brBkgnd ( crBkgnd );

//      if(GetStyle()|LVS_EX_GRIDLINES)
//         rcAllLabels.DeflateRect(1,1,1,1);

      if(GetStyle()|LVS_EX_CHECKBOXES)
      {

      }

      
		pDC->FillRect(rcAllLabels, &brBkgnd );      
	}

// set color and mask for the icon

	if (lvi.state & LVIS_CUT)
	{
		clrImage = GetItemBkColor(lpDrawItemStruct->itemID);
		uiFlags |= ILD_BLEND50;
	}
	else if (bSelected)
	{
		clrImage = ::GetSysColor(COLOR_HIGHLIGHT);
		uiFlags |= ILD_BLEND50;
	}

// draw state icon

	UINT nStateImageMask = lvi.state & LVIS_STATEIMAGEMASK;
	if (nStateImageMask)
	{
		int nImage = (nStateImageMask>>12) - 1;
		pImageList = GetImageList(LVSIL_STATE);
		if (pImageList)
		{
			pImageList->Draw(pDC, nImage,
			CPoint(rcItem.left, rcItem.top), ILD_TRANSPARENT);
		}
	}

   
// draw normal and overlay icon

	CRect rcIcon;
	GetItemRect(nItem, rcIcon, LVIR_ICON);

	pImageList =  GetImageList(LVSIL_SMALL);
	if (pImageList)
	{
		UINT nOvlImageMask=lvi.state & LVIS_OVERLAYMASK;
		if (rcItem.left<rcItem.right-1)
		{
			ImageList_DrawEx(pImageList->m_hImageList, lvi.iImage,
					pDC->m_hDC,rcIcon.left,rcIcon.top, 16, 16,
					GetItemBkColor(lpDrawItemStruct->itemID), clrImage, uiFlags | nOvlImageMask);
		}
	}

// draw item label

	GetItemRect(nItem, rcItem, LVIR_LABEL);
//	rcItem.right -= 16;

	rcLabel = rcItem;
	rcLabel.left += OFFSET_FIRST;
	rcLabel.right -= OFFSET_FIRST;

   LV_COLUMN lvc;
   lvc.mask = LVCF_FMT | LVCF_WIDTH;
   GetColumn(0, &lvc);

   UINT nJustify = DT_LEFT;
   switch(lvc.fmt & LVCFMT_JUSTIFYMASK)
	{
	case LVCFMT_RIGHT:
      nJustify = DT_RIGHT;
      break;
   case LVCFMT_CENTER:
      nJustify = DT_CENTER;
      break;
   default:
      break;
	}

	pDC->DrawText ( szBuff, -1, rcLabel, nJustify | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER | DT_MODIFYSTRING | DT_END_ELLIPSIS );

// draw labels for extra columns

	for(int nColumn = 1;  GetColumn(nColumn, &lvc); nColumn++)
   {
      rcItem.left = rcItem.right;
      rcItem.right += lvc.cx;
      
      if ( GetItemText ( nItem, nColumn, szBuff, sizeof ( szBuff ) ) == 0 )
         continue;
      
      rcLabel = rcItem;
      rcLabel.left += OFFSET_OTHER;
      rcLabel.right -= OFFSET_OTHER;
      
      nJustify = DT_LEFT;
      switch(lvc.fmt & LVCFMT_JUSTIFYMASK)
      {
      case LVCFMT_RIGHT:
         nJustify = DT_RIGHT;
         break;
      case LVCFMT_CENTER:
         nJustify = DT_CENTER;
         break;
      default:
         break;
      }      
      
      pDC->DrawText ( szBuff, -1, rcLabel, nJustify | DT_SINGLELINE | DT_NOPREFIX | DT_VCENTER | DT_MODIFYSTRING | DT_END_ELLIPSIS );
   }

// draw focus rectangle if item has focus

	if (lvi.state & LVIS_FOCUSED && bFocus)
		pDC->DrawFocusRect(rcAllLabels);

// set original colors if item was selected

	if (bSelected)
	{
		pDC->SetTextColor(clrTextSave);
		pDC->SetBkColor(clrBkSave);
	}
}

są z nią powiązane inne metody, ale nie będę ich wklejał - szkoda miejsca

2 - na dialogu mam metodę OnDblclkListTimetable, która wygląda następująco i zaznacza checkbox w wierszu

void CDlgListControl::OnDblclkListTimetable(NMHDR* pNMHDR, LRESULT* pResult) 
{
	BOOL bItemState=TRUE;
   DWORD dwPos = GetMessagePos();
   CPoint point( LOWORD(dwPos), HIWORD(dwPos) );
   m_ctrListRozkladLinia.ScreenToClient(&point);
   LVHITTESTINFO lvinfo;
   lvinfo.pt = point;
   lvinfo.flags = LVHT_ABOVE;
   lvinfo.pt.x;
   lvinfo.pt.y;
   if(nItem != -1)
   {
      // m_ctrListRozkladLinia.EnableWindow(FALSE);
      bItemState = (m_ctrList.GetCheck(nItem)==TRUE)?FALSE:TRUE;
   }
	
	*pResult = 0;
}

3 - chcę to rozgraniczyć, tak aby dbclick pozostał dla wierszy, a click działał dla checkboxa

jako, że nie za bardzo i nie do końca rozumiem kod (szczególnie DrawItem) to próbowałem metoda prób i błędów, jedyne co osiągnąłem to efekt, że częśc wiersza należąca do pierwszej kolumny działa na click, a pozostałe na dblclick - co więcej zwykła lista, bez dziedziczenia ma takie rozgraniczenie - na checkboxa i wiersz - ale nie wiem co jest źle na tej dziedziczonej że czegoś takiego nie ma i jak takie coś zrobić

siędzę już nad tym trochę czasu i nie wiem jak się do tego zabrać co zrobić itp itd

może ktoś miałby pomysl - chętnie poczytam

0

skad magicznie bierzesz nItem w OnDblclkListTimetable ? skad wiesz ze to jest id item'a ktory zostal klikniety?

do obiektu Twojej listy powinienes podpiac dwa handlery:

  • OnClick
  • OnDblClick
    w obu, powinienes sprawdzic ktore miejsce zostalo klikniete (np, na podstawie point'a) - czy klik odbyl sie na ckboxie czy poza nim. nastepnie w:
  • OnClick - jesli na ckboxie, to okreslic nItem tego ckboxa i zmienic jego checkstate else nic
  • OnDblClick - jesli niena ckboxie, to okreslic numer wiersza listy i go za/od/znaczyc, else nic

afair, nItem to wlasnie numer wiersza listy, ale wolalem napisac osobno

0

dzięki za pomoc i za zainteresowanie
nie wiem czy komuś może się to przydać, czy może ktoś poda bardziej eleganckie rozwiązanie, ala ja to zrobiłem tak i działa

void CDlgLista::OnClickListTimetable(NMHDR* pNMHDR, LRESULT* pResult)
{
  
    ustawListe(TRUE,m_ctrlListSprawdz);
	*pResult = 0;
}


void CDlgLista::OnDblclkListTimetable(NMHDR* pNMHDR, LRESULT* pResult) 
{
  
   ustawListe(FALSE,m_ctrlListSprawdz);
	
	*pResult = 0;
}

void CDlgLista::ustawListe(BOOL bCzySprPoz, ClistCtrl &clLista)//argumenty to 1-CZY SPRAWDZAĆ 2-lista której ma to dotyczyć
{
   DWORD dwPos = GetMessagePos();
   CPoint point( LOWORD(dwPos), HIWORD(dwPos) );
   clLista.ScreenToClient(&point);
   LVHITTESTINFO lvinfo;
   lvinfo.pt = point;
   lvinfo.flags = LVHT_ABOVE;
   lvinfo.pt.x;
   lvinfo.pt.y;
   int nItem = clLista.SubItemHitTest(&lvinfo);

   bCzySprPoz= (!bCzySprPoz)?TRUE:(point.x>2 && point.x<14);//tak mi wyszło w VC6 rozkład checkboxa
   
  if(nItem != -1 && bCheckPos)
  {
       //ta sama akcja na KLIK CHECKOBOX i DBKLIK wiersz
  }

}
0

lvinfo.pt.x;
lvinfo.pt.y;
A to co ?

1 użytkowników online, w tym zalogowanych: 0, gości: 1