Solved

Access MFC edit controls within a CFormView

Posted on 2003-11-14
17
901 Views
Last Modified: 2013-11-20
I have an dialog based application that uses PropertySheets and PropertyPages. There is 1 PropertySheet and the 29 PropertyPages (dialogs) are children of the Sheet. On one of the pages, I have a scrollable CFormView that shows a dialog created visually. I can access the data from the edit controls by just calling UpdateData() and then just retreiving data from the variables. My problem is that I want to mask certain edit controls using Subclass. And I have not been able to access them using a GetDlgItem( {IDENTIFIER} ); I have tried comparing the CDC pointer from the OnDraw(CDC*) to a pointer I retrieve from the DC that I try to get using:
CDC *mDC = pWnd->GetDC();
if (mDC == pDC) {
    HWND hwnd = pWnd->GetSafeHwnd();
    SPMaskEdit m_maskEdit;
    m_maskEdit.SubclassWindow(hwnd);
    m_maskEdit.setMask("###.#");
}
But it never evaluates as true. I am assuming that maybe pDC is just a temp DC and not pointing to the actual control.
The SPMaskEdit class is just used to mask the edit control so that the entry will only allow values in the form of '300.2'. If there is a better way of doing this better than trying to retreive the CWnd* and then modify it that way please let me know.
The dialog for the form is directly attached to the CMyFormView class and not to a document that is then added to the view.
0
Comment
Question by:krissanders
  • 8
  • 7
  • 2
17 Comments
 
LVL 19

Expert Comment

by:Dexstar
ID: 9749615
krissanders:

> CDC *mDC = pWnd->GetDC();
> if (mDC == pDC) {
>     HWND hwnd = pWnd->GetSafeHwnd();
>     SPMaskEdit m_maskEdit;
>     m_maskEdit.SubclassWindow(hwnd);
>     m_maskEdit.setMask("###.#");
> }
> But it never evaluates as true. I am assuming that maybe pDC is just a temp
> DC and not pointing to the actual control.

You are comparing pointers to CDC objects, not actual Device Context handles.  I'm not even sure that you really want to be doing that, but if you are, then you need to get the HDC values out of the CDC classes and compare them.

     if ( mDC->m_hDC == pDC->m_hDC )

I'm not even sure why you are comparing DC's like that, but at least you're comparing the right values.  Why do you want to compare DC's?

Hope That Helps,
Dex*
0
 

Author Comment

by:krissanders
ID: 9750201
Thanks for the responce. Although that has not solved the problem. The reason that I am comparing the DCs in this case is that the OnDraw(CDC *pDC); is the function where I am trying to gain access to the edit control. I know the IDC_* of each of the edits that I want to mask and was doing:
void CMyFormViewView::OnDraw(CDC* pDC)
{
//      CMyFormViewDoc *pDoc = (CMyFormViewDoc*)GetDocument();
      CRect rect;
      GetClientRect(rect);

      GetTempFields();                // populates a vector that contains all of the IDC_EDIT* for the edit controls that I want to mask
      for (int i=0; i<37; i++) {      // there are 37 edit controls that I want to mask
            CWnd *pWnd = (CWnd*)maskEdits.back();  // get the window pointer to the edit control
            maskEdits.pop_back();                                // clear the current edit control from the vector
            CDC *mDC = pWnd->GetDC();                    // get the DC of the edit control
            if (mDC->m_hDC == pDC->m_hDC) {          // check to see if the item to be drawn is the edit control that I want to mask
                  HWND hwnd = pWnd->GetSafeHwnd();    // if it is, get the handle
                  SPMaskEdit m_maskEdit;                         // create a mask class object
                  m_maskEdit.SubclassWindow(hwnd);       // subclass the window/edit control to the mask object
                  m_maskEdit.setMask("###.#");               // set the mask for the edit control
                  break;                                                    // since the item to be drawn has been masked, break from the loop
            }
            ReleaseDC(mDC);      
      }
      
      CPaintDC dc(this);                    
      dc.SetBkMode(TRANSPARENT);
}

Again, I dont know if the OnDraw function is the best to try to modify the control or not. But my thinking is that it will have been created, I can then modify it, and then it will be draw to the display.
0
 
LVL 19

Expert Comment

by:Dexstar
ID: 9750231
Yeah, you don't want to subclass a control like that...  You should do it once, right after the control is created...  Or, in your OnInitDialog handler, get the handle to the control, and then subclass it.  You don't need to keep doing it everytime you want to paint it.  Just do it once, and leave it subclassed.

Dex*
0
 

Author Comment

by:krissanders
ID: 9750339
Exactly. The problem is that I am not sure exactly when the control is being created and the CView->CFormView->CMyFormViewView class does not have a OnInitDialog handler even though it controlls a dialog. I don't have much experience with Views and looked around on the web for info on how to implement one inside a propertypage and it worked. Now it is just a matter of accessing the controls within the view's dialog.
0
 
LVL 19

Expert Comment

by:Dexstar
ID: 9750435
krissanders:

It's weird that CFormView is based on a dialog resource, but doesn't have OnInitDialog().  Have you tried adding on manually?  Add a handler for WM_INITDIALOG and see if you get the message.

If that doesn't work, then override OnCreate(), and inside, call the base class, and then try and hook up the controls and subclass them.

Dex*
0
 

Author Comment

by:krissanders
ID: 9773337
Dex:
  Thanks for the responce. I have tried the OnCreate() and found that it will work for the creation of the view window but does not seem to handle the creation of the controls. I do not have a separate class that the dialog is attached to to override and gain control of the edits. In my formview header there is the line:
      enum { IDD = IDD_MYFORM_DIALOG };
which to my understanding tells the app that the view class will have control of that dialog. But maybe what I should do is create a Document class and then add the document to the view and then just see if I can control within the document class. Please let me know what you think of this idea.

Kris
0
 
LVL 19

Expert Comment

by:Dexstar
ID: 9773465
You don't need to create a document class.  There is an easy way to do this, we just need to figure it out.

Inside of your CMyFormViewView::OnCreate function, did you call the base class version of OnCreate BEFORE trying to get the dialog controls?  I think the base class is what sets all of that up, so you need to call that before you can get to the controls.

Then, in your class, you should declare one variable for each control you want to sub-class.  Get the handle to the control, and assign it to that class.

Dex*
0
 

Author Comment

by:krissanders
ID: 9773742
Dex:

Yes. What I did was something like this:

// the OnCreate is called by the framework after the Create has taken place but before the window is displayed
int CMyFormViewView::OnCreate(CREATESTRUCT lpCreateStruct) {  // this function is only called 1 time in my app
   int result = CFormView::OnCreate(lpCreateStruct);
   for (int i=0; i<37; i++) { // the maskEdits vector only contains 37 IDDs
      CWnd *pWnd = GetDlgItem(maskEdits[i]);      /* Here is a problem. It is returning NULL */
      if (pWnd != NULL) {
         SPMaskEdit m_maskEdit;                                          // create a mask class object
         m_maskEdit.SubclassWindow(pWnd->m_hWnd);       // subclass the window/edit control to the mask object
         m_maskEdit.setMask("###.#");                                // set the mask for the edit control
       } // end if statement
    } // end for loop
   return result;
}

Now, for some reason it is not able to obtain the CWnd pointer to each of the IDDs of the controls. I think that we are on the right track here. The above should, if the CWnd* is retreived, subclass each of the 37 controls and then return the 0 from the CFormView::OnCreate
0
Why You Should Analyze Threat Actor TTPs

After years of analyzing threat actor behavior, it’s become clear that at any given time there are specific tactics, techniques, and procedures (TTPs) that are particularly prevalent. By analyzing and understanding these TTPs, you can dramatically enhance your security program.

 
LVL 19

Expert Comment

by:Dexstar
ID: 9773818
Okay, I'm not sure what you're doing with SPMaskEdit is going to work because I'm not familiar with that class.  What is it?  Where did you get it?  My concern is that when you destroy the variable after falling out of the for loop, that it will UN-subclass the window.

But, we're getting ahead of ourselves, because you still can't get the CWnd* for the control.  To get past that, here's what I want you to do:
1) Open the dialog IDD_MYFORM_DIALOG in the editor.
2) Select one of the controls that you want to subclass, and get its ID value (I'll assume it is IDC_EDIT1 in my sample, but you should change it to reflect a value actually on the dialog.  This is important).
3) Use this code:
int CMyFormViewView::OnCreate(CREATESTRUCT lpCreateStruct) {  // this function is only called 1 time in my app
   int result = CFormView::OnCreate(lpCreateStruct);

   CWnd* pWnd = GetDlgItem( IDC_EDIT1 );
   return result;
}

Just set a breakpoint on the "return" line, and see if the value of pWnd is NULL or not.  If it is, then OnCreate isn't the right spot to hook up these values.

Dex*
0
 
LVL 3

Expert Comment

by:freewell
ID: 9776945
It is better to handle the controls initilization in  OnActivateView when it is called at first time.

void CMyFormViewView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView)
{
    // TODO: Add your specialized code here and/or call the base class
    static BOOL bInit = FALSE;

    if(!bInit)
    {
        bInit = TRUE;

        for (int i=0; i<37; i++) { // the maskEdits vector only contains 37 IDDs
            CWnd *pWnd = GetDlgItem(maskEdits[i]);      /* Here is a problem. It is returning NULL */
            if (pWnd != NULL) {
                SPMaskEdit m_maskEdit;                                          // create a mask class object
                m_maskEdit.SubclassWindow(pWnd->m_hWnd);       // subclass the window/edit control to the mask object
                m_maskEdit.setMask("###.#");                                // set the mask for the edit control
            } // end if statement
        } // end for loop
    }
    CFormView::OnActivateView(bActivate, pActivateView, pDeactiveView);
}
0
 

Author Comment

by:krissanders
ID: 9779424
Dex:

I tried that and it is reporting NULL.

Freewell:

The problem that I am having is that the OnActivateView() is not being called. I have tried putting ON_WM_ACTIVATE() in the message map but it is still not calling the routine.
0
 
LVL 19

Accepted Solution

by:
Dexstar earned 300 total points
ID: 9781688
It doesn't have to be OnActivateView() ... Try OnShow() ... Try any of them...  Just to see if there is a place where
     CWnd* pWnd = GetDlgItem( IDC_EDIT1 );

Will not return NULL.  Once you have that, we can identify the best place.  But if there is no place where that function will not return NULL, then there is something else wrong.

Dex*
0
 
LVL 3

Expert Comment

by:freewell
ID: 9785095
May be you should try to call your control initialization in CFormView::OnInitialUpdate.
0
 

Author Comment

by:krissanders
ID: 9787461
I do get a pointer from within the OnInitialUpdate(). However, upon execute of the code, I get a debug assertion error when the framework calls
HWND CDataExchange::PrepareCtrl(int nIDC)
{
      ASSERT(nIDC != 0);
      ASSERT(nIDC != -1); // not allowed
      HWND hWndCtrl;
      m_pDlgWnd->GetDlgItem(nIDC, &hWndCtrl);
      if (hWndCtrl == NULL)
      {
            TRACE1("Error: no data exchange control with ID 0x%04X.\n", nIDC);
            ASSERT(FALSE);                                                                                    /* app fails here */
            AfxThrowNotSupportedException();
      }
      m_hWndLastControl = hWndCtrl;
      m_bEditLastControl = FALSE; // not an edit item by default
      ASSERT(hWndCtrl != NULL);   // never return NULL handle
      return hWndCtrl;
}

There is another function:
HWND CDataExchange::PrepareEditCtrl(int nIDC)
{
      HWND hWndCtrl = PrepareCtrl(nIDC);
      ASSERT(hWndCtrl != NULL);
      m_bEditLastControl = TRUE;
      return hWndCtrl;
}

This is going just off CWnd *pWnd = GetDlgItem(IDC_EDIT3); On my previous attempt from within this routine I assumed that the control had not yet been initialized
0
 

Author Comment

by:krissanders
ID: 9787632
Here is something.. Let me know what you guys think.
I tried the same deal from within the OnPaint(). It will then take that control and not display that single control. All the rest of the edit controls show up normally but the one that was just subclassed. I was able to actually gain control of the edit box.

Here is where I got the SPMaskClass http://www.codeguru.com/editctrl/masked_edit3.shtml
The SubclassDlgItem was giving me a "instruction at memory could not be read" error.
0
 

Author Comment

by:krissanders
ID: 9788415
I think that I almost have it working. The edit control is now being drawn and shows the mask. I can only enter 1 number rather than the ###.# it comes out 1___._ And I think that it has to do with using the edit control variable instead of the maskedit control variable. I also had to create a global SPMaskEdit array for the variables of the masked edits. I appreciate the help and I will award the points to Dex for all of your help. I really appreciate the time that you put in to this effort.
0
 
LVL 19

Expert Comment

by:Dexstar
ID: 9788689
I'm not sure how much I helped, but you're welcome for the effort...  :)

Good luck!

Dex*
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Introduction: The undo support, implementing a stack. Continuing from the eigth article about sudoku.   We need a mechanism to keep track of the digits entered so as to implement an undo mechanism.  This should be a ‘Last In First Out’ collec…
Introduction: Dialogs (1) modal - maintaining the database. Continuing from the ninth article about sudoku.   You might have heard of modal and modeless dialogs.  Here with this Sudoku application will we use one of each type: a modal dialog …
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

707 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

18 Experts available now in Live!

Get 1:1 Help Now