Set CSliderCtrl to move in 10 unit intervals - mfc

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;
}
}

Related

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

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)
}

CMFCListCtrl force selected item to have red color

I have my own CMFCListCtrl derived class where I implemented the
virtual COLORREF OnGetCellTextColor(int nRow, int nColum)
{
CMyClass* pMyClass = (CMyClass*)GetItemData(nRow);
if (pMyClass && pMyClass->m_bDeleted)
return RGB(255, 0, 0);
return __super::OnGetCellTextColor(nRow, nColum);
}
function for marking red the deleted entries. This works except when a item is selected.
I went to the void CMFCListCtrl::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult) function and placed a breakpoint with the condition iRow==selected item on the line
lplvcd->clrText = OnGetCellTextColor(iRow, iColumn);
executed it, then I created a new Data Breakpoint for &lplvcd->clrText.
The Data Breakpoint got hit on function
comctl32.dll!SHThemeComputeTextColors()
, as the callstack image shows:
, which is clearly overriding the variable value.
As I search for SHThemeComputeTextColors on Internet and nothing appears, can somebody help me on forcing the selected items text to be red?
Changing the colors and fonts of highlighted items is trickier than it seems, because highlighting selected items is a system-wide issue. In fact, it was something I had delayed for a long time, and just today I've finally tackled...
There are at least two ways to change the looks of a list control: owner draw (you have to do all the drawing yourself) and custom draw (the system tells you when it is about to do some steps of the drawing and lets you change the color, the font, etc.). This answer is about custom draw. This article covers the basics of using Custom Draw with CListCtrl.
How to change the highlight color in a CListCtrl (not a CMFCListCtrl, we'll get to that soon) is explained in this other article. These are the steps you have to do:
Intercept the listview draw routine just before it is about to draw a highlighted row (item).
Turn off the row highlight.
Set the row colors to whatever you want.
Let the listview draw the row.
Intercept the listview draw routine after it has drawn the row (post-draw item).
Turn this row's highlighting back on.
So, when the listview is about to draw a highlighted row, you have to telll the system the row is not highlighted so it doesn't use the system colors to paint it, tell what color to use, and set the item as highlighted again, so the selection works as usual.
Here is the code from that article:
COLORREF g_MyClrFgHi; // My foreground hilite color
COLORREF g_MyClrBgHi; // My background hilite color
HWND g_hListView; // Window handle of listview control
void EnableHighlighting(HWND hWnd, int row, bool bHighlight)
{
ListView_SetItemState(hWnd, row, bHighlight? 0xff: 0, LVIS_SELECTED);
}
bool IsRowSelected(HWND hWnd, int row)
{
return ListView_GetItemState(hWnd, row, LVIS_SELECTED) != 0;
}
bool IsRowHighlighted(HWND hWnd, int row)
{
// We check if row is selected.
// We also check if window has focus. This was because the original listview
// control I created did not have style LVS_SHOWSELALWAYS. So if the listview
// does not have focus, then there is no highlighting.
return IsRowSelected(hWnd, row) && (::GetFocus(hWnd) == hWnd);
}
BOOL OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
static bool bIsHighlighted = false;
*pResult = 0;
NMHDR *p = (NMHDR *)lParam;
switch (p->code)
{
...
case NM_CUSTOMDRAW:
NMLVCUSTOMDRAW *lvcd = (NMLVCUSTOMDRAW *)p;
NMCUSTOMDRAW &nmcd = lvcd->nmcd;
switch (nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
// We want item prepaint notifications, so...
*pResult = CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT:
{
int iRow = (int)nmcd.dwItemSpec;
bHighlighted = IsRowHighlighted(g_hListView, iRow);
if (bHighlighted)
{
lvcd->clrText = g_MyClrFgHi; // Use my foreground hilite color
lvcd->clrTextBk = g_MyClrBgHi; // Use my background hilite color
// Turn off listview highlight otherwise it uses the system colors!
EnableHighlighting(g_hListView, iRow, false);
}
// We want item post-paint notifications, so...
*pResult = CDRF_DODEFAULT | CDRF_NOTIFYPOSTPAINT;
break;
}
case CDDS_ITEMPOSTPAINT:
{
if (bHighlighted)
{
int iRow = (int)nmcd.dwItemSpec;
// Turn listview control's highlighting back on now that we have
// drawn the row in the colors we want.
EnableHighlighting(g_hListView, iRow, true);
}
*pResult = CDRF_DODEFAULT;
break;
}
default:
*pResult = CDRF_DODEFAULT;
break;
}
break;
...
}
}
This works fine with a CListCtrl, but you are asking about a CMFCListCtrl. The problem is that CMFCListCtrl is already asking to be notified about NM_CUSTOMDRAW notifications (that's when it calls the OnGetCellTextColor function, as you've seen). If you create a handler for that in your own CMFCListCtrl-derived class, those notifications won't get to CMFCListCtrl and you lose that functionality (it may fine, depending on your needs).
So here is what I've done: I've created a NM_CUSTOMDRAW handler in my list control and, if I'm dealing with a highlighted row, I change the colors, otherwise, I call CMFCListCtrl::OnNMCustomDraw(). As you'll see, I just use the normal highlight colors; that's because I just wanted to see the selected items even when the control doesn't have the focus:
void CMyListCtrl::OnNMCustomdraw(NMHDR* pNMHDR, LRESULT* pResult)
{
bool callParent = true;
static bool bHighlighted = false;
LPNMLVCUSTOMDRAW lpLVCustomDraw = reinterpret_cast<LPNMLVCUSTOMDRAW>(pNMHDR);
NMCUSTOMDRAW nmcd = lpLVCustomDraw->nmcd;
*pResult = CDRF_DODEFAULT;
switch (lpLVCustomDraw->nmcd.dwDrawStage)
{
case CDDS_PREPAINT:
*pResult = CDRF_NOTIFYITEMDRAW;
break;
case CDDS_ITEMPREPAINT:
{
int row = nmcd.dwItemSpec;
bHighlighted = IsRowHighlighted(row);
if (bHighlighted)
{
lpLVCustomDraw->clrText = GetSysColor(COLOR_HIGHLIGHTTEXT);
lpLVCustomDraw->clrTextBk = GetSysColor(COLOR_HIGHLIGHT);
EnableHighlighting(row, false);
*pResult = CDRF_DODEFAULT | CDRF_NOTIFYPOSTPAINT;
callParent = false;
}
}
break;
case CDDS_ITEMPOSTPAINT:
if (bHighlighted)
{
int row = nmcd.dwItemSpec;
EnableHighlighting(row, true);
callParent = false;
}
*pResult = CDRF_DODEFAULT;
break;
default:
break;
}
if (callParent)
{
__super ::OnCustomDraw(pNMHDR, pResult);
}
}
bool CMyListCtrl::IsRowHighlighted(int row)
{
bool selected = GetItemState(row, LVIS_SELECTED) != 0;
return selected;
}
void CMyListCtrl::EnableHighlighting(int row, bool enable)
{
SetItemState(row, enable ? 0xff : 0, LVIS_SELECTED);
}
I haven't tested it thoroughly, but it seems to work.
UPDATE:
There is a little problem. When you call EnableHigilighting(), the dialog will get LVN_ITEMCHANGED notifications, which can trigger a redraw, which will trigger a LVN_ITEMCHANGED notification... So if you are listening to LVN_ITEMCHANGED notifications, you might need to do something like this:
void CMyListCtrl::EnableHighlighting(int row, bool enable)
{
m_internalStateChange = true;
SetItemState(row, enable ? 0xff : 0, LVIS_SELECTED);
m_internalStateChange = false;
}
void CWhateverDialog::OnLvnItemchangedListaEjesPane(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
if (!c_List.InternalStateChange() && /* other conditions */)
{
// Respond to state changes
}
}

C++ Animating a button with MoveWindow()

I am new to C++ and seem to be stuck. I basically have a Window and a Button inside it(also created with CreateWindow()). I want to have the button moving on the X axis, I tried doing with MoveWindow() but I cant seem to do the animation effect. I tried writing it in a for loop but I have not found what to use to delay the animation. I would appreciate any help.
for (int i = 0; i < 50; i++) {
MoveWindow(g_MovingDot, i, ButtonTop, ButtonWidth, ButtonHeight, true);
//Delay it somehow
}
Would this be the right way to do it? I just want the button to move slowly to the right.
Use the SetTimer function to be notified when the time-out value is elapse.
Implement a function with the signature
VOID (CALLBACK* TIMERPROC)(HWND, UINT, UINT_PTR, DWORD);
and pass a function pointer to SetTimer, to register a callback procedure .
Use a global control variable, which controls the animation of the window. In the following code snippet the control variable is named i_g.
When the timer is elapsed, then the callback procedure is called. Increment the control variable, slightly move the dialog element and restart the timer, until the final position is reached.
The animation time is controlled by the number of animation steps and the time interval of a single step.
int g_i=0;
void CALLBACK BtnTimer( HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime )
{
KillTimer( hwnd, idEvent );
if ( g_i < 50 )
{
MoveWindow(g_MovingDot, i, ButtonTop, ButtonWidth, ButtonHeight, true);
g_i ++;
SetTimer( hwnd, idEvent, 100 /* time milliseconds */, &BtnTimer );
}
}
void AnimateButton( HWND hDialogWnd //* HWND from Dialog */)
{
g_i = 0;
SetTimer( hDialogWnd, 0 /* idEvent */, 100 /* time milliseconds */, &BtnTimer );
}

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);
}