MFC, ListCtrl selection question
Project_Main_Code.cpp
Code:
#include "Project_Main_Header.h"
#include "resource.h"
CMyApp myApp;
BOOL CMyApp::InitInstance()
{
//Create dialog box as the main window
m_pMainWnd = new CMainDialog;
m_pMainWnd->ShowWindow(m_nCmdShow);
m_pMainWnd->UpdateWindow();
return TRUE;
}
BEGIN_MESSAGE_MAP(CMainDialog,CDialog)
//Message Handlers for the button pressed
ON_BN_CLICKED(IDC_BUTTON_EXIT,OnDialogExit)
ON_BN_CLICKED(IDC_BUTTON_ADD,OnAddTask)
ON_BN_CLICKED(IDC_BUTTON_DELETE,OnDeleteTask)
ON_BN_CLICKED(IDC_BUTTON_INSA,OnInsertAbove)
ON_BN_CLICKED(IDC_BUTTON_INSB,OnInsertBelow)
//Message Handler to repaint the dialog box
ON_WM_PAINT()
//Message Handler to check if any changes has been made in Edit Box
ON_EN_CHANGE(IDC_EDIT_TASKTITLEWRITE,OnUpdateEditCtrl)
//Message Handler to check if any selections made in ListCtrl
ON_NOTIFY(LVN_ITEMCHANGED,IDC_LIST_TASK,OnUpdateListCtrl)
END_MESSAGE_MAP()
CMainDialog::CMainDialog()
{
//Constructor to create the dialog box
Create(IDD_MAIN_DIALOG);
}
BOOL CMainDialog::OnInitDialog()
{
CDialog::OnInitDialog();
//Initialization function call for ListCtrl.
OnInitListCtrl();
OnInitCalCtrl();
//Disables the buttons initially
GetDlgItem(IDC_BUTTON_ADD)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_DELETE)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_INSA)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_INSB)->EnableWindow(FALSE);
//Modifies the DWSTYLE. Changes to a window without a system
//close button on Top-Right corner of the dialog box.
ModifyStyle(WS_SYSMENU,0,0);
return TRUE;
}
void CMainDialog::OnInitCalCtrl()
{
//Create a local Month Calendar Control Object
CMonthCalCtrl *l_MonthCalCtrl = (CMonthCalCtrl*)GetDlgItem(IDC_MONTHCALENDAR);
//Set the cursor in the month calendar object to the current date
l_MonthCalCtrl->SetCurSel(COleDateTime::GetCurrentTime());
//Create a local DateTimeCtrl object
CDateTimeCtrl *l_DateTimeCtrl = (CDateTimeCtrl*)GetDlgItem(IDC_DATETIMEPICKER);
//Set the current time in the object
l_DateTimeCtrl->SetTime(COleDateTime::GetCurrentTime());
}
void CMainDialog::OnInitListCtrl()
{
//Retrieve Dialog item for List Ctrl
//And store it in pListCtrl
pListCtrl = (CListCtrl*) GetDlgItem(IDC_LIST_TASK);
//Insert all columns displayed on the list control
pListCtrl->InsertColumn(0,"Start Time",LVCFMT_LEFT,100);
pListCtrl->InsertColumn(1,"Month",LVCFMT_CENTER,100);
pListCtrl->InsertColumn(2,"Date",LVCFMT_CENTER,100);
pListCtrl->InsertColumn(3,"Day",LVCFMT_CENTER,100);
pListCtrl->InsertColumn(4,"Year",LVCFMT_CENTER,100);
pListCtrl->InsertColumn(5,"Title",LVCFMT_CENTER,270);
}
void CMainDialog::DoDataExchange(CDataExchange *pDX)
{
CDialog::DoDataExchange(pDX);
//DoDataExchange functions for the title, monthctrl and time.
DDX_Text(pDX, IDC_EDIT_TASKTITLEWRITE, m_sTitle);
DDX_MonthCalCtrl(pDX,IDC_MONTHCALENDAR,m_cMonth);
DDX_DateTimeCtrl(pDX,IDC_DATETIMEPICKER,m_cTime);
}
void CMainDialog::OnUpdateEditCtrl()
{
//As soon as any changes have been made in the Editbox,
//Enables the add button
GetDlgItem(IDC_BUTTON_ADD)->EnableWindow(TRUE);
}
void CMainDialog::OnUpdateListCtrl(NMHDR * pNotifyStruct, LRESULT * result )
{
UpdateData(FALSE);
//As soon as any selections have been made in the list control,
//Enables the insert above and insert below objects.
GetDlgItem(IDC_BUTTON_INSA)->EnableWindow(TRUE);
GetDlgItem(IDC_BUTTON_INSB)->EnableWindow(TRUE);
//pListCtrl must be retrieved again
//as the variable is modified in the memory
//during run-time of application
//I.E : Code crashes if the item is not retrieved again.
pListCtrl = (CListCtrl*) GetDlgItem(IDC_LIST_TASK);
//Retrieve the index of the item selected (Row #)
int l_nSelected = pListCtrl->GetSelectionMark();
//Concatenate the strings, based on the column entries
CString l_tempStr = pListCtrl->GetItemText(l_nSelected,1)
+ " " + pListCtrl->GetItemText(l_nSelected,2) + " "
+ pListCtrl->GetItemText(l_nSelected,3) + " " +
pListCtrl->GetItemText(l_nSelected,4);
//Set the string in edit control box (Day)
//Object must be fetched again
l_EditBoxDay = (CEdit*) GetDlgItem(IDC_EDIT_TASKDAY);
l_EditBoxDay->SetWindowText(l_tempStr);
//Set the string for the time (Column 0)
l_tempStr = pListCtrl->GetItemText(l_nSelected,0);
//Set the string in edit control box (Time)
l_EditBoxTime = (CEdit*) GetDlgItem(IDC_EDIT_TASKTIME);
l_EditBoxTime->SetWindowText(l_tempStr);
//Set the string for the title
l_tempStr = pListCtrl->GetItemText(l_nSelected,5);
//Set the string in edit control box (Title)
l_EditBoxTitle = (CEdit*) GetDlgItem(IDC_EDIT_TASKTITLEDISP);
l_EditBoxTitle->SetWindowText(l_tempStr);
}
void CMainDialog::OnPaint()
{
//Repaints the client area
CPaintDC dc (this);
}
void CMainDialog::OnDialogExit()
{
//Destroys the window
DestroyWindow();
}
void CMainDialog::OnInsertAbove()
{
//Method to call DoDataExchange.
//UpdateData() is used, as DoDataExchange
//Cannot be called directly.
UpdateData();
//pListCtrl must be retrieved again
//as the variable is modified in the memory
//during run-time of application
//I.E : Code crashes if the item is not retrieved again.
pListCtrl = (CListCtrl*) GetDlgItem(IDC_LIST_TASK);
//Fetch the row # from the selection made
int l_listCtrlRow = pListCtrl->GetSelectionMark();
//Insert the time as a selectable item
pListCtrl->InsertItem(l_listCtrlRow,m_cTime.Format("%I:%M %p"));
//Set the corresponding texts onto the same column (Unselectable)
//Sets the Month, Day, Date and Year based on the format given
pListCtrl->SetItemText(l_listCtrlRow,1,m_cMonth.Format("%B"));
pListCtrl->SetItemText(l_listCtrlRow,2,m_cMonth.Format("%d"));
pListCtrl->SetItemText(l_listCtrlRow,3,m_cMonth.Format("%A"));
pListCtrl->SetItemText(l_listCtrlRow,4,m_cMonth.Format("%Y"));
//Sets the title
pListCtrl->SetItemText(l_listCtrlRow,5,m_sTitle);
//Creates a local CEdit object to clear the box
CEdit *pEditBox = (CEdit*) GetDlgItem(IDC_EDIT_TASKTITLEWRITE);
pEditBox->SetSel(0,-1);
pEditBox->Clear();
//Disables the Add, Insert Above and Insert Below buttons
GetDlgItem(IDC_BUTTON_ADD)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_INSA)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_INSB)->EnableWindow(FALSE);
}
void CMainDialog::OnInsertBelow()
{
//Method to call DoDataExchange.
//UpdateData() is used, as DoDataExchange
//Cannot be called directly.
UpdateData();
//pListCtrl must be retrieved again
//as the variable is modified in the memory
//during run-time of application
//I.E : Code crashes if the item is not retrieved again.
pListCtrl = (CListCtrl*) GetDlgItem(IDC_LIST_TASK);
//Fetch the row # from the selection made
int l_listCtrlRow = pListCtrl->GetSelectionMark() + 1;
//Insert the time as a selectable item
pListCtrl->InsertItem(l_listCtrlRow,m_cTime.Format("%I:%M %p"));
//Set the corresponding texts onto the same column (Unselectable)
//Sets the Month, Day, Date and Year based on the format given
pListCtrl->SetItemText(l_listCtrlRow,1,m_cMonth.Format("%B"));
pListCtrl->SetItemText(l_listCtrlRow,2,m_cMonth.Format("%d"));
pListCtrl->SetItemText(l_listCtrlRow,3,m_cMonth.Format("%A"));
pListCtrl->SetItemText(l_listCtrlRow,4,m_cMonth.Format("%Y"));
//Sets the title
pListCtrl->SetItemText(l_listCtrlRow,5,m_sTitle);
//Creates a local CEdit object to clear the box
CEdit *pEditBox = (CEdit*) GetDlgItem(IDC_EDIT_TASKTITLEWRITE);
pEditBox->SetSel(0,-1);
pEditBox->Clear();
//Disables the Add, Insert Above and Insert Below buttons
GetDlgItem(IDC_BUTTON_ADD)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_INSA)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_INSB)->EnableWindow(FALSE);
}
void CMainDialog::OnAddTask()
{
//Method to call DoDataExchange.
//UpdateData() is used, as DoDataExchange
//Cannot be called directly.
UpdateData();
//pListCtrl must be retrieved again
//as the variable is modified in the memory
//during run-time of application
//I.E : Code crashes if the item is not retrieved again.
pListCtrl = (CListCtrl*) GetDlgItem(IDC_LIST_TASK);
//Assign the amount of items into the variable
m_itemCount = pListCtrl->GetItemCount();
//Insert the time as a selectable item
pListCtrl->InsertItem(m_itemCount,m_cTime.Format("%I:%M %p"));
//Set the corresponding texts onto the same column (Unselectable)
//Sets the Month, Day, Date and Year based on the format given
pListCtrl->SetItemText(m_itemCount,1,m_cMonth.Format("%B"));
pListCtrl->SetItemText(m_itemCount,2,m_cMonth.Format("%d"));
pListCtrl->SetItemText(m_itemCount,3,m_cMonth.Format("%A"));
pListCtrl->SetItemText(m_itemCount,4,m_cMonth.Format("%Y"));
//Sets the title
pListCtrl->SetItemText(m_itemCount,5,m_sTitle);
//Creates a local CEdit object to clear the box
CEdit *pEditBox = (CEdit*) GetDlgItem(IDC_EDIT_TASKTITLEWRITE);
pEditBox->SetSel(0,-1);
pEditBox->Clear();
//Disables the add, insert above and below button
GetDlgItem(IDC_BUTTON_ADD)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_INSA)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_INSB)->EnableWindow(FALSE);
//Enables the delete button
GetDlgItem(IDC_BUTTON_DELETE)->EnableWindow(TRUE);
}
void CMainDialog::OnDeleteTask()
{
//pListCtrl must be retrieved again
//as the variable is modified in the memory
//during run-time of application
//I.E : Code crashes if the item is not retrieved again.
pListCtrl = (CListCtrl*) GetDlgItem(IDC_LIST_TASK);
//Check if the user has made any selections in List Control
if(pListCtrl->GetSelectionMark() == -1)
MessageBox("Select a Task to Delete","Error");
else
{
//Delete the selected item
pListCtrl->DeleteItem(pListCtrl->GetSelectionMark());
//Check if there's any item remaining in the list control
//and disable the delete button, if itemcount = 0
if(pListCtrl->GetItemCount()==0)
GetDlgItem(IDC_BUTTON_DELETE)->EnableWindow(FALSE);
//Unselect on the List Control
pListCtrl->SetSelectionMark(-1);
//Disable the Insert Above and Insert Below buttons
GetDlgItem(IDC_BUTTON_INSA)->EnableWindow(FALSE);
GetDlgItem(IDC_BUTTON_INSB)->EnableWindow(FALSE);
//Clear the readable-only edit boxes
//All the objects needs to be fetched again
//(Same case with pListCtrl)
l_EditBoxDay = (CEdit*) GetDlgItem(IDC_EDIT_TASKDAY);
l_EditBoxDay->SetWindowText("");
l_EditBoxTime = (CEdit*) GetDlgItem(IDC_EDIT_TASKTIME);
l_EditBoxTime->SetWindowText("");
l_EditBoxTitle = (CEdit*) GetDlgItem(IDC_EDIT_TASKTITLEDISP);
l_EditBoxTitle->SetWindowText("");
}
}
void CMainDialog::PostNcDestroy()
{
delete this;
}
Ok, I'm having a serious problem with the list control and it updating the text to a read-only edit control box.
Under the OnUpdateListCtrl function, where it triggers as soon as the user selects an item in the list control box, it's supposed to update the read-only edit control box based on the selection made. Now, the problem occurs on this particular line -
int l_nSelected = pListCtrl->GetSelectionMark();
This returns the integer of the row # (0, from the first one), but it returns it a tad-bit late. Let me demonstrate what I mean -
Suppose the program had two items and I clicked on the second item - this properly returns an integer of 1. NOW suppose that I clicked on the first item, it's supposed to return an integer of 0, right? well it doesn't. It returns 1. Again, if I were to click on the second item (Where it should return 1), it returns 0. This becomes a problem because the read-only edit control box updates the information one-step too late.
This function acts immediately upon WM_NOTIFY message, tricking the list control to think that it hasn't made the selection change just yet.
I believe I have pinpointed the problem correctly, but I do not know of the correct fix. I was thinking about using CPoint in the OnUpdateListCtrl, but I believe that's taking things way too far in terms of the correction. There has to be an easier fix for this.
Can anyone tell me any suggestions? I'd greatly appriciate any help.
Re: MFC, ListCtrl selection question
try this:
Override the NM_CLICK message:
Code:
NMLISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
LVHITTESTINFO hitinfo;
hitinfo.pt = pNMListView->ptAction;
int item = myListCtrl.HitTest(&hitinfo);
item will hold the clicked item. than continue as you should.
Re: MFC, ListCtrl selection question
Does not work. here's the code I have :
Code:
void CMainDialog::OnUpdateListCtrl(NMHDR * pNotifyStruct, LRESULT * result )
{
NMLISTVIEW* pNMListView = (NM_LISTVIEW*) pNotifyStruct;
LVHITTESTINFO hitinfo;
hitinfo.pt = pNMListView->ptAction;
//As soon as any selections have been made in the list control,
//Enables the insert above and insert below objects.
GetDlgItem(IDC_BUTTON_INSA)->EnableWindow(TRUE);
GetDlgItem(IDC_BUTTON_INSB)->EnableWindow(TRUE);
//pListCtrl must be retrieved again
//as the variable is modified in the memory
//during run-time of application
//I.E : Code crashes if the item is not retrieved again.
pListCtrl = (CListCtrl*) GetDlgItem(IDC_LIST_TASK);
//Retrieve the index of the item selected (Row #)
int l_nSelected = pListCtrl->HitTest(&hitinfo);
//Concatenate the strings, based on the column entries
CString l_tempStr = pListCtrl->GetItemText(l_nSelected,1)
+ " " + pListCtrl->GetItemText(l_nSelected,2) + " "
+ pListCtrl->GetItemText(l_nSelected,3) + " " +
pListCtrl->GetItemText(l_nSelected,4);
//Set the string in edit control box (Day)
//Object must be fetched again
l_EditBoxDay = (CEdit*) GetDlgItem(IDC_EDIT_TASKDAY);
l_EditBoxDay->SetWindowText(l_tempStr);
//Set the string for the time (Column 0)
l_tempStr = pListCtrl->GetItemText(l_nSelected,0);
//Set the string in edit control box (Time)
l_EditBoxTime = (CEdit*) GetDlgItem(IDC_EDIT_TASKTIME);
l_EditBoxTime->SetWindowText(l_tempStr);
//Set the string for the title
l_tempStr = pListCtrl->GetItemText(l_nSelected,5);
//Set the string in edit control box (Title)
l_EditBoxTitle = (CEdit*) GetDlgItem(IDC_EDIT_TASKTITLEDISP);
l_EditBoxTitle->SetWindowText(l_tempStr);
//Set the string in edit control box (Title)
CEdit *l_EditBoxTitleW = (CEdit*) GetDlgItem(IDC_EDIT_TASKTITLEWRITE);
l_EditBoxTitleW->SetSel(0,-1);
l_EditBoxTitleW->Clear();
}
Re: MFC, ListCtrl selection question
why aren't you using a control variable for the list control. you are trying to get a control variable each time you nedd it. Maybe the problem is there, I don't know. Also set a variable for each edit control you need to handle. it also reduces your code as you don't have to call GetDlgItem each time you need a control variable.
Re: MFC, ListCtrl selection question
I tried doing that, but the code crashes, if I don't call GetDlgItem, each time in a different function. I think the object is somehow changed during runtime.
I'm using ListCtrl object, not ListView. In your code you gave me, perhaps the problem lies there?
Re: MFC, ListCtrl selection question
I'm also using ListControl not list View. The only diference I have with your code is thar I've placed *pResult = 0 at the end of the function. Here is the code I'm using in my program:
Code:
void CSendSmsT9::OnNMDblclkT9Nowwords(NMHDR *pNMHDR, LRESULT *pResult)
{
// TODO: Add your control notification handler code here
NMLISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
LVHITTESTINFO hitinfo;
hitinfo.pt = pNMListView->ptAction;
int item = l_wordsNow.HitTest(&hitinfo);
if(item != -1)
{
CString buffer,tmp;
CString startText,endText;
int startSel,endSel;
e_T9_text.GetWindowTextA(buffer);
e_T9_text.GetSel(startSel,endSel);
if(startSel == endSel)
{
if(startSel == buffer.GetLength())
{
startText = buffer.Left(startSel - decodeNumber.GetLength());
endText = buffer.Right(buffer.GetLength() - endSel);
}
else
{
startText = buffer.Left(startSel - decodeNumber.GetLength());
endText = buffer.Right(buffer.GetLength() - endSel);
}
}
else
{
startText = buffer.Left(startSel);
endText = buffer.Right(buffer.GetLength() - endSel);
}
tmp = l_wordsNow.GetItemText(item,0);
if(statusMode == MODE_T9Caps)
tmp.MakeUpper();
tmp += " ";
e_T9_text.SetWindowTextA(startText + tmp + endText);
e_T9_text.GetWindowTextA(buffer);
if(tmp.GetLength() == decodeNumber.GetLength())
e_T9_text.SetSel(startSel + 1,startSel + 1);
else
e_T9_text.SetSel(startSel + tmp.GetLength() - decodeNumber.GetLength(),startSel + tmp.GetLength() - decodeNumber.GetLength());
e_T9_text.SetFocus();
words_database.clear();
decodeNumber = "";
l_wordsNow.SetItemCount(0);
}
CountCharacters();
*pResult = 0;
}
I'm using the doble click on an Item of the list but the result will be the same. And my code doesn't crash. On resource editor I've clicked wil the right button on the list and I've choosed Add Variable. This will add a control variable for the list. I don't know if you have done tha same.
Re: MFC, ListCtrl selection question
No, I didn't use Add Member variable. That menu item was disabled in the resource compiler, when I right clicked, so I went ahead and defined all the control variable in the header file and in the CPP file. How should I fix this issue?
Re: MFC, ListCtrl selection question
in the cpp file you should have this
Code:
void yourClass::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
}
add the hadler for the control just after the CDialog:: DoDataExchage(pDX);
Code:
DDX_Control(pDX, IDC_T9_NOWWORDS, l_wordsNow);
DDX_Control(pDX, IDC_T9_TEXT, e_T9_text);
IDC_T9_NOWWORDS is the value you have assigned to your list and l_wordsNow will be the control variable of your ListCtrl.
IDC_T9_TEXT is the value you have assigned to your Edit control and e_T9_text is the control variable of your Edit control.
Also on the .h file add this to lines on the public: section
Code:
CListCtrl l_wordsNow;
CEdit e_T9_text;
I'm supprised why the Add Variable is grayed on your case.
And another thing: You are you creating a modeless dialog box as your main Window, shoudn't it be a Modal DialogBox?
Re: MFC, ListCtrl selection question
Since DDX_Control accepts CWnd& type for the third variable,
DDX_Control(pDX, IDC_LIST_TASK, (CWnd&)pListCtrl);
Does this make sense? I think something's off.
IDC_LIST_TASK is my ID for the ListControl
pListCtrl is the member variable for it
If I put
DDX_Control(pDX, IDC_LIST_TASK, pListCtrl);
Compiler complains with the error message of
error C2664: 'DDX_Control' : cannot convert parameter 3 from 'CListCtrl *' to 'CWnd &'
[Edit]Project requirement stated it needed to be modaless dialog box as main window.
Also, here's my header file, in case you wanted to see it
Code:
#include <afxwin.h>
#include <afxcmn.h>
#include <afxdtctl.h>
#ifndef _DIALOGASMAINWINDOW_H_
#define _DIALOGASMAINWINDOW_H_
class CMyApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
class CMainDialog : public CDialog
{
public:
//Constructor for the main dialog
CMainDialog();
//Function Prototype Declarations for Buttons
void OnDialogExit();
void OnInsertAbove();
void OnInsertBelow();
void OnAddTask();
void OnDeleteTask();
//Function Prototype Declarations for Updates
void OnUpdateEditCtrl();
void OnUpdateListCtrl(NMHDR * pNotifyStruct, LRESULT * result );
//Function Prototype Declaration for Initializations for ListCtrl,Month
void OnInitListCtrl();
void OnInitCalCtrl();
protected:
virtual void DoDataExchange(CDataExchange *pDX);
virtual BOOL OnInitDialog();
virtual void PostNcDestroy();
afx_msg void OnPaint();
DECLARE_MESSAGE_MAP()
private:
CListCtrl *pListCtrl;
CEdit *l_EditBoxDay;
CEdit *l_EditBoxTime;
CEdit *l_EditBoxTitle;
CTime m_cMonth;
CTime m_cTime;
CString m_sTitle;
int m_itemCount;
};
#endif
Re: MFC, ListCtrl selection question
In your .h file you have defined CListCtrl *pListCtrl or CListCtrl pListCtrl. You have to define it as a control variable not a pointer to a control variable.
Re: MFC, ListCtrl selection question
Oh man, I didn't think that would solve the problem, I'll try this and see what happens. Thanks a lot!
[Edit]
I found a solution for my prior problem
Code:
DWORD l_mPosition = ::GetMessagePos();
CPoint myPoint((int)LOWORD(l_mPosition),(int)HIWORD(l_mPosition));
m_ListCtrl.ScreenToClient(&myPoint);
int l_nSelected = m_ListCtrl.HitTest(myPoint);
this works excellently!