Solved

TextOut question

Posted on 2000-02-29
43
562 Views
Last Modified: 2013-11-20
here is my code and below it is what I want to do.

void CChildView::OnPaint()
{
 dc.SetTextAlign(TA_UPDATECP);
 dc.TextOut(0,0,result);
      
 Invalidate(FALSE);
 UpdateWindow();
}

result is of Type CString.  The text is displayed on the screen.  There are two problems with it.  The first is that it flickers and the next is that it erases the last text that was displayed with the new stuff.  Here is what I would like for it to do.  I want the flickering gone and I want the text to be displayed right underneath the last text that was displayed.  Just like when you are using notepad and you enter some text on the first line and then you press enter and enter some more text!  That is what I want to happen.  Can someone please help me with my question.
0
Comment
Question by:simongod
  • 22
  • 20
43 Comments
 
LVL 11

Expert Comment

by:mikeblas
ID: 2570818
The reason that it flickers is because you're forcing it to repaint over and over and over again.  Remove the calls to Invalidate() and UpdateWindow().

If you want to show a list of values, you need to create a list of values. That means that you'll need to put historical values of "result" into a CStringArray, and then paint the whole array.

If your CStringArray is named m_strResultArray, your paint code is going to look like this:

..B ekiM
0
 
LVL 11

Expert Comment

by:mikeblas
ID: 2570994
Sorry. I forgot the darn code!

BTW, why are you using OnPaint() and not OnDraw()?

void CFooView::OnDraw(CDC* pDC)
{
   int nYPlace = 0;
   int nIndex = 0;

   CRect rect;
   GetClientRect(rect);

   while (nYPlace < rect.bottom && nIndex < m_strResultArray.GetUpperBound())
   {
      nYPlace += pDC->DrawText(m_strResultArray[nIndex], rect, DT_SINGLELINE);
      rect.top = nYPlace;
       nIndex++;
   }
}

Note that I use DrawText() instead of TextOut() because DrawText() conveniently returns the hieght of the text that I drew. I just add that value to my rect's top before drawing the next line.

..B ekiM
0
 
LVL 2

Author Comment

by:simongod
ID: 2572779
because I dont have an OnDraw function my program is childview based and there is only onpaint.  Will your code work with onpaint?  Can you show me how to add and ondraw so that your code will work?
0
 
LVL 2

Author Comment

by:simongod
ID: 2572833
this didnt work because I went into the classwizard and I tried to add an onDraw and I cannot there isnt one that I can add.  TRy again.
0
 
LVL 11

Accepted Solution

by:
mikeblas earned 300 total points
ID: 2575160

 
 > because I dont have an OnDraw function my program is
 > childview based

Uh, what does that mean, exactly?

 > and there is only onpaint.  Will your code work with onpaint?

Yes.

void CFooView::OnPaint()
{
   CPaintDC dc(this);
   int nYPlace = 0;
   int nIndex = 0;

   CRect rect;
   GetClientRect(rect);

   while (nYPlace < rect.bottom && nIndex < m_strResultArray.GetUpperBound())
   {
      nYPlace += dc.DrawText(m_strResultArray[nIndex], rect, DT_SINGLELINE);
      rect.top = nYPlace;
       nIndex++;
   }
}


..B ekiM
0
 
LVL 2

Author Comment

by:simongod
ID: 2576509
if you go into classwizard and try to find it, you will not.
0
 
LVL 2

Author Comment

by:simongod
ID: 2576520
so here is my next problem, The text that I have coming in is a CString how do I convert CString to CStringArray?  How can I put the CString into the Array and does your code display everything in the array at once?
0
 
LVL 11

Expert Comment

by:mikeblas
ID: 2576973
> so here is my next problem, The text that I have coming
 > in is a CString how do I convert CString to CStringArray?

A CStringArray is a collection. It holds many individual CStrings. As many as you want--or, actually, as many as memory will allow. To add a string, just call CStringArray::Add(). To get a particular string, use array syntax just like I did in my sample. To remove an element, calle CStringArray::RemoveAt().

   m_strResultArray.Add("One");
   m_strResultArray.Add("Two");
   m_strResultArray.Add("Three");
   m_strResultArray.Add("Bad");
   m_strResultArray.Add("Four");
   m_strResultArray.RemoveAt(3);

 > How can I put the CString into the Array and does
 > your code display everything in the array at once?

It loops through each element and displays them one by one in the view, ligning them up from top to bottom.  Just like I described in my original answer.

It looks like you really should buy a couple of books on programming. Charles Petzhold's book is a great intro to the way Windows works. My own book, or Jeff Prosize's book, are very good at explaining MFC.

..B ekiM
0
 
LVL 2

Author Comment

by:simongod
ID: 2580741
looks like you need to buy some books because it still doesnt work.  It doesnt update the display the only way I can get it to work is if I move another program over the text FORCING the program to refresh its display.  I want the display to be refreshed when ever there is NEW text to be shown.
0
 
LVL 11

Expert Comment

by:mikeblas
ID: 2582300

 > looks like you need to buy some books because it still doesnt work.  

Looks like you need to learn not to bite the hand that feeds you.

 > I want the display to be refreshed when ever there is NEW text to be shown.

Whenever you add new text, just invalidate the window--just call Invalidate().

..B ekiM
0
 
LVL 2

Author Comment

by:simongod
ID: 2588577
you should not make fun of others!  I have read your book many times and it does not say anything about what I wanted to do.  I have read many books that just teach the basics none that I have found teach anything more.  IF you can recommend anything that would be more advanced then that would be a big help.  I just tried Invalidate and it causes the screen to flicker.  My original question was how to eliminate the flickering!
0
 
LVL 2

Author Comment

by:simongod
ID: 2588779
here is what I have done.  What happens is the program displays the last record in the array and not everything.

m_strResultArray.Add(result);
      int nYPlace = 0;
      int nIndex = 0;

      CRect rect;
      GetClientRect(rect);

      while (nYPlace < rect.bottom && nIndex <= m_strResultArray.GetUpperBound())
      {
            nYPlace += dc.DrawText(m_strResultArray[nIndex], rect, DT_SINGLELINE);
            rect.top = nYPlace;
            nIndex++;
            Invalidate(FALSE);
      }
0
 
LVL 11

Expert Comment

by:mikeblas
ID: 2590335
> you should not make fun of others!  

I'm not. I made a suggestion to help you.

 > I just tried Invalidate and it causes the screen to flicker.

Are you going to follow my advice, or not?

 > My original question was how to eliminate the flickering!

And I already told you how to do that: don't call Invalidate() from your paint handler. But, that's what you've gone and done despite my direct advice.

You need to remove the call to Invalidate() from your OnPaint() handler. You need to call m_strResultArray.Add() from some place other than your OnPaint() handler.

I have no idea where new result strings come from in your app. But, when a new one is available (that is, _not_ in the OnPaint() handler!) you should call m_strResultArray.Add().  Then, you should call Invalidate() to have the window update its contents.

You see, calling Invalidate() _causes_ Windows to send an OnPaint(). By calling Invalidate() within OnPaint() causes flicker because painting happens over and over and over. That's not what you want--it also sucks CPU cycles endlessly.

..B ekiM
0
 
LVL 2

Author Comment

by:simongod
ID: 2598416
here is the new code that I have with the new function that handles adding CString to a CStringArray and invalidates the window

void CChildView::OnPaint()
{
      CPaintDC dc(this); // device context for painting

      MessageArray();
      
      int nYPlace = 0;
      int nIndex = 0;

      CRect rect;
      GetClientRect(rect);

      while (nYPlace < rect.bottom && nIndex <= m_strResultArray.GetUpperBound())
      {
            nYPlace += dc.DrawText(m_strResultArray[nIndex], rect, DT_SINGLELINE);
            rect.top = nYPlace;
            nIndex++;
      }

      // Do not call CWnd::OnPaint() for painting messages
}

CString newresult = "";
void CChildView::MessageArray()
{
      CMoveMessage moved;
      CString result = moved.GetMoveMessage();

      if(result != newresult)
      {
            m_strResultArray.Add(result);
            Invalidate(FALSE);
            newresult = result;
      }
      UpdateWindow();
}

The problem that I am having now is that in order of the screen to refresh itself I have to move the program around on the screen.  How should I fix that?
0
 
LVL 11

Expert Comment

by:mikeblas
ID: 2599064
> How should I fix that?

By calling Invalidate(). Are you sure you're calling Invalidate() on the correct window?

..B ekiM
0
 
LVL 2

Author Comment

by:simongod
ID: 2600635
by the looks of my code I should be.  thats why its up there so that you can tell me if I am.  I wrote another function to handle adding CString data to the CStringArray and then called that function within the in OnPaint function.  Invalidate is called only IF new Data is added to the array.  Should I have done it that way?
0
 
LVL 11

Expert Comment

by:mikeblas
ID: 2601142
> thats why its up there so that you can tell me if I am

Unfortunately, you don't show which window you're calling Invalidate() upon. So, I can't tell by inspection.

..B ekiM
0
 
LVL 2

Author Comment

by:simongod
ID: 2605278
ok I got it to work (with a little thinking he he)  Here is the NEW problem that occured because of what I did.  First of all this is a client/server app.  The server is on one computer and the client is on another.  when the client sends a message the server recieves it and keeps a log of it in a edit box in the server program.  Now everything works just fine.  The problem that I now have is when both programs are running on the same computer and I drag the client program over the server program the OnDraw function causes the text that is displayed in the edit box to refresh EVERYTIME that I move the client program over the server program.  Now that really isn't a problem its rather an annoyance.  Is there a way to check to see if something is dragged over the program so that it doesnt display that text over again?  I really dont care about this but I would like to fix it. As far as i am concerned you have answered my question BUT if you want a few more points you could help me with this small one since you already know what is going on.
0
 
LVL 11

Expert Comment

by:mikeblas
ID: 2606572
> Is there a way to check to see if something is dragged
 > over the program so that it doesnt display that text over again?  

I can't understand your question.  What text are you talking about? How can you tell the text is being displayed again if there is a window obscuring it, anyway?

..B ekiM
0
 
LVL 2

Author Comment

by:simongod
ID: 2612317
well the window that obscures it is a great deal smaller and I can see what is going on in the background.  I think that I stated that this is a client and a server programs that are communicating with each other.  When I run the server in the background and use the client to send the server a message the server gets updated. When I move the client over the servers window then the server gets displays text over and over again.  If you read what my original question was then you would not ask me "what text".
0
 
LVL 11

Expert Comment

by:mikeblas
ID: 2612640
> When I move the client over the servers window then the server gets displays
 > text over and over again.

I'm sorry; I still don't understand what that means. "gets displays text over and over again" makes no sense to me.

Do you mean that OnPaint() is being called very frequently? If so, that probably means you're calling Invalidate() too much.

..B ekiM
0
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!

 
LVL 2

Author Comment

by:simongod
ID: 2613195
I have a server app that is being run on the same computer as the client app.  The client app sends a string to the server and the server displays the string inside of an edit control.  When I move the client (which is a lot smaller) over the server app the string that the client last sent is refreshed (i guess thats how to explain it) every pixel that the client moves over the server app.  I dont really know how to explain that.  The servers OnDraw is called for every drag of the client app. Does that help you?
0
 
LVL 11

Expert Comment

by:mikeblas
ID: 2616584
> refreshed (i guess thats how to explain it) every pixel that the client moves over the server app.  

I still can barely understand what you mean.

If you're saying that the server app repaints itself as the client app moves over it, that's exactly what I'd expect. One window is obscuring another. Then, it moves so the part obscured is now visible. The area revealed is painted again.

That's how Windows works.

..B ekiM
0
 
LVL 2

Author Comment

by:simongod
ID: 2617203
thats what is happening.  My server app can be minimized to the system tray.  When its there the text that is sent from the client app is not displayed when I restore the server app.  Does any of this help?  I can send you the apps they are small.
0
 
LVL 11

Expert Comment

by:mikeblas
ID: 2626979
> thats what is happening.  

That's the way Windows works. How do you expect to get around it?

 > I can send you the apps they are small.

I'd need lots more than 50 points to debug your code for you.

..B ekiM
0
 
LVL 2

Author Comment

by:simongod
ID: 2628034
I will raise the points to 150.  Don't be greedy.
0
 
LVL 11

Expert Comment

by:mikeblas
ID: 2630802
> Don't be greedy.

Greedy?  Look: I can't imagine being more generous. To have someone of my caliber look over your code would normally cost $150 an hour, not 150 useless points. I've kept with this question for more than two weeks.

If you want to post your code, I'll look at it.  But, please: try to be less abrasive and demanding.

..B ekiM
0
 
LVL 2

Author Comment

by:simongod
ID: 2641337
well what part of my code would you like to look at?  there is a lot.  How about I email it to you and you can run it to see what it going on. There is not that much functionality involved.  I am sorry that I have been "abrasive and demanding"  you have helped me a lot and I thank you for it.
0
 
LVL 11

Expert Comment

by:mikeblas
ID: 2647735
Like I said, I'm not going to look at your projects for so few points. Increase the point value and we'll then talk about it.

I need to see your code to demo the problem you're describing. I'm not fully sure I understand it, but what I think you're telling me seems to be exactly the way Windows normally behaves: a window that's obscured gets repainted when it again becomes visible.

Maybe that's not exactly what's happening, but seeing some specific code would help. It would also positively show me what I need to tell you in order to close this question. I'm not sure what's wrong, so I can't possibly figure out what to tell you to fix in order to make it right.

..B ekiM
0
 
LVL 2

Author Comment

by:simongod
ID: 2650838
did you help develop VC?  or are you a major contributor to its development?  I was just curious.  I will send the code to you and raise the points by 250pts IF you would be so kind as to help me further with my project.  I will post questions everynow and then with your name in the subject so that you know that it deals with this subject.  I will grade the pts accordingly.  What do you say, hmm?
0
 
LVL 3

Expert Comment

by:kulina
ID: 2655709
Try DrawText func. instead. Or maybe removing Invalidate(FALSE) or UpdateWindow() from your OnPaint would help (I'm just guessing).
Or call CWnd::OnPaint() explicitly from your OnPaint after you update your CString.
Good luck!
0
 
LVL 11

Expert Comment

by:mikeblas
ID: 2656956

 simongod> did you help develop VC?  or are you a major
 simongod> contributor to its development?  I was just curious.  

Yes. It's no secret. See my bio, or the back of my book, or my webpage.

 simongod> I will send the code to you and raise the points by 250pts

OK.  Please post your email address and I will contact you.

 simongod> I will post questions everynow and then with your name in the
 simongod> subject so that you know that it deals with this subject.  

Huh?  I don't understand what you're saying here.

 kulina> Try DrawText func. instead.

If he's using the code I posted last month, he's already using DrawText().

 kulina> Or call CWnd::OnPaint() explicitly from your OnPaint
 kulina> after you update your CString.

Calling a message handler without a message to be handled is a terrible idea.
Good luck!

..B ekiM

0
 
LVL 2

Author Comment

by:simongod
ID: 2708347
asulwer@yahoo.com

Are you going to help me further?
0
 
LVL 11

Expert Comment

by:mikeblas
ID: 2710279
> Are you going to help me further?

I guess I'll try, though I deeply resent being strung on for so long.

..B ekiM

0
 
LVL 11

Expert Comment

by:mikeblas
ID: 2714625
You've sent your code. But you've forgotten to provide instructions so I know how to reproduce the problem you're complaining about.

In the meantime, I can't let it go unsaid that the code I've seen so far, on a very cursory examination, is just about the most inefficient and poorly designed that I've seen in quite a while. Your app has lots of problems.

..B ekiM
0
 
LVL 11

Expert Comment

by:mikeblas
ID: 2714737
The code we discussed here, previously, can't be found in your app. Instead, you have code in OnDraw() that adds more and more text to an edit control.

I don't think that's what you want; it seems like you want this edit to indicate changes that happen to your app's state. So, the code doesn't belong in OnDraw(). You should get rid of that OnDraw handler and move it to a function you call whenever the state does change.

..B ekiM
0
 
LVL 2

Author Comment

by:simongod
ID: 2716029
thanks for telling me that my code is not very good.  I am trying to make it better.  You could help by explaining what it is I need to do to make my code more efficient.

i am not really sure how to do that.  Yes that sounds like what I want.  When the client sends the server some info the servers edit control should update.

run both client and server programs.  When the server is running connect to the server and send some data to the server.  Now minimize the server it should go to task menu.  Now try sending data to the server from the client if you restore the server you will notice that the servers edit control did not show the data that the client sent it.  I am trying to fix that.
0
 
LVL 11

Expert Comment

by:mikeblas
ID: 2718167
> You could help by explaining what it is I need to do to
 > make my code more efficient.

I'm not going to do that for only fifty points.

Look: I'll say it again. I've stuck with this question for more than six weeks. You're not even using the code you originally asked about. Now you want me to review your project and make suggestions for fixes.

I've repeatedly asked for more points to continue this level of support, and you've even agreed to that. Yet you've not adjusted the value of the question.

..B ekiM

0
 
LVL 2

Author Comment

by:simongod
ID: 2724161
Adjusted points from 50 to 200
0
 
LVL 2

Author Comment

by:simongod
ID: 2724162
I have adjusted the points is this a more appropriate value?
0
 
LVL 11

Expert Comment

by:mikeblas
ID: 2728918
> I have adjusted the points is this a more appropriate value?

Do I really have a choice?

The fundamental problem is that you don't unerstand the Windows programming model. Windows lets you do event-driven programming. Windows sends your program events--which really are, abstractly--function calls. Your OnDraw() function is called when Windows thinks it is time for your application to repaint the content of its window.

"Repaint" means that that pixels in your window need to be redrawn. If someone drags another window over your app, then drags the window away, at least a part of your window needs to be redrawn. As the obscuring window moves, in fact, small slices of your window needs to be redrawn. So, Windows will send you OnDraw() events over and over--very rapidly, as the obsucring window moves through your client area.

You should also use this mechanism to update the content of your window when you know that the data represented in your application needs to be updated. If you're displaying some variable, for instance, you should call Invalidate() when that variable changes. If Windows knows your window is visible, it will turn around and call OnDraw() to let you paint the change. If your window isn't visible, Windows won't make the call--that saves lots of time!
(See my post of Monday, March 06 2000.)

This matter is explained in detail in any book that serves as an intro to Windows programming. (See my post of Thursday, March 02 2000.)

Instead of using OnDraw() for painting, you've used it to concatenate more and more text into an edit control on your window. That makes no sense: it doesn't serve your needs. You should remove that code from OnDraw(). On the other hand, that code _is_ valuable: you can put it someplace else to make the edit control update whenever it needs to.

The edit control, you see, encapsulates all the details of repainting and drawing and formatting. When you want to, you can just throw some text at it. It will handle OnDraw() for itself and you don't have to worry about it.

On the other hand, if you were using TextOut()--like you originally said you were!--you'd be far more worried with OnDraw() and painting the content yourself.

Of course, even though the edit control handles painting for you, you're still charged with wiring up some mechanism for having the code update the content your window. From the looks of it, you're just using the edit control to log the state of the CMoveMessage::movemessage static variable.

You've already got some code that you call when the movemessage changes; it's in CMoveMessage::SetMoveMessage(). That code takes the new message and holds it in the move message. But that function could also initiate the update of the UI. Since you already call that function whenever the move message changes, it's an ideal time to refresh the UI.

So, instead of this:

void CMoveMessage::SetMoveMessage(CString Message)
{
   movemessage = Message;
}

Let's code this in your SetMoveMessage() routine, instead:

void CMoveMessage::SetMoveMessage(CString Message)
{
   // only do work if the message has changed
   if (movemessage != Message)
   {
      movemessage = Message;

      // get the main window
      CWnd* pWnd = AfxGetMainWnd();
      CFrameWnd* pFrameWnd = DYNAMIC_DOWNCAST(CFrameWnd, pWnd);
      if (pFrameWnd != NULL)
      {
         CView* pActiveView = pFrameWnd->GetActiveView();
       CTimeCardServerView* pTCSView = DYNAMIC_DOWNCAST(CTimeCardServerView, pActiveView);
         if (pTCSView != NULL)
         {
            pTCSView->UpdateStatus();
         }
      }
   }
}

There's substantially more code here. But what it does is pretty simple: it gets the main window of the application and makes sure that the window is of a type that we expect. If it is, we know it should have a CTimeCardServerView in it. If, indeed, it does, we can call the UpdateStatus() member of Time Card Server.

(Note: to make this code compile, you'll need to add these lines to the top of your MoveMessage.CPP file:

#include "TimeCardServerDoc.h"
#include "TimeCardServerView.h"

and make a few more changes, like I'm about to describe.)

This way, the message object notifies the view that its data has changed. Of course, your view code doesn't have an UpdateStatus() function it it--so we'll need to add one. First, edit your TimeCardServerView.h file so that it declares UpdateStatus() as a public member. Just add these lines to the class declaration:

public:
   void UpdateStatus();

And then actually implement the function. Move the code you had in your OnDraw() (it's OK to leave an empty OnDraw() function) to the new function called CTimeCardServerView::UpdateStatus(). You'll put this code into your TimeCardServerView.CPP file, of course. So, the end result will look something like this:

CString newresult = "";
void CTimeCardServerView::OnDraw(CDC* pDC)
{
}

void CTimeCardServerView::UpdateStatus()
{
   CMoveMessage moved;
   CString result = "";
   // 9 = checked 8 = not checked
   result = moved.GetMoveMessage();
      
   if(result != newresult)
   {
      result += "\r\n";
      int len = m_messages.GetWindowTextLength();
      m_messages.SetSel(len,len);
      m_messages.ReplaceSel(result);
      newresult = result;
   }
}

And that's it. Rebuild your app, and you'll find that it works mostly like you expect it to--with one exception. When you first start the app, "Start" isn't shown in the edit control. That's because OnDraw() was called when the window first painted, but the OnDraw() function doesn't do anything anymore. The movemessage has changed, if you think about it; it's changed from being unitialized to "start". To reflect that initial change, just add a call to UpdateStatus() to your  OnInitialUpdate() handler on your view. The resulting code looks like this:

void CTimeCardServerView::OnInitialUpdate()
{
   CFormView::OnInitialUpdate();
   GetParentFrame()->RecalcLayout();
   ResizeParentToFit();

   UpdateStatus();
}

You're all set, now.  Just this one contribution should be worth 200 points--it is as if you've gotten two months of other help for free!

..B ekiM

0
 
LVL 2

Author Comment

by:simongod
ID: 2730936
Adjusted points from 200 to 300
0
 
LVL 2

Author Comment

by:simongod
ID: 2730937
well to make you happy and to show that I REALLY appriciate what you did I will give you more points for what you did.  I haven't checked this code yet but I assume that it will work and even if there is a slim chance that it doesn't the time that you spent writing a detailed explaination is good enough for me.  I hope that you can help me in my future projects.
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

Suggested Solutions

Introduction: Dynamic window placements and drawing on a form, simple usage of windows registry as a storage place for information. Continuing from the first article about sudoku.  There we have designed the application and put a lot of user int…
Introduction: Displaying information on the statusbar.   Continuing from the third article about sudoku.   Open the project in visual studio. Status bar – let’s display the timestamp there.  We need to get the timestamp from the document s…
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 tutorial demonstrates a quick way of adding group price to multiple Magento products.

746 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

16 Experts available now in Live!

Get 1:1 Help Now