MFC-ActiveX is not refreshed/repainted automatically - events

I have a MFC-based ActiveX control, where some important things do not work. A size-event is never called and the controls contents are redrawn only when I click the border of the control (in ActiveX test container).
That's my code for the size-event in header file:
public:
afx_msg void OnPaint();
...and in source file:
BEGIN_MESSAGE_MAP(CBeamConstruXCtrl, COleControl)
ON_WM_SIZE()
ON_WM_PAINT()
ON_OLEVERB(AFX_IDS_VERB_PROPERTIES, OnProperties)
END_MESSAGE_MAP()
void MyCtrl::OnPaint()
{
//this is never called also when I change the size of the control in test container
}
Any ideas what is missing here?
Edit: just a clarification: OnSize() is called once in initialisation phase of the OCX, but never when I change the controls size.

As mentioned by Roger Rowland: OnDraw() does the trick.

Related

Custom draw ListView Items without border per how TListView itself does it

In a custom control, I custom-draw TListView items myself in the TListView.OnAdvancedCustomDrawItem event. This works well.
I have been experimenting with several Theme classes and parts. For instance, when I use:
HTHEME Theme = OpenThemeData(Handle, L"Explorer::ListView") ;
DrawThemeBackground (Theme, Sender->Canvas->Handle, LVP_LISTITEM, LISS_NORMAL, &ItemRect, NULL);
I get what is to be expected, per Theme explorer (do notice the borders around the item):
But, if I look at a proper VCL TListView object, similar items are painted without a border. I expected this control to use the same Theme class and part. Is that not the case? If so, what class/part should I use to mimic the behavior?
This is what I see (notice the borders in the custom control vs the absence of borders in the true TListView control just below it:
I'm actually getting a 'nicer' result with LVP_GROUPHEADER, but I'm still very curious about LVP_LISTITEM.
FYI, using LVP_GROUPHEADER instead of LVP_LISTITEM. It works well for this type control, so a nice alternative, but I'm still very curious as to why the real ListView control paints no borders using LVP_LISTITEM (I THINK).
Perhaps I should add that I still use old C++ Builder 2009 for this project. This is a low effort attempt to improve the control to give it a life beyond W10 (Especially W11 where current MENU / MENU_POPUPITEM choice is not kind on the eyes). PS. having more problems with TPopupMenu now .. but that's another topic I guess.
A simple code example (not same as in project but showing the border):
#include "uxtheme.h"
#include "Vsstyle.h"
// Project includes uxtheme.lib
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
HTHEME Theme = OpenThemeData(Handle, L"Explorer::ListView") ;
TRect ItemRect = Image1->ClientRect ;
DrawThemeBackground (Theme, Image1->Canvas->Handle, LVP_LISTITEM, LISS_NORMAL, (tagRECT*)&ItemRect, NULL);
CloseThemeData(Theme) ;
}
//---------------------------------------------------------------------------

CDockablePane and Accelerators

In my MFC MDI application, I have CDockablePanes.
In CDockablePane's I have edit control and listcontrol.
For example, if the user is typing text in an edit control in the app, and presses the delete key, instead of deleting a character like normal, it sends the ID_EDIT_DELETE command to active view, causing the selected objects to be deleted.
How can I fix this?
I think I need to override PreTranslateMessage, and check what window has focus before passing it on, but I really don't know what to do in PreTranslateMessage.
I overrided the PreTranslatemessage function in CDockablePane derived class and added below code and it is working for me.
BOOL CMyDockablePane::PreTranslateMessage(MSG* pMsg)
{
if(IsDialogMessage(pMsg))
return true;
return CDockablePane::PreTranslateMessage(pMsg);
}

Change color of background and title in MFC Control

I want to change text color and background color for my EDIT CONTROL, STATIC CONTROL and BUTTON CONTROL in MFC application. The control is in a CDialogEx dialogue .
I try to add the OnCtlColor (with wizard in visual studio, on the WM_CTLCOLR message) but I can't set the color of may static control and button control.
I put also a break point in the OnCtlColor function (in the IF construct), but I don't receive anything.
I also tried to use the SetTextColor function retrieving the handle of the control from GetDlgItem, but I can't change the color as I want.
Pleas help me.
I can assert that I tried to use in OnCtlColor in a CDialog and it worked for the static and for the edit controls.
All you have to do is:
For changing background color, you need to create a brush that still exists outside that function and return its HBRUSH with
return (HBRUSH) m_brush.GetSafeHandle();
So you have to make a variable (m_brush in this code) that is member or a static (I recommend the first), and in the dialog initialization you have to create the brush you want.
I thought maybe some controls will not work with this, and for those I also did
pDC->SetBkColor(RGB(0,0,255));
But seems to do nothing; it is in the code for safety.
For changing the text color,I did
pDC->SetTextColor(RGB(255,0,0));
These experiences worked well for edits and statics, but did not work at all for groupboxes!
Groupboxes are a strange entity in MFC, some kind of a platyplus: they are a CButton with the BS_GROUPBOX, but in this function, its nCtlColor is CTLCOLOR_STATIC instead of CTLCOLOR_BTN! I did this for them
UINT nStyle = (UINT)(pWnd->GetStyle() & 0x0F);
if(nStyle == BS_GROUPBOX)
{
return (HBRUSH) m_brush2.GetSafeHandle();
}
and what got painted was the little rectangle behind the groupbox title!
I could not get the text colour of groupboxes changed!
If you have groupboxes and it is really important to change their titles' text color, you can get the one from http://www.codeproject.com/Articles/29016/XGroupBox-an-MFC-groupbox-control-to-display-text and get its essential code parts: to be derived from CStatic, the OnPaint() and DrawItem() methods. Do not forget also the ON_WM_PAINT() on the message map. I don't know if the OnEraseBkgnd() and its is ON_WM_ERASEBKGND() message mapping are so essential.
It is also needed to change them to be Static text controls in the resources, declare a XGroupBox variable and do a DDX_Control of it.
I tested it and it really works.
For buttons, with CButtons it did not work. But, for each button, I simply declared a CMFCButton variable in the class and did a DDX_Control of each one. After, I had two choices:
Set its m_bTransparent property to TRUE in the form constructor (search this variable on afxbutton.cpp file for reference) for the ones I wanted to have the same color as the form (I also painted the form; in my case I was implementing themes on an application)
Set the Background color with SetFaceColor() and set the Text Color with SetTextColor() in form initialization.
When the CMFCButton does not have these things set, it got its color from theme blending of the currently selected CMFCVisualManager.
Note: I also replaced my CSpinButton entities with CMFCSpinButon ones, because I wanted colors from the selected theme.
In the OnCtlColor, the nCtlColor variable is important because it will allow you to personalize different colors to different types, without testing dynamic_cast success or failure for every control.
Do not forget to add ON_WM_CTLCOLOR() to your message map.
UPDATE 1:
After following the advice of the accepted answer on http://social.msdn.microsoft.com/Forums/vstudio/en-US/53f47162-078a-418f-8067-ee61a81ceeac/checkbox-transparent-color-not-working-in-vs2008?forum=vcgeneral , I did my own Groupbox class, and now it is like:
class CMyGroupBox: public CButton
{
protected:
virtual void PreSubclassWindow()
{
SetWindowTheme(*this, _T(""), _T(""));
#pragma comment(lib, "UxTheme.lib")
}
};
I just declared one of this, and did DDX_Control with its respective control ID, and now I can see the text in the color I supplied to SetTextColor. If you return a HBRUSH for this control, what gets painted is a non-filled rectangle drawn around the groupbox's title.
UPDATE 2: I just generalized the CMyGroupBox to be CMyButton, for using its PreSubClassWindow method not only in groupboxes, but also in checkboxes and buttons. In checkboxes it works well, in buttons, I am not so satisfied with the results.
UPDATE 3: I was trying to remove some weird effect on the rendering of the text and I just commented the pDC->SetBkColor(RGB(0,0,255)); line; the result was an ugly while rectangle behind the text :( . Then I replaced it with pDC->SetBkMode(TRANSPARENT); and I also see tht weird effect :(
UPDATE 4: In order to avoid to have to declare all my checkboxes, groupboxes and buttons as the class that contains the PreSubClassWindow method, I researched and discovered that it is not needed to do it. The code
SetThemeAppProperties(0);
#pragma comment(lib, "UxTheme.lib")
AfxGetMainWnd()->SendMessage(WM_THEMECHANGED, 0U, 0L);
disables theming for all controls at the whole application level.

Owner-drawn CTabCtrl in WTL

WTL/WIN32 newbie here, struggling to understand how messages are passed around.
I'm trying to write an owner-drawn CTabCtrl in WTL. For some (at least to me) incomprehensible reason, WM_DRAWITEM is sent to the parent window, not to the window that actually needs to know. Which makes it difficult to make a nice, self-contained GUI class to simply replace CTabCtrl. I could always capture the message in the parent and pass it on to the tab control, but that would be poor OO design. Is there a way to intercept the message, without requiring extra re-routing code in the owner/parent class?
EDIT: After a bit of googling, I now have
class CQueryTabCtrl :
public CWindowImpl<CQueryTabCtrl, CTabCtrl>,
public COwnerDraw<CQueryTabCtrl>
{
public:
DECLARE_WND_SUPERCLASS(NULL, CTabCtrl::GetWndClassName())
BEGIN_MSG_MAP(CQueryTabCtrl)
CHAIN_MSG_MAP(COwnerDraw<CQueryTabCtrl>)
DEFAULT_REFLECTION_HANDLER()
END_MSG_MAP()
BOOL PreTranslateMessage(MSG* pMsg)
{
pMsg;
return FALSE;
}
void DeleteItem(LPDELETEITEMSTRUCT /*lpDeleteItemStruct*/)
{
}
void DrawItem ( LPDRAWITEMSTRUCT lpdis )
{
CDCHandle dc = lpdis->hDC;
CDC dcMem;
dcMem.CreateCompatibleDC ( dc );
dc.SaveDC();
dcMem.SaveDC();
dc.FillSolidRect ( &lpdis->rcItem, RGB(255,0,0) );
dcMem.RestoreDC(-1);
dc.RestoreDC(-1);
}
};
Which is obviously utterly wrong, since DrawItem() is never called.
WM_DRAWITEM is sent to parent by design.
Sent to the parent window of an owner-drawn button, combo box, list box, or menu when a visual aspect of the button, combo box, list box, or menu has changed.
You handle it on hosting window, and with WTL you might leverage COwnerDraw class on it and/or reflect messages there so that they are sent back to the window where your subclassing WindowProc would handle them as you perhaps originally planned.
This answer is a bit late to the party, but might help others...
There's no way to directly achieve reflected messages w/o extra routing code, because that's how Window's window messaging works, as Roman points out.
ATL however has mechanisms to reflect messages onto child windows, which at least helps in keeping self-written code to a minimum.
1) Opt-In to reflection
This requires an extra step in your parent window, in order to tell it to reflect messages from child windows onto them, using the REFLECT_NOTIFICATIONS() macro:
// Just a made-up dialog class for outlining message reflection installed on the parent window
class SomeDialog : public CDialogImpl<SomeDialog, CWindow>
{
public:
BEGIN_MSG_MAP_EX(SomeDialog)
REFLECT_NOTIFICATIONS()
END_MSG_MAP()
};
2) Handle reflected (owner-draw) messages
Because your control will receive reflected messages, and the COwnerDraw mixin provides an alternative message map for those, you simply chain to that message map, using the CHAIN_MSG_MAP_ALT() macro.
class CQueryTabCtrl :
public CWindowImpl<CQueryTabCtrl, CTabCtrl>,
public COwnerDraw<CQueryTabCtrl>
{
public:
DECLARE_WND_SUPERCLASS(NULL, CTabCtrl::GetWndClassName())
BEGIN_MSG_MAP(CQueryTabCtrl)
CHAIN_MSG_MAP_ALT(COwnerDraw<CQueryTabCtrl>, 1)
END_MSG_MAP()
void DrawItem(LPDRAWITEMSTRUCT)
{
// ...
}
};
Also see a complete owner-drawn tablist control from the wtlext repository, which can serve as an example (Dislaimer: I am associated with the author FireDaemon Technologies Ltd).

Hiding a control in Windows

I can't figure out how to hide a child window (a control), more specifically a GroupBox and a PushButton. I thought ShowWindow() with SW_HIDE as the second parameter would do the job, but it simply doesn't work. Yet SW_SHOW works just fine. I have the correct window handle for both controls, so that's not the issue.
I googled and all I could find was people asking how to hide dialogs, not controls. Either that or MFC-based applications, which doesn't apply here.
I'm using pure Windows API, no MFC.
What am I getting wrong?
EDIT: More info: I'm writing some simple class wrappers for WinApi controls. The WindowsControl class has, along other methods, the following methods for showing and hiding the Control:
void Show() {
ShowWindow(this->_hWnd,SW_SHOWNOACTIVATE);
}
void Hide() {
ShowWindow(this->_hWnd,SW_HIDE);
}
Every control inherits from WindowsControl.
This image has the window layout so you understand exactly what I'm doing: http://i.stack.imgur.com/PHQnH.png
When the user clicks inside the "Chipset" Static control, it'll load information for a given Tile (which is stored in an array, but that's irrelevant). Depending on the setting, it'll hide the "Edit bitwall" button on the left and show the empty GroupBox behind it or viceversa.
Just to be clear this isn't something wrong with my windows api wrappers, I am getting the correct HWND. Though ShowWindow might not be able to be called from a Window Procedure that isn't the parent's (that'd be weird).
EDIT2: Using C++ with Visual Studio 2008, no MFC, no WTL, no CLR, no .NET
EDIT3: I'll post even more code so it's easier
Inside the static's window procedure, I handle WN_LBUTTONDOWN like this:
case WM_LBUTTONDOWN: {
...
update_tiledata(c, l)
void update_tiledata(GroupBox * c, ListView* l ) {
...
if (chp_copy.Tiles[selectedTile].Pass() == PT_BITWALL) {
c->Controls(CTL_BTNEDITBIT)->Show();
c->Controls(CTL_FRPHOLD)->Hide();
} else {
c->Controls(CTL_FRPHOLD)->Show();
c->Controls(CTL_BTNEDITBIT)->Hide();
}
update_edits();
}
The ommited code does nothing to affect the classes, as I said before, ShowWindow with SW_HIDE IS getting called, with the correct HWND, but nothing is happening.
A control in a Window or dialog can be hidden using
ShowWindow(hControlWin, SW_HIDE);
In a dialog you can retrive the controls window handle by calling
GetDlgItem(hDlg, < CtrlID >);
Typically you write something like:
ShowWindow(GetDlgItem(hDlg, 2), SW_HIDE);
It would be helpful if you give more information and some code: How did you create the controls? What language, compile and framework did you use?
I think the function call you want is EnableWindow I have used this before to disable a button on a form. You will need to get an handle to the Window (object) first though so you might want to use EnumChildWindows to iterate through all the controls to find the one you want.

Resources