Using ON_WM_HSCROLL() while still having a functional default scrollbar? - c++

I am writing an MFC application which uses the CSliderCtrl class to control a slider. I have the ON_WM_HSCROLL() message in my message map, but this arises with the problem that this disables the default window scrollbar that appears at the bottom of the view when the windows is too small. Manipulating it does nothing to the window. What do I have to do in order to preserve functionality in that scrollbar?
Currently, my OnHScroll() function simply looks like:
void myClass::OnHScroll(UINT nSHCode, UINT nPos, CScrollBar* pScrollBar)
{
if (*pScrollBar == mySlider)
{
// do stuff
}
}

You still need to call the default handler defined in base/parent class: CDialog::OnHScroll(nSBCode, nPos, pScrollBar); in case of dialog window or CFormView::OnHScroll(nSBCode, nPos, pScrollBar); in case of SDI/MDI view.
So your handler will look like this:
void myClass::OnHScroll(UINT nSHCode, UINT nPos, CScrollBar* pScrollBar)
{
if (*pScrollBar == mySlider)
{
// do stuff
}
CDialog::OnHScroll(nSBCode, nPos, pScrollBar)
}

Related

Set CSliderCtrl to move in 10 unit intervals

I have a CSliderCtrl and I set it up like this:
m_sliderServerTimeout.SetRange(10, 600);
I have an event handler:
void COtherSettingsEmailInfoPage::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
if (pScrollBar->GetDlgCtrlID() == IDC_SLIDER)
{
UpdateServerTimeoutDescription();
}
CMFCPropertyPage::OnHScroll(nSBCode, nPos, pScrollBar);
}
Works fine but I want the slider to move in 10 unit intervals.
How?
Update
I tried using SetLineSize and SetPageSize but they don't apply to when the slider is dragged.
Just use the slider metric you want. So just use 1/10th in the range of the values.
m_sliderServerTimeout.SetRange(1, 60);
And finally when you want to use the data, just scale the set value by *10.
There doesn't seem to be a Trackbar Control Message to handle this.
One option is to override OnHScroll manually. Watch for SB_THUMBTRACK message and save the value for nPos. When SB_ENDSCROLL is called, use CSliderCtrl::SetPos to set to the desired value. Example:
void CMyDialog::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
static int track = -1;
if(nSBCode == SB_THUMBTRACK)
track = nPos;
if(nSBCode == SB_ENDSCROLL && track >= 0)
{
m_sliderServerTimeout.SetPos(10 * int(track / 10));
track = -1;
}
}

VisualStudio MFC CListCtrl SetItemText fails

I created a MFC Visual Studio Project with a CListCtrl. I added some items in the CDialog class like:
int l_iItem = m_listCtrl.InsertItem(LVIF_TEXT|LVIF_STATE, counter, someString, 0, LVIS_SELECTED, 0, 0);
m_listCtrl.SetItemText( l_iItem, 1, blockHexChar );
m_listCtrl.SetItemText( l_iItem, 2, description);
This works fine.
Afterwards i want to edit a subitem (over double click event). Works also fine.
If the editing is finished (this is in the CListCtrl class),
OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult)
will be called. It looks like this
LV_DISPINFO *plvDispInfo = (LV_DISPINFO *)pNMHDR;
LV_ITEM *plvItem = &plvDispInfo->item;
if (plvItem->pszText != NULL)
{
bool res = SetItemText(plvItem->iItem, plvItem->iSubItem, plvItem->pszText);
}
I always getting 0 back, so the SetItemText is failing.
Any idea what i'm doing wrong?
Cheers ehmkey
You have to post yourself a user-defined message (WM_USER+NNN) using PostMessage from within OnEndLabelEdit. Change label in response to that message.
LVN_ENDLABELEDIT passes a pointer to NMLVDISPINFO through lParam. I think you're looking at the wrong structure when processing the notification. Using the class wizard to generate the event handler in VS2013 gives
void CMFCApplication6Dlg::OnLvnEndlabeleditList1(NMHDR *pNMHDR, LRESULT *pResult)
{
NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
// TODO: Add your control notification handler code here
*pResult = 0;
}
Thanks for your input, but the problem was in the MESSAGE_MAP(....) in CDialog class.
I have a member here, which holds the list and I directly mapped to the CListCtrl Class.
BEGIN_MESSAGE_MAP(CPeriDialog, CDialog)
ON_NOTIFY(LVN_ENDLABELEDIT, IDC_LIST1, &CListCtrl::OnEndLabelEdit)
END_MESSAGE_MAP()
Now I made a wrapper function which forward the event to the right object.
BEGIN_MESSAGE_MAP(CPeriDialog, CDialog)
ON_NOTIFY(LVN_ENDLABELEDIT, IDC_LIST1, &CPeriDialog::EndEdit)
END_MESSAGE_MAP()
Function simple looks like this
void CPeriDialog::EndEdit(NMHDR* pNMHDR, LRESULT* pResult)
{
m_listCtrl.OnEndLabelEdit(pNMHDR, pResult);
}

MFC how to know a resizing of view is finished

I am wondering how to catch the fact that a view (CView in a CMDIChildWnd frame) has been resized, and that the user just released the left mouse button.
None of OnSize, OnSizing and OnLButtonUp work.
I have tried the solution in http://www.codeguru.com/forum/showthread.php?t=59476 and it doesn't work.
I am working on VC2010 with W7.
Thanks in advance.
Try WM_NCLBUTTONUP. I don't know if a view can be resized other than by the mouse, but if it can you probably also want to respond to WM_EXITSIZEMOVE as in the link you gave.
I recently needed to do this. I used a combination of OnSysMessage to capture the SC_SIZE and SC_MOVE event and WM_ENTERSIZEMOVE and WM_EXITSIZEMOVE within the CMDIChildWnd derived class. Within the OnSysMessage handler I set a public variable for IsSizing. I can then check the variable within the OnEnterSizeMove function and act accordingly. Within the OnExitSizeMove function, it resets the variable to FALSE.
BEGIN_MESSAGE_MAP(CChildFrame, CMDIChildWnd)
//{{AFX_MSG_MAP(CChildFrame)
ON_WM_SYSCOMMAND()
ON_MESSAGE(WM_ENTERSIZEMOVE, OnEnterSizeMove)
ON_MESSAGE(WM_EXITSIZEMOVE, OnExitSizeMove)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
And for the handlers:
void CChildFrame::OnSysCommand(UINT nID, LPARAM lParam)
{
switch(nID&0xfff0)
{
case SC_SIZE:
m_bIsSizing = TRUE;
break;
case SC_MOVE:
m_bIsSizing = FALSE;
break;
}
CMDIChildWnd::OnSysCommand(nID, lParam);
}
LRESULT CChildFrame::OnEnterSizeMove(UINT wParam, LPARAM lParam)
{
if(m_bIsSizing)
{
TRACE("CChildFrame::OnEnterSizeMove\n");
}
return 0; //don't flag we processed the message
}
LRESULT CChildFrame::OnExitSizeMove(UINT wParam, LPARAM lParam)
{
if(m_bIsSizing)
{
TRACE("CChildFrame::OnExitSizeMove\n");
m_bIsSizing = FALSE; // set to false before calling OnSizing
CRect dlgRect;
pView->GetClientRect(dlgRect);
pView->InvalidateRect(NULL, FALSE);
pView->SendMessage(WM_SIZE, WPARAM(SIZE_RESTORED), MAKELONG(dlgRect.Width(), dlgRect.Height()));
}
return 0; //don't flag we processed the message
}
Then within your View code, you can check if the Frame is sizing within your OnSize handler.
void CMyView::OnSize(UINT nType, int cx, int cy)
{
CChildFrame* pFrame=(CChildFrame*)GetParentFrame();
if(!pFrame->m_bIsSizing) {
CWaitCursor cur;
//DO UPDATE CODE
}
}

need some help in making slider control

i have made the slider control in c++ using MFC. there is my code.
void CImageAnalyserDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
if(nSBCode == SB_THUMBPOSITION)
{
slidervalue.Format(_T("%d"), nPos);
UpdateData(false);
}
else
{
CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
}
}
every thing is done, i just wanna know where should i write the implementaion of slider control, i mean where should i write this
if(slidervalue="10")
{
//do something
}
Why would you want to put the slider position into a string and compare it at some other place in your code?
In the OnHScroll handler, you already got the slider position. Do whatever you want to do in that function, or call some other function from the handler.
You can add an integer variable 'slidervalue' to your slider and set its max and min values to 100 and 0 respectively. Instead of reading the nPos parameter you can read this variable easily.
void CImageAnalyserDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
UpdateData(TRUE);
if(slidervalue==10)
{
//do something
}
}
Hope this helps!

MFC OnEnChange handler function - infinite loop

(I'm using VS++2005)
I put edit box control (with ID - ID_edit_box) on my dialog, and associate (with handler wizard) two varibles for it: control (c_editbox) and value (v_editbox) variable. Also I associate handler function OnEnChangeedit_box with that edit box control. Suppose that we may enter just one digit in edit box, and that digit can be 0 or 1. If we enter some other value - what I want is that content of that edit box is automaticaly cleared, so user can't see that he type anything (in other words user can not enter anything except 0/1 in edit box). I do that check in onEnChangeedit_box function. Here is the code:
void CSDRDlg::OnEnChangeedit_box()
{
CWnd* pWnd;
CString edit_box_temp;
pWnd = GetDlgItem(ID_edit_box);
pWnd->GetWindowText(edit_box_temp);
if ((edit_box_temp == "0" || edit_box_temp == "1")
{...do something - i.e. setfocus on some other edit box }
else
{
pWnd->SetWindowText(""); // clear the content of edit box
//... any other statement below will not be executed because the
//above line cause again call of this function
}
}
I debug and discover that line: pWnd->SetWindowText(""); cause an infinite loop because we change control content in this function which triggers again her call.
But I change above code like this:
void CSDRDlg::OnEnChangeedit_box()
{
UpdateData(TRUE);
if ((v_editbox == "0" || v_editbox== "1")
{...do something - i.e. setfocus on some other edit box }
else
{
v_editbox = "";
UpdateData(FALSE);
}
}
and that works what I want but can someone explain to me why when we call
v_editbox = "";
UpdateData(FALSE);
that doesn't cause an infinite loop.
Why don't you Set Min/Max value to 0/1 or Set Value Type as bool when you Add Variable for your EditBox
You should probably do this by subclassing CEdit and filter out invalid characters. eg:
class CSDREdit public CEdit
{
afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CSDREdit, CEdit)
ON_WM_CHAR()
END_MESSAGE_MAP()
void CSDREdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if (nChar != _T('0') && nChar != _T('1'))
return;
CEdit::OnChar(nChar, nRepCnt, nFlags);
}