Getting a disabled CView to give a strong visual cue of being disabled, so that the user understands that it isn't a bug that it's disabled.
-
Hello, I'm developing an MFC application, an enterprise database accessing program where the user is potentially denied access to some of the views that appear in the right hand splitter pane (all of which are classes derived from my own custom base class that is derived from CFormView). I call EnableWindow(FALSE) from within the right hand views where appropriate (from my custom base class) to disable the view entirely, including making it impossible to tab to the view. So far, so good. However, the problem of there being no obvious visual indication of *why* the view is locked, and that it is supposed to be locked, persists. Examples of appropriate strong visual cues include: 1. Darkening the View 2. Making the view grayscale 3. Making the mouse cursor appear as a padlock or somesuch when it overlays the view. 4. When the user clicks on the view, display a messagebox that explains that access is denied. How can I achieve any of these visual cues? The problem is, because I've disabled the view, through the call to EnableWindow(FALSE), I cannot do a whole lot. I cannot change the mouse cursor in the usual way. I can provide implementations of any Cview/CWnd virtual functions, such as OnDraw(), but I cannot seem to Draw to my CViews in any useful way, as can be done with a CScrollView. How can I create any of these strong visual cues? Regards, Sternocera
-
Hello, I'm developing an MFC application, an enterprise database accessing program where the user is potentially denied access to some of the views that appear in the right hand splitter pane (all of which are classes derived from my own custom base class that is derived from CFormView). I call EnableWindow(FALSE) from within the right hand views where appropriate (from my custom base class) to disable the view entirely, including making it impossible to tab to the view. So far, so good. However, the problem of there being no obvious visual indication of *why* the view is locked, and that it is supposed to be locked, persists. Examples of appropriate strong visual cues include: 1. Darkening the View 2. Making the view grayscale 3. Making the mouse cursor appear as a padlock or somesuch when it overlays the view. 4. When the user clicks on the view, display a messagebox that explains that access is denied. How can I achieve any of these visual cues? The problem is, because I've disabled the view, through the call to EnableWindow(FALSE), I cannot do a whole lot. I cannot change the mouse cursor in the usual way. I can provide implementations of any Cview/CWnd virtual functions, such as OnDraw(), but I cannot seem to Draw to my CViews in any useful way, as can be done with a CScrollView. How can I create any of these strong visual cues? Regards, Sternocera
You should still be able to change the cursor. Handle
WM_SETCURSOR
in the form class and return a different cursor if the form is disabled.--Mike-- New sig under construction...
-
You should still be able to change the cursor. Handle
WM_SETCURSOR
in the form class and return a different cursor if the form is disabled.--Mike-- New sig under construction...
Mike, Thanks for getting back to me. You're right - I made the mistake of not putting the message map entry in the derived class, just the base. Thanks, Sternocera
-
Hello, I'm developing an MFC application, an enterprise database accessing program where the user is potentially denied access to some of the views that appear in the right hand splitter pane (all of which are classes derived from my own custom base class that is derived from CFormView). I call EnableWindow(FALSE) from within the right hand views where appropriate (from my custom base class) to disable the view entirely, including making it impossible to tab to the view. So far, so good. However, the problem of there being no obvious visual indication of *why* the view is locked, and that it is supposed to be locked, persists. Examples of appropriate strong visual cues include: 1. Darkening the View 2. Making the view grayscale 3. Making the mouse cursor appear as a padlock or somesuch when it overlays the view. 4. When the user clicks on the view, display a messagebox that explains that access is denied. How can I achieve any of these visual cues? The problem is, because I've disabled the view, through the call to EnableWindow(FALSE), I cannot do a whole lot. I cannot change the mouse cursor in the usual way. I can provide implementations of any Cview/CWnd virtual functions, such as OnDraw(), but I cannot seem to Draw to my CViews in any useful way, as can be done with a CScrollView. How can I create any of these strong visual cues? Regards, Sternocera
How about overlaying the view with a 50% gray, 50% opaque window (see WS_EX_LAYERED[^] and SetLayeredWindowAttributes[^])? That's kind of what Safari (the web browser) does when you click on a bookmarked site that it has a preview for - looks kind of cool.
Java, Basic, who cares - it's all a bunch of tree-hugging hippy cr*p
-
Mike, Thanks for getting back to me. You're right - I made the mistake of not putting the message map entry in the derived class, just the base. Thanks, Sternocera
Hmm. It seems that the disabled CViews do not change cursor, only the enabled ones. Are you sure that it is as you've described? Regards, Sternocera
-
How about overlaying the view with a 50% gray, 50% opaque window (see WS_EX_LAYERED[^] and SetLayeredWindowAttributes[^])? That's kind of what Safari (the web browser) does when you click on a bookmarked site that it has a preview for - looks kind of cool.
Java, Basic, who cares - it's all a bunch of tree-hugging hippy cr*p
Stuart, That's interesting. How could this overlaying effect be achieved? calls to CWnd::SetLayeredWindowAttributes() don't seem to be affect my CView in anyway:
SetLayeredWindowAttributes(RGB(50,50,50), 100, WS_EX_LAYERED);
Equally, calling the Win32 API equivalents doesn't have any discernable effect:
HWND hwnd = GetSafeHwnd();
::SetWindowLong(hwnd, GWL_EXSTYLE,
::GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);::SetLayeredWindowAttributes(hwnd, 0, (255 * 70) / 100, LWA_ALPHA);
Regards, Sternocera
-
Stuart, That's interesting. How could this overlaying effect be achieved? calls to CWnd::SetLayeredWindowAttributes() don't seem to be affect my CView in anyway:
SetLayeredWindowAttributes(RGB(50,50,50), 100, WS_EX_LAYERED);
Equally, calling the Win32 API equivalents doesn't have any discernable effect:
HWND hwnd = GetSafeHwnd();
::SetWindowLong(hwnd, GWL_EXSTYLE,
::GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED);::SetLayeredWindowAttributes(hwnd, 0, (255 * 70) / 100, LWA_ALPHA);
Regards, Sternocera
Sorry - wasn't quite clear enough. Create another window. Make it the same size and position as your view, but put it on top of it. Make it layered, 50% gray, 50% opaque. The reason your trials didn't work is (I think) because you can't apply WS_EX_LAYERED to a child window - my own experiments (with a trial SDI app) indicate that. So, you'd need to set that style on your CMainFrame.
Java, Basic, who cares - it's all a bunch of tree-hugging hippy cr*p
-
Sorry - wasn't quite clear enough. Create another window. Make it the same size and position as your view, but put it on top of it. Make it layered, 50% gray, 50% opaque. The reason your trials didn't work is (I think) because you can't apply WS_EX_LAYERED to a child window - my own experiments (with a trial SDI app) indicate that. So, you'd need to set that style on your CMainFrame.
Java, Basic, who cares - it's all a bunch of tree-hugging hippy cr*p
Sorry Stuart, but I'm going to need you to be clearer still. What sort of window class would I create, and how? What about resizing of my CMainFrame, wouldn't I have to resize my overlaying windows as my underlying view changed shape? Regards, Sternocera
-
Sorry Stuart, but I'm going to need you to be clearer still. What sort of window class would I create, and how? What about resizing of my CMainFrame, wouldn't I have to resize my overlaying windows as my underlying view changed shape? Regards, Sternocera
Sternocera wrote:
Sorry Stuart, but I'm going to need you to be clearer still. What sort of window class would I create, and how?
This (Win32 code) seems to work nicely enough and shows how to register the window class and create the window:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// Don't let the window close - might want to modify slightly...
if (message == WM_CLOSE) return 0;
return DefWindowProc(hWnd, message, wParam, lParam);
}BOOL CreateLayeredWindow(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = &WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = 0;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_BTNTEXT+1);
wcex.lpszMenuName = 0;
wcex.lpszClassName = _T("layered");
wcex.hIconSm = 0;RegisterClassEx(&wcex);
const DWORD screenWidth = GetSystemMetrics(SM_CXSCREEN);
const DWORD screenHeight = GetSystemMetrics(SM_CYSCREEN);hWnd = CreateWindowEx(WS_EX_LAYERED|WS_EX_TOOLWINDOW, _T("layered"), _T("layered"), WS_POPUPWINDOW,
screenWidth/4, screenHeight/4, screenWidth/2, screenHeight/2, NULL, NULL, hInstance, NULL);if (!hWnd)
{
return FALSE;
}SetLayeredWindowAttributes(hWnd, 0, 0xc0, LWA_ALPHA);
ShowWindow(hWnd, nCmdShow);return TRUE;
}You will probably need to replace
hInstance
byAfxGetModuleState()->m_hCurrentInstanceHandle
and there're probably advantages to making the layered windows parent be one of your application's windows (mainly ensuring the window z-order is sane).Sternocera wrote:
What about resizing of my CMainFrame, wouldn't I have to resize my overlaying windows as my underlying view changed shape?
Yes - that's not too much of a problem - in your view's OnSize handler, just get the view's window rect (GetWindowRect) and tell the overlay to move itself there. You also need to handle the main frame's OnMove handler similarly - here's what I've got in a sample app:
// Main-frame OnMove handler
void CMainFrame::OnMove(int x, int y)
{
CFrameWndEx::OnMove(x, y);if (const MSG* msg = GetCurrentMessage())
{ -
Sternocera wrote:
Sorry Stuart, but I'm going to need you to be clearer still. What sort of window class would I create, and how?
This (Win32 code) seems to work nicely enough and shows how to register the window class and create the window:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// Don't let the window close - might want to modify slightly...
if (message == WM_CLOSE) return 0;
return DefWindowProc(hWnd, message, wParam, lParam);
}BOOL CreateLayeredWindow(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = &WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = 0;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_BTNTEXT+1);
wcex.lpszMenuName = 0;
wcex.lpszClassName = _T("layered");
wcex.hIconSm = 0;RegisterClassEx(&wcex);
const DWORD screenWidth = GetSystemMetrics(SM_CXSCREEN);
const DWORD screenHeight = GetSystemMetrics(SM_CYSCREEN);hWnd = CreateWindowEx(WS_EX_LAYERED|WS_EX_TOOLWINDOW, _T("layered"), _T("layered"), WS_POPUPWINDOW,
screenWidth/4, screenHeight/4, screenWidth/2, screenHeight/2, NULL, NULL, hInstance, NULL);if (!hWnd)
{
return FALSE;
}SetLayeredWindowAttributes(hWnd, 0, 0xc0, LWA_ALPHA);
ShowWindow(hWnd, nCmdShow);return TRUE;
}You will probably need to replace
hInstance
byAfxGetModuleState()->m_hCurrentInstanceHandle
and there're probably advantages to making the layered windows parent be one of your application's windows (mainly ensuring the window z-order is sane).Sternocera wrote:
What about resizing of my CMainFrame, wouldn't I have to resize my overlaying windows as my underlying view changed shape?
Yes - that's not too much of a problem - in your view's OnSize handler, just get the view's window rect (GetWindowRect) and tell the overlay to move itself there. You also need to handle the main frame's OnMove handler similarly - here's what I've got in a sample app:
// Main-frame OnMove handler
void CMainFrame::OnMove(int x, int y)
{
CFrameWndEx::OnMove(x, y);if (const MSG* msg = GetCurrentMessage())
{Thanks a lot for that Stuart. I'll see what I can come up with, Regards, Sternocera