Re: How can I use C# GUI component in my MFC application.

Tech Tip: Click here to run a free scan for Windows Errors and optimize PC performance



Hi Nishant,

Thanks for your help and I really appreciate your time. I will use it and
let you know how it turned out in my application.

Again Thanks
Sushi Srivastava

"Nishant Sivakumar" wrote:

> Hello Sushil,
>
> Here's some stuff I had written on this topic for my book (Extending MFC
> Applications with the .NET Framework co-authored with Tom Archer) - but it
> never made it to the book as we found that COM techniques were available
> that were better suited to doing it. But personally, I've always felt that
> the technique I used (crude subclassing) was actually simpler to implement.
> Of course with VC 8, you won't need to do all this as MFC has classes to
> interact with Windows Forms smoothly.
>
> [*****snip*****]
>
> Using a .NET control in an MFC dialog
>
> Well, we have seen how Windows Forms makes GUI development slightly easier
> for us, and how we can easily develop custom Forms controls. But obviously
> if we cannot use them in our existing MFC based applications, it's not going
> to be very useful for us. Let's see how we can use a .NET Windows Forms
> control in an MFC application. In fact let's use the same
> Centigrade-Fahrenheit conversion control that we developed in the previous
> section.
>
> First create a MFC dialog based application and add Managed Extensions
> support to it by setting the required project properties. Now add
> System.Windows.Forms , System.Drawing and FirstComposite (this is our custom
> C-F-C control) to the Project Reference list. Basically what we do is to
> write a wrapper class for the custom .NET control we developed. The wrapper
> class will have a CWnd member variable and we use the CWnd::Attach function
> to attach the control to the CWnd object, and we get the HWND of the user
> control using the Handle property. Things will be clearer when you examine
> the code :-
>
> //Header file
> #pragma once
>
> using namespace FirstComposite;
>
> class CControlWrapper
> {
> public:
> CControlWrapper(void);
> ~CControlWrapper(void);
> BOOL Create(CWnd* pWnd, int x, int y, int cx, int cy);
> private:
> CWnd m_wnd;
> gcroot<FirstCompositeControl*> m_control;
> };
>
> //Implementation file
> #include "StdAfx.h"
> #include ".\controlwrapper.h"
> #using <mscorlib.dll>
> #using <system.dll>
>
> CControlWrapper::CControlWrapper(void)
> {
>
> }
>
> CControlWrapper::~CControlWrapper(void)
> {
> m_wnd.Detach();
> }
>
> BOOL CControlWrapper::Create(CWnd *pWnd, int x,
> int y, int cx, int cy)
> {
> BOOL suc = FALSE;
>
> m_control = new FirstComposite::FirstCompositeControl();
> m_control->Location = System::Drawing::Point(x, y);
> m_control->Name = S"WrappedUserControl";
> m_control->Size = System::Drawing::Size(cx, cy);
>
> if( m_wnd.Attach((HWND)m_control->Handle.ToPointer()) )
> suc = TRUE;
>
> m_wnd.SetParent(pWnd);
>
> return suc;
> }
>
> As you can see, all the action is in the Create method, where we instantiate
> and create our .NET custom Forms control. We get the HWND of the control
> using the Handle property and attach it to the CWnd member object using the
> CWnd::Attach method. And then we call the SetParent method to associate the
> control with our dialog window. As you can see, we have called Detach on the
> CWnd object in the class destructor, else when the CWnd object's destructor
> gets called, the call to DestroyWindow will fail.
>
> Add a CControlWrapper member variable to the dialog class, and add the
> Create call in the OnInitDialog :-
>
> m_control.Create(this,20,20,200,200);
>
> Compile and run the program, and you'll see the .NET control we created
> earlier looking nice and snappy inside our MFC dialog. In fact, you can
> embed any .NET Forms controls in your MFC program, including the native .NET
> controls like TextBox, ListBox etc. In the above control we don't need to
> access any methods or properties of the wrapped control, but sometimes we
> might need to make calls to the control methods, as in the case of our
> colored list box which we created earlier. For such controls we need to
> write wrapper methods and properties for all functions and properties that
> we need to expose to the calling program. Let's now write a similar program
> as above to use the colored list box control in an MFC dialog program. Just
> follow similar steps as above, make sure you reference the colored list box
> DLL, and create the wrapper class as follows :-
>
> //Header file
> #pragma once
>
> class CControlWrapper
> {
> public:
> CControlWrapper(void);
> ~CControlWrapper(void);
> BOOL Create(CWnd* pWnd, int x, int y, int cx, int cy);
> void Clear();
> void Add(CString str);
> void GenerateWinner();
> private:
> CWnd m_wnd;
> gcroot<ColorListBox::ColorListBoxControl*> m_control;
> };
>
> //Implementation file
> #include "StdAfx.h"
> #include ".\controlwrapper.h"
> #using <mscorlib.dll>
>
> CControlWrapper::CControlWrapper(void)
> {
> }
>
> CControlWrapper::~CControlWrapper(void)
> {
> m_wnd.Detach();
> }
>
> BOOL CControlWrapper::Create(CWnd* pWnd, int x, int y, int cx, int cy)
> {
> BOOL suc = FALSE;
>
> m_control = new ColorListBox::ColorListBoxControl();
> m_control->Location = System::Drawing::Point(x, y);
> m_control->Name = S"WrappedUserControl";
> m_control->Size = System::Drawing::Size(cx, cy);
>
> if( m_wnd.Attach((HWND)m_control->Handle.ToPointer()) )
> suc = TRUE;
>
> m_wnd.SetParent(pWnd);
>
> return suc;
> }
>
> void CControlWrapper::Clear()
> {
> m_control->Items->Clear();
> }
>
> void CControlWrapper::Add(CString str)
> {
> m_control->Items->Add((System::String*)str);
> }
>
> void CControlWrapper::GenerateWinner()
> {
> m_control->GenerateWinner();
> }
>
> As you can see, we have written methods in the wrapper class that internally
> call the corresponding methods in the inner class. Notice how we have used a
> CString as the argument, but in the method we convert it to a String*. This
> makes it easier for MFC code to call this function. And now calling those
> methods for a calling program would be as easy as the code snippet below :-
>
> void CMFCListBoxTestDlg::OnBnClickedButton1()
> {
> m_control.Clear();
> m_control.Add(S"Goran Ivanisevic");
> m_control.Add(S"Andre Agassi");
> m_control.Add(S"Pete Sampras");
> m_control.Add(S"Gabriela Sabatini");
> m_control.Add(S"Mary Joe Fernandez");
> m_control.Add(S"Venus Williams");
> m_control.Add(S"Boris Becker");
> m_control.Add(S"Leander Paes");
>
> m_control.GenerateWinner();
> }
>
> The only disadvantage is that for every function that you need to expose,
> you will have to write a corresponding wrapper function. But usually you'll
> find that you only need to expose a few functions, and thus you need to
> write wrapper functions only for those methods.
>
> [/*****snip*****]
>
> And here's some more stuff :-
>
> [*****snip*****]
> Getting Event notifications for the .NET control
>
> One issue you'll soon realize when you use .NET controls in your MFC dialogs
> or form views is that there is no direct way for you to get event
> notifications for the control. Let's say you have a list box control (the
> ..NET version) on an MFC dialog and that you want to update a text box (the
> MFC version) with the current selected item's text every time the list box
> selection changes. Well, it can be done, as we'll demonstrate in this
> section.
>
> Let's create a MFC dialog based application and add support for Managed
> Extensions to it. Now add a text box using the dialog editor. Now we'll add
> a .NET Windows Forms list box to the dialog just as we had done in the
> previous sections with our custom user controls. Here is the header file for
> the class :-
>
> #pragma once
>
> #define WM_LISTBOXCHANGED WM_APP + 100
>
> class MyListBox
> {
> public:
> MyListBox(void);
> ~MyListBox(void);
> BOOL Create(CWnd *pWnd, int x, int y, int cx, int cy);
> private:
> CWnd m_hWnd;
> gcroot <System::Windows::Forms::ListBox*> m_listbox;
> HWND m_parent;
> };
>
> __gc class EventHandlerClass
> {
> public:
> static System::IntPtr m_hwnd;
> static System::Void SelectedValChangedHandler(System::Object * sender,
> System::EventArgs * e)
> {
> System::Windows::Forms::ListBox* listbox =
> static_cast<System::Windows::Forms::ListBox*>( sender );
> CString s = (CString)listbox->Text;
> SendMessage((HWND)m_hwnd.ToPointer(),WM_LISTBOXCHANGED,
> (WPARAM)(LPCTSTR)s,0);
> }
> };
>
> Most of it is similar to what we did in the previous sections, but you'll
> notice that I have added an __gc class called EventHandlerClass. This class
> is used as a proxy for the events generated by the .NET control. Basically
> we have a static event handler in the __gc class which we register as an
> event handler for the .NET control; and thus whenever the event occurs, we
> have an access point to the event. Now the next difficulty would be in
> passing on this event information to the dialog window where it should be
> handled. That's where we use the age old trusted windows message technique.
> We have a user-defined message called WM_LISTBOXCHANGED and we simply post
> this message to the handle of the parent window (which in this case will be
> the dialog window) and pass the currently selected string in the list box as
> the WPARAM parameter. And here is the implementation file for the class :-
>
> #include "StdAfx.h"
> #include ".\mylistbox.h"
> #using <mscorlib.dll>
>
> MyListBox::MyListBox(void)
> {
> m_parent = NULL;
> }
>
> MyListBox::~MyListBox(void)
> {
> m_hWnd.Detach();
> }
>
> BOOL MyListBox::Create(CWnd* pWnd, int x, int y, int cx, int cy)
> {
> BOOL suc = FALSE;
>
> m_listbox = new System::Windows::Forms::ListBox();
> m_listbox->Location = System::Drawing::Point(x, y);
> m_listbox->Name = S"WrappedUserControl";
> m_listbox->Size = System::Drawing::Size(cx, cy);
>
> m_listbox->Items->Add(S"Apples");
> m_listbox->Items->Add(S"Bananas");
> m_listbox->Items->Add(S"Oranges");
>
> m_listbox->SelectedIndexChanged += new System::EventHandler(NULL,
> EventHandlerClass::SelectedValChangedHandler);
>
> if( m_hWnd.Attach((HWND)m_listbox->Handle.ToPointer()) )
> suc = TRUE;
>
> m_hWnd.SetParent(pWnd);
> m_parent = pWnd->m_hWnd;
> EventHandlerClass::m_hwnd = m_parent;
>
> return suc;
.


Quantcast