CTabCtrl SetItemRect? - mfc

I want to change the size of the tabs.
We have added a closing cross to our tabs, but it conflicts spaciously with the text of the tab.
So far I have realized the following:
GetItemRect(int i, RECT* rc) gives me the rect. What I really would like is a SetItemRect.
SetItem cannot be used as the item does not contain its size. It is calculated based upon the contents I give it.
I could add a space char in the end of the string, but that's just against the natural order of things. I will not tweak pixels with CStrings.
SetSize is supposed to set the size of a tab (all tabs?). But I cannot find out where to put it that does not trigger a redraw, which sparks an infinite loop if I put it with the WM_PAINT case.
This is where I custom draw the contents of the tab, but I cannot resize them here:
LRESULT CSkinnedTabCtrl::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_PAINT: {
...
CPaintDC dc(this);
INT nCount = GetItemCount();
for (INT i = 0; i < nCount; i++) {
CRect rc;
GetItemRect(i, rc);
DrawItem(dc, i, rc);
}
return TRUE;
}
Where do I set the size of the tabs, and how?

IIRC you need to overwrite WM_NCCALCSIZE message.

Related

Combobox selection loading previous selection

I have a combobox containing "1,2,3,4,5". When I make a selection in my combobox, the previous selection is loaded. For example, if the value in the combobox is "1" and I select "2", "1" is loaded into my variable nApplication and "2" will be displayed on my combobox. If i want to load "2", I need to change the selection one more time. Example change from "2" to "3". How do I make it load the number I select? My code looks like this.
void CAppDlg::DoDataExchange(CDataExchange* pDX)
{
DDX_Text(pDX, IDC_Application, nApplication);
.
.
void CAppDlg::OnCbnSelchangeapplication()
{
UpdateData(TRUE);
int j = nApplication-1;
for(int i=0; i<MAX_LABEL; i++){
bool bShow = i < arrLabel[j];
((CButton *)GetDlgItem(IDC_Label1+i))->ShowWindow(bShow);
}
}
Any help would be appreciated. Thanks.
As explained in my comments, I suspect that the contents of the combo-box are updated after the CBN_SELCHANGE message is processed, and that's what causing you problems. Therefore a workaround could be post a custom (application-defined) message, to be processed later.
The type of the function must be afx_msg LRESULT (CWnd::*)(WPARAM, LPARAM) (check the documentation).
So, in your class declaration, add:
public:
afx_msg LRESULT OnComboSelChanged(WPARAM wParam, LPARAM lParam);
In your message-map:
ON_MESSAGE(WM_APP+100, OnComboSelChanged)
And finally the implementation:
void CAppDlg::OnCbnSelchangeapplication()
{
// Place a message in the message queue
PostMessage(WM_APP+100);
}
LRESULT CAppDlg::OnComboSelChanged(WPARAM wParam, LPARAM lParam)
{
UpdateData(TRUE);
int j = nApplication-1;
// I have simplified your code a little
for(int i=0; i<MAX_LABEL; i++)
GetDlgItem(IDC_Label1+i)->ShowWindow(i < arrLabel[j]);
return 0L;
}
However, all this, ie calling UpdateData(TRUE);, get the string there, convert it to int and take actions depending on its value, may just be an overkill, esp if the combo-box contains just a contiguous number-range (1..5 in your case). UpdateData(TRUE); transfers the values in ALL your controls to variables, and this may just not be needed. It seems to be a mechanism controlling the appearance of other controls, it's not really "data-entry" (I guess the combod-box is a non-editable drop-down list). So, you could just not use DDX/DDV at all, instead check the selected item yourself. It's a zero-based index. Here is the code:
void CAppDlg::OnCbnSelchangeapplication()
{
// Get selected item
int j = ((CComboBox *)GetDlgItem(DC_Application))->GetCurSel();
if (j == CB_ERR) return;
for(int i=0; i<MAX_LABEL; i++)
GetDlgItem(IDC_Label1+i)->ShowWindow(i < arrLabel[j]);
}
Please note that the code fragments above have not been tested, it's just what I would do.

Display formatted text on selecting item in the Combobox

I have a combobox in that I want to display different string on selecting an item in Combo.
My combo box is a dropdown combobox.
For eg: I have following in my combobox.
Alex - Manager
Rain - Project Lead
Shiney - Engineer
Meera - Senior Engineer
OnSelecting an item in combobox I want to diaply only name i.e. Alex.
I tried below code
struct details{
CString name;
CString des;
};
BOOL CComboTestDlg::OnInitDialog()
{
CDialog::OnInitDialog();
details d1;
d1.name = _T("alex");
d1.des =_T("manager");
m_vec.push_back(d1);
details d2;
d2.name = _T("Rain");
d2.des =_T("Engineer");
m_vec.push_back(d2);
// TODO: Add extra initialization here
for(int i=0;i<m_vec.size();i++)
{
m_ctrlCombo.AddString(m_vec[i].name+m_vec[i].des);
m_ctrlCombo.SetItemData(i,(DWORD_PTR)&m_vec[i]);
}
m_ctrlCombo.SelectString(-1,m_vec[0].name);
m_ctrlCombo.SetWindowText(m_vec[0].name);
return TRUE; // return TRUE unless you set the focus to a control
}
void CComboTestDlg::OnCbnSelchangeCombo1()
{
int nItem = m_ctrlCombo.GetCurSel();
details* det = (details*)m_ctrlCombo.GetItemData(nItem);
PostMessage(SETCOMBOTEXT,IDC_COMBO1,(LPARAM)(LPCTSTR)det->name);
}
BOOL CComboTestDlg::PreTranslateMessage(MSG* pMsg)
{
MSG msg1=*pMsg;//I am loosing the value after checking ..so storing temp.
MSG msg;
CopyMemory(&msg, pMsg, sizeof(MSG));
HWND hWndParent = ::GetParent(msg.hwnd);
while (hWndParent && hWndParent != this->m_hWnd)
{
msg.hwnd = hWndParent;
hWndParent = ::GetParent(hWndParent);
}
if (pMsg->message==SETCOMBOTEXT && (pMsg->wParam == IDC_COMBO1))
SetDlgItemText(IDC_COMBO1, (LPCTSTR)pMsg->lParam);
if(pMsg->message==WM_KEYDOWN)
{
if(pMsg->wParam==VK_RETURN && msg.hwnd ==m_ctrlCombo.m_hWnd )
{
OnCbnSelchangeCombo1();
}
}
return CDialog::PreTranslateMessage(pMsg);
}
I am able to achieve my requirement OnComboSelChange() and Arrow Keys event but on pressing enter key after using arrow keys in combo box, it is not showing formatted text in combo box.
I think the most reliable and easy to implement solution is to subclass the edit control of the combobox. Intercept the WM_SETTEXT message and change the text as you like before forwarding it to the rest of the chain (finally the original window proc).
Install the sub class proc in OnInitDialog():
COMBOBOXINFO cbi{ sizeof(cbi) };
if( m_ctrlCombo.GetComboBoxInfo( &cbi ) )
{
SetWindowSubclass( cbi.hwndItem, ComboEditSubClassProc, 0, 0 );
}
ComboEditSubClassProc() could look like this:
LRESULT CALLBACK ComboEditSubClassProc( HWND hWnd, UINT uMsg, WPARAM wParam,
LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
switch( uMsg )
{
case WM_SETTEXT:
{
CString text = reinterpret_cast<LPCTSTR>( lParam );
// Extract the name (everything before "-").
CString name = text.SpanExcluding( _T("-") );
name.TrimRight();
// Forward the modified text to any other sub class procs, aswell
// as the original window proc at the end of the chain.
return DefSubclassProc( hWnd, uMsg, 0, reinterpret_cast<LPARAM>( name.GetString() ) );
}
case WM_NCDESTROY:
{
// We must remove our subclass before the subclassed window gets destroyed.
// This message is our last chance to do that.
RemoveWindowSubclass( hWnd, ComboEditSubClassProc, uIdSubclass );
break;
}
}
return DefSubclassProc( hWnd, uMsg, wParam, lParam );
}
Notes:
Contrary to my original solution of processing CBN_SELCHANGE, the current solution also works correctly if the combobox drop-down list is closed by pressing Return or is dismissed.
I think it is in general more reliable because we don't have to rely on the order of the notifications. The combobox has to finally call WM_SETTEXT to change the content of the edit control so this message will always be received.
There will also be no flickering as in the original solution where the text was first changed by the combobox and then modified by our code only after the fact.

Getting value from edit field (C++ WINAPI)

I am writing my first simple program in C++/WINAPI, with a lot of check boxes and a few edit fields, which will set up some calculations on a button press. All of my check boxes work/store info through individual cases, ie
switch (msg)
{
...
case WM_COMMAND:
{
switch (wParam)
{
case IDBC_BoxCheck1:
{...}
case IDBC_BoxCheck2:
{...}
...
} ...
...but I assumed edit fields didn't work as a case statement like a button press, since the value has to be read at the end once it has been changed as many times as the user wants. I looked online and attempted to use the SendMessage(hwnd, ...) and GetWindowText(hwnd, ...) functions to send a WM_GETTEXT command to the edit field and store it to a lpstr string, but I ran into the same problem with both of them - the hwnd for the edit fields aren't declared in the scope where the WM_GETTEXT command is being sent from, and I'm not sure how to get it there. Here is an overview of the structure being used in my program, which comes from a mix of some tutorials I was working with:
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_CREATE:
{
return OnCreate(hwnd, reinterpret_cast<CREATESTRUCT*>(lParam));
// OnCreate is a sub function that handles the creation of all the buttons/controls,
// since there are so many of them, with the format:
// HWND editControl1 = CreateControl(...); // CreateControl being another sub fnct
// that creates edit field ctrls
// editControl1 is the hwnd I'm trying
// to read the value from
// HWND checkControl1 = CreateButton(...); // Creates button ctrls, including ck box
...
}
...
case WM_COMMAND:
{
switch (wParam)
{
case IDBC_BoxCheck1: // These control IDs are defined at the top of the file
{
LPSTR Check1;
StoreInfo(Check1); // Just a sub fnct to store info for later calculations
}
case IDBC_BoxCheck2:
{
LPSTR Check2;
StoreInfo(Check2);
} // etc... there are 20 or so check boxes/buttons
case IDBC_Calculate:
{
LPSTR edit1;
GetWindowText(editControl1, edit1, 100); // or SendMessage(editControl1, ...)
// This kicks out the error of editControl1 not being declared in this scope
StoreInfo(edit1);
// Calculation function goes here
} ...
} ....
}
default: DefWindowProc(hwnd, msg, wParam, lParam);
}
}
IDBC_Calculate is the final button pressed before the calculations run. I figured the best place to read and store the values from the edit fields would be after this button is pressed, right before the calculation function is called, but tied to the same command. This is where the hwnd editControl1 is undefined, but I don't know how to send the definition to this scope, or where else I should be reading and storing the edit field values.
Any help or pointers on getting the values from these edit fields to my other functions would be appreciated! I've seen many different ways to check button states in various tutorials/lessons, so I'd love to know if there's a better way to do what I've written above in general.
Your edit fields have IDs right? Then you can use GetDlgItem.
editControl1 = GetDlgItem(hwnd, CONTROL_ID_1);
GetDlgItem is badly named, it doesn't just work in dialog boxes. It gets the handle of any child window from a parent window, using the ID of the child window.
And what Anders K says is correct. The way you are using GetWindowText will crash your program.

Accessing multiple Editboxes in MFC

I am writing a MFC program in which I have a a lot of Editboxes and I want to get all their text values and put them in a container. How can I achieve this without writing a line for each ID. I'm using this code for each ID:
CEdit *edit;
edit = reinterpret_cast<CEdit *>(GetDlgItem(IDC_NAME1));
But if I use that method I would have to write it 45 times. That doesn't seem right.
Is there a way of getting all the Editboxes in a container so I can use them that way or something like that?
You can certainly create an array (or other container) or pointers to CEdit: CEdit edits[45]; If the values of IDC_NAME1 through IDC_NAME45 are contiguous, you can just do something like:
for (int i=0; i<45; i++)
names[i] = reinterpret_cast<CEdit *>(GetDlgItem(IDC_NAME1 + i));
If those identifiers may not be contiguous, then you can put them in an array, and just index into that array as needed.
One caution: unless they're something like a grid of otherwise nearly identical edit controls, 45 on a screen may well be a bit much. If they are like a grid, you might want to look at one of the many available grid controls instead.
You do not have to use controls IDs.
Use EnumChildWindows and get test only for edit controls. Snippet follows.
Add following in dialog’s header:
afx_msg LRESULT OnFoundEdit(WPARAM wParam, LPARAM lParam);
And this to cpp:
#define WM_U_FOUND_EDIT WM_APP + 0x100
BEGIN_MESSAGE_MAP(CEditCtrlFishingDlg, CDialog)
ON_MESSAGE(WM_U_FOUND_EDIT, OnFoundEdit)
.
.
.
.
END_MESSAGE_MAP()
Write this line in the place you want to start edit text collection:
EnumChildWindows(m_hWnd, EnumChildProc, (LPARAM)m_hWnd);
Enum child procedure:
BOOL CALLBACK EnumChildProc(HWND hwnd, LPARAM lParam)
{
CString csBuffer;
LPTSTR pBuf = csBuffer.GetBufferSetLength(MAX_PATH);
GetClassName(hwnd, pBuf, MAX_PATH);
csBuffer.ReleaseBuffer();
if(!csBuffer.CompareNoCase(_T("edit")))
{
SendMessage((HWND)lParam, WM_U_FOUND_EDIT, 0, (LPARAM)hwnd);
}
return TRUE;
}
and the handler:
LRESULT YourDlg::OnFoundEdit(WPARAM wParam, LPARAM lParam)
{
CWnd *pWnd = FromHandle((HWND)lParam);
CString csTxt;
pWnd->GetWindowText(csTxt);
// do what you need with text here
return 0;
}

Hooking Win32 windows creation/resize/querying sizes

I'm trying to "stretch" an existing application.
The goal is to make an existing application become larger without changing the code of that application.
A cosntraint is that the stretched application will not "notice" it, so if the application query a created window size it'll see the original size and not the resized size.
I managed to resize the windows using SetWindowsHookEx:
HHOOK hMessHook = SetWindowsHookEx(WH_CBT,CBTProc, hInst, 0);
And:
LRESULT CALLBACK CBTProc( __in int nCode,
__in WPARAM wParam,
__in LPARAM lParam)
{
if (HCBT_CREATEWND == nCode)
{
CBT_CREATEWND *WndData = (CBT_CREATEWND*) lParam;
// Calculate newWidth and newHeight values...
WndData->lpcs->cx = newWidth;
WndData->lpcs->cy = newHeight;
}
CallNextHookEx(hMessHook, nCode, wParam, lParam);
}
The problem I'm facing is that I'm unable to the make the stretched application see the original sizes.
For example, if a .NET form is created:
public class SimpleForm : Form
{
public SimpleForm()
{
Width = 100;
Height = 200;
}
}
And later the size is queried:
void QuerySize(SimpleForm form)
{
int width = form.Width;
int height = form.Height;
}
I'd like width and height be 100 and 200 and not the resized values. I was unable to find the right hook which queries the size of an existing window.
What is the right way to hook window size querying?
Unfortunately, queries for window size aren't handled by messages -- they are direct API calls such as GetWindowRect -- so they can't be intercepted by standard Windows hooks. You might want to look into the Detours API, which allows you to hook arbitrary Win32 functions. (You can find a tutorial on Detours here)