How do I drag and drop something into a Static control? - mfc

How would I drag and drop something into a static control? It looks like I need to create a sub class of COleDropTarget and include that as a member variable in a custom CStatic. That doesn't appear to be working though. When I try and drag something onto the Static control I get the drop denied cursor.

The static control's m_hWnd must be valid when you call COleDropTarget::Register, which is why it doesn't work from within your CMyStatic constructor. What you can do is override CWnd::PreSubclassWindow within your CMyStatic class:
class CMyStatic : public CStatic {
...
virtual void PreSubclassWindow();
};
void CMyStatic::PreSubclassWindow()
{
CStatic::PreSubclassWindow();
m_MyDropTarget.Register(this);
}
There's a really good article here on CodeProject that you may find helpful.

In addition to the PreSubClassWindow() addition, you also have to set your CStatic control to have the Notify flag set in its resource parameters. Otherwise the control won't let the app know about mouse movements and hence not trigger the OnDragEnter() method.

Related

MFC: How to custom draw a dynamically created CListCtrl

I need to customize the head/row height of a CListCtrl. After some googling, I know that I need to subclass CListCtrl, so I wrote my own list class, with the outline as follows:
class CListCtrlCl : public CListCtrl
{
DECLARE_DYNAMIC(CListCtrlCl)
public:
...
BEGIN_MESSAGE_MAP(CListCtrlCl, CListCtrl)
ON_WM_MEASUREITEM()
ON_WM_MEASUREITEM_REFLECT()
END_MESSAGE_MAP()
void CListCtrlCl::PreSubclassWindow()
{
ModifyStyle(0,LVS_OWNERDRAWFIXED);
CListCtrl::PreSubclassWindow();
CHeaderCtrl *pHeader = GetHeaderCtrl();
m_Header.SubclassWindow(pHeader->GetSafeHwnd());
}
void CListCtrlCl::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
if (m_nRowHeight>0)
{
lpMeasureItemStruct->itemHeight = 100;
}
}
The problem is that this method worked if I drag a CListCtrl control in dialog template, but if I create this listctrl dynamically, like:
BOOL CListCtrlTestDlg::OnInitDialog()
{
CRect rect(7,7,300,300);
this->m_ListCtrl.Create(WS_CHILD|WS_VISIBLE|WS_VSCROLL|LVS_REPORT|LVS_ALIGNLEFT|WS_BORDER | WS_TABSTOP, rect, this,IDC_LIST1 + 1);
SetWindowLong(m_ListCtrl.m_hWnd ,GWL_EXSTYLE,WS_EX_CLIENTEDGE);
m_ListCtrl.SetExtendedStyle(LVS_EX_GRIDLINES);
::SendMessage(m_ListCtrl.m_hWnd, LVM_SETEXTENDEDLISTVIEWSTYLE,LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
...
Then the customization code just doesn't take effect. Whatever I did, the result listctrl is the standard one without any customization. But I need to create this listctrl dynamically, so could anyone tell me what I need to do to make dynamically created clistctrl customizable?
Thanks.
You've left off the necessary style for custom drawing when you create the control. Add LVS_OWNERDRAWFIXED. That should fix your problem.
The reason is that, PreSubclassWindow is only called when you subclass a control. When you createthe control you have also control over the style.
Simply overwrite the virtual Create function and just add the style like you are doing it in the PreSubclassWindow function. Than call the base class. You can also overwrite PreCreateWindow.
But much simpler than using the ownerdraw feature is cusum draw.

Changing default sort arrows of CMFCListCtrl

I found a problem. I can't change default sort arrows in header of CMFCListCtrl.
I found post on msdn about CMFCHeaderCtrl::OnDrawSortArrow but it didn't help because no examples there.
I tried simple method how I set arrows to CListCtrl header through the CimageList and HDITEM but those arrow sets only to left side because right side has already arrow default.
Google is empty on solutions how change default arrows on CMFCListCTrl.
Please help me)
Thanks!
PS. Please note that this is CMFCListCtrl not a CListCtrl where I can add arrows very easly.
Because CMFCHeaderCtrl is a member inside CMFCListCtrl you can not overwrite it.
Try to derive yor own CMFCListCtrl class, with your own CMFCHeaderCtrl class, that overwrites OnDrawSortHeader OnDrawSortArrow.
Overwrite CMFCListCtrl::InitHeader and subclass to your header control class.
If you start form scratch with a CListCtrl you can directly subclass the header control. The complete stuff inside CMFCListCtrl isn't so complicated and easy to reimplement. Depends what functionality you need.
The CMFCHeaderCtrl calls the currently active Visual Manager to do the actual drawing of sort arrows. It is easy to implement a customized Visual manager which overrides the arrow drawing method in the base class.
class CMyVisualManager:public CMFCVisualManagerOffice2007
{
virtual void OnDrawHeaderCtrlSortArrow(CMFCHeaderCtrl* pCtrl, CDC* pDC, CRect& rect, BOOL bIsUp);
};
void CMyVisualManager::OnDrawHeaderCtrlSortArrow(CMFCHeaderCtrl* pCtrl, CDC* pDC, CRect& rectArrow, BOOL bIsUp)
{
BOOL bDlgCtrl = pCtrl->IsDialogControl();
CPen penDark(PS_SOLID, 1, bDlgCtrl ? afxGlobalData.clrBtnDkShadow : afxGlobalData.clrBarDkShadow);
CPen* pPenOld = pDC->SelectObject(&penDark);;
ASSERT_VALID(pPenOld);
if (!bIsUp)
{
pDC->MoveTo(rectArrow.CenterPoint().x, rectArrow.bottom);
pDC->LineTo(rectArrow.CenterPoint().x, rectArrow.top);
pDC->MoveTo(rectArrow.CenterPoint().x-2, rectArrow.top+4);
pDC->LineTo(rectArrow.CenterPoint().x+3, rectArrow.top+4);
pDC->MoveTo(rectArrow.CenterPoint().x-1, rectArrow.top+3);
pDC->LineTo(rectArrow.CenterPoint().x+2, rectArrow.top+3);
pDC->MoveTo(rectArrow.CenterPoint().x-1, rectArrow.top+2);
pDC->LineTo(rectArrow.CenterPoint().x+2, rectArrow.top+2);
}
else
{
pDC->MoveTo(rectArrow.CenterPoint().x, rectArrow.top);
pDC->LineTo(rectArrow.CenterPoint().x, rectArrow.bottom);
pDC->MoveTo(rectArrow.CenterPoint().x-2, rectArrow.bottom-4);
pDC->LineTo(rectArrow.CenterPoint().x+3, rectArrow.bottom-4);
pDC->MoveTo(rectArrow.CenterPoint().x-1, rectArrow.bottom-3);
pDC->LineTo(rectArrow.CenterPoint().x+2, rectArrow.bottom-3);
pDC->MoveTo(rectArrow.CenterPoint().x-1, rectArrow.bottom-2);
pDC->LineTo(rectArrow.CenterPoint().x+2, rectArrow.bottom-2);
}
pDC->SelectObject(pPenOld);
}

CMFCButton::SetToolTip(str) not work in OnInitDialog() and

I want my CMFCButton to show tooltip when mouse over.
It doesn't work if I use SetToolTip() method in OnInitDialog
CMFCButton* bt = ((CMFCButton*)GetDlgItem(IDC_MFCBUTTON1));
bt->SetTooltip(_T("tooltip"));
BUT it does work if I put this code in message handle function like another button's click handle.
What I want is that the CMFCButton could show tooltip when the dialog is created, where should I put these code?
========================
By the way, The tooltip text I set in the Property view does not work for most time.
I just derived a class
class CMyButton : public CMFCButton
{
public:
void SetDelayFullTextTooltipSet(bool DelayFullTextTooltipSet)
{
m_bDelayFullTextTooltipSet = DelayFullTextTooltipSet;
}
};
Instead of a CMFCButton variable on the Dialog class I use the button, I now have a CMyButton.
And in the OnInitDialog, after the SetTooltip call, I do
button.SetDelayFullTextTooltipSet(FALSE);
Have you called the base class' OnInitDialog()? The main point is that the control needs to be created before you call SetToolTip() on it. Step into OnInitDialog() with the debugger and see if m_hWnd of the control has a value at the moment you call SetToolTip().

Clicking in an MFC edit box

I've created a read-only edit box in an MFC dialog box. I'm trying to have it so a user clicks in the edit box, which is read-only, it opens a file dialog, and then puts this value into the text box using UpdateData. I'm catching the ON_EN_SETFOCUS message but pressing OK on the file dialog respawns it, so I get caught in an infinite loop.
UpdateData(TRUE);
CFileDialog fileDialog(TRUE,NULL, NULL,OFN_HIDEREADONLY|OFN_FILEMUSTEXIST, _T("Text Files(*.txt)|*.txt||"));
if( fileDialog.DoModal() == IDOK )
{
configFile=fileDialog.GetPathName(); //Note to self, this includes filename, getPathName includes filename and path.
}
else
{
return;
}
UpdateData(FALSE);
If you've got any ideas on how this should be done, I would be very grateful.
Alright Mr. Lister I guess I'll add an answer.
First off I would preface this with I would probably simply add a button name "..." to launch the file dialog to the right of the edit box for opening the file dialog as that's the simplest solution and what most windows users will expect.
Another option however is to extend an MFC control. When deciding to extend a control you want to pick one that mostly has the desired behavior and that has a virtual destructor which lends itself to being a subclass. Since you want button like behavior CButton may be a good choice.
Your class interface might look something like this:
class CPathButton : public CButton
{
public:
enum { ID /*= IDC_BUTTON1*/ };
const CString GetPath() const;
const CString GetFileName() const;
const CString GetDirectory() const;
const CString GetExtension() const;
// other useful methods for setting file filters etc
protected:
// add ON_CONTROL(BN_CLICKED, ID, &OnClick) or ON_BN_CLICKED(ID, &OnClick)
DECLARE_MESSAGE_MAP()
// CFileDialog fdlg.DoModal(), m_path = fdlg.GetPathName(), SetWindowText(fdlg.GetFileTitle()), etc
afx_msg void OnClick();
// additional message handlers etc
private:
CString m_path; // save full path for after dialog is closed
};
You can add as much or as little customization as you want depending on if the control will be created dynamically, via the resource file, or whatever. The basic idea being that you display the currently selected file name on the button while storing the full path for other uses as a member so the user doesn't need to see the clutter of a long path with nested directories.
If you don't like the way it looks by default you can override OnPaint and handle WM_PAINT messages and use a custom font, size, or add ellipsis for a long file title. You could also handle re-sizing the button to fit the file title by using text metrics and GetTextExtent to ensure the name fits or simply display a CToolTipCtrl when they hover the mouse over the button so they can see the full name. The CMFCButton from the MFC feature pack in VS2008+ has tool tip functionality built in so if you inherit from that instead of CButton displaying a tool tip would be as simple as calling SetTooltip(m_path)
If you want to get really fancy you could use some of the uxtheme API or new windows animation API.
You can override PreTranslateMessage() in your dialog class, and determine if the edit control was clicked that way:
CEdit m_CEditCtrl;
// ...
BOOL YourDialogClass::PreTranslateMessage(MSG *pMsg)
{
if((pMsg->wParam == VK_LBUTTON) && (m_CEditCtrl.m_hWnd == pMsg->hwnd))
{
// open your file dialog
return TRUE; // Return that the message was translated and doesn't need to be dispatched
}
return CDialog::PreTranslateMessage(pMsg);
}
Update: You can also (and it may be a better idea) to override your CEdit control's CWnd::PreTranslateMessage() function. This would require deriving a class from CEdit.
If you are using VS2008 SP1 or above, the easiest way to ask for a path is with CMFCEditBrowseCtrl. It displays an edit control with a button. The steps to use it are:
Change your edit control's class to CMFCEditBrowseCtrl
Call EnableFileBrowseButton to tell it that you want to browse for files, not folders (you can set a filter and default extension)
When the user clicks the button, a file dialog appears, and when you click OK in it, the selected path is written in the edit control.

How do i update a control outside of a dialog?

For example, in an MFC program, I have my main application and a 'class'. What should I do if I want to update a control (say, a listbox) that is situated on my main application from that 'class'?
heres an example that worked for me
theApp.m_pMainWnd->GetDlgItem(IDC_BUTTON6)->SetWindowTextW(L"Run Auto Test");
Your class can be designed to trigger an event which your main application can listen for. Then, a listener/event handler/delegate can be called to handle the event and update the listbox. Typically, most event formats pass a reference of the sender, in this case your 'class', as well as an object containing event arguments. These arguments can be used to pass the list of items you want to add to your listbox.
If you have the handle to dialog object in your class, then you can use GetDlgItem(ResourceID) to get list control object.
The easiest approach is to expose the listview from your application form/window to the classes that use it. You can do this either by passing the listview object (or parent window) to the class constructor, or storing it in a static variable that is accessible by the class.
For better encapsulation, you can put a method in the application that the class can call, e.g. "AddItemToListBox()". This allows the application object to remain in control of how you access the listbox. Again you can do this as a static method, or pass the main program object's 'this' pointer into the class constructor.
i.e.
class CApplication
{
CListBox m_ListBox;
public:
static void CApplication::AddItemToListBox(CString itemText)
{
// Add the item as you wish here
}
}
class CMyClass
{
afx_msg void CMyClass::OnMouseDown(...)
{
CApplication::AddItemToListBox("This is a test");
}
}