Solved

Need some help with ListView and display my data

Posted on 2011-03-16
29
638 Views
Last Modified: 2013-11-20
I ask this question before, and until now I have no solution.
I add a lot of data to my ListView m_List1.
My code ist just fine....only the display of the ListView. For some second the ListView looks like my enclosed picture.
I want that the ListView looks normal with all row etc. before all data show up.

Please help.
500 points with a solution.
Best reards,
Thomas



 
In CmainFrame
CLieferscheine_Dialog *pMeinDialog;
pMeinDialog = new CLieferscheine_Dialog;
pMeinDialog->mb_Kennzahl_first_show_up=true;
pMeinDialog->ShowWindow(SW_SHOW);







Meindialog.cpp
void CLieferscheine_Dialog::OnTimer(UINT nIDEvent)
{
       if(mb_Kennzahl_first_show_up==true)
       {
             //==================================
             Lieferscheine_lesen();//Aufruf von Funktion
             //==================================

              mb_Kennzahl_first_show_up=false;
       }

      CDialog::OnTimer(nIDEvent);
}




BOOL CLieferscheine_Dialog::OnInitDialog()
{
      CDialog::OnInitDialog();

//========================
      SetTimer(1,200,NULL);//   Lieferscheine_lesen();    //Funktion
//========================




void CLieferscheine_Dialog::Lieferscheine_lesen(void)
{
      
      //SQL Server
      m_strConnection = _T("Driver={SQL Server}; Server=DSERVER; Database=OMSDATEN;Uid=;Pwd;");
      
      CString sql;
      
            m_ptrRs = NULL;   
            m_piAdoRecordBinding = NULL;   

      //Initialize the COM environment
      ::CoInitialize(NULL);
      try
      {
                        m_ptrRs.CreateInstance(__uuidof(Recordset));  
                
                       CString str_suche_Jahr="2011";

                        sql.Format("SELECT * FROM ViewLieferscheine WHERE LieferscheinNr<%d\
                       AND DATEPART(Year,Lieferscheindatum)='%s'\
                       ORDER BY LieferscheinNr,Lieferscheindatum DESC",2000000,str_suche_Jahr);
                          
    _bstr_t bstrQuery(sql);
  
                   //Open the recordset object Tabelle 
                  m_ptrRs->Open(_variant_t(bstrQuery),(LPCTSTR)m_strConnection, adOpenDynamic, adLockOptimistic,
                 adCmdUnknown);


                 if(FAILED(m_ptrRs->QueryInterface(__uuidof(IADORecordBinding),(LPVOID *)      
                &m_piAdoRecordBinding)))     _com_issue_error(E_NOINTERFACE);
  
              //Bind the record class to the recordset
             //  m_piAdoRecordBinding->BindToRecordset(&m_rsRecSet);  auf REM setzen wichtig  
      
      
             //***************************************************************************
           _variant_t Holder_LieferscheinNr;
           _variant_t Holder_Name1;
           .
           .
           .

           CString str_LieferscheinNr;
           CString str_Name1;    
           .
           .
           .
            int Spalte =1;
            int hoechstes_Jahr=0;
            int Zeile=0;


              
             
           if(m_ptrRs->BOF) goto Ende_der_while_Schleife;
           //-------------------------------- Lese Datei ------------------------------------
          m_ptrRs->MoveFirst();
       
          while(!m_ptrRs->EndOfFile)
          {
                 Holder_LieferscheinNr              = m_ptrRs->GetCollect("LieferscheinNr");
                 Holder_Name1                         = m_ptrRs->GetCollect("VersandName1");
                 .
                 .
                 .
                 .
                 Holder_Sachbearbeiter            = m_ptrRs->GetCollect("Sachbearbeiter");
             //-------------------------------------------------------------------------------------
                   

             //----------- Copy _variant_t into CString ---------- 
             if(Holder_LieferscheinNr.vt!=VT_NULL)
             {
                      str_LieferscheinNr=(LPCSTR)(_bstr_t)Holder_LieferscheinNr.bstrVal;
                      str_LieferscheinNr.TrimRight();
                      m_LiefNr=str_LieferscheinNr;
              }
              else
              {
                    str_LieferscheinNr.Empty();
               }
               .
               .
               .
               .
               .

          //----------- Copy _variant_t into CString ----------Sachbearbeiter
          if( Holder_Sachbearbeiter.vt!=VT_NULL)
          {
                   str_Sachbearbeiter=(LPCSTR)(_bstr_t) Holder_Sachbearbeiter.bstrVal;
                   str_Sachbearbeiter.TrimRight();
          }
         else
         {
                 str_Sachbearbeiter.Empty();
         }
         //-------------------------------------------------------            
                           
     

        
          //----------------------------- Listenelement ------------------------------------
         m_List1.InsertItem(Zeile,str_LieferscheinNr);   
         m_List1.SetItemText(Zeile,1,str_Lieferscheindatum);
         m_List1.SetItemText(Zeile,2,str_Name1);
         m_List1.SetItemText(Zeile,3,str_KDNr);
         m_List1.SetItemText(Zeile,4,str_Gewicht);
         m_List1.SetItemText(Zeile,5,str_Lieferbedingung_Bezeichnung);
         m_List1.SetItemText(Zeile,6,str_Fakturiert);
         m_List1.SetItemText(Zeile,7,str_Sachbearbeiter);
         //------------------------------------------------------------------------------------




       
          //Verhindert Error-Meldung BOF / EOF
          if(m_ptrRs->EndOfFile)break;

         //nächste Zeile
        Zeile=Zeile+1;
                    
        m_ptrRs->MoveNext();

           
     }
                    
}

//#####################################################################  
            //---------- Any erros? -------------
          catch (_com_error &e)
          {
                  //Display the error
                  CString x=m_LiefNr;
                 GenerateError(e.Error(), e.Description());
           }
            //-----------------------------------
        
           Ende_der_while_Schleife:
      
     

             //Close the recordset
              if(m_ptrRs)  m_ptrRs->Close();           

            // Do we have a valid pointer to the record binding?
           if(m_piAdoRecordBinding) m_piAdoRecordBinding->Release();            
     
          //Set the recordset pointer to NULL
           m_ptrRs = NULL;           
      
         //Shut down the COM environment
          ::CoUninitialize();
}

Open in new window

1.BMP
0
Comment
Question by:tsp2002
  • 8
  • 7
  • 7
  • +1
29 Comments
 
LVL 44

Accepted Solution

by:
AndyAinscow earned 250 total points
ID: 35147421
You could use a thread to load the items from the database into memory then load the list from the items in memory.

Make certain that sort is set to none as a litview property - otherwise the listview will attempt to sort after adding each individual item.

You could have the list view as hidden until all items are added then show it.  (simplest to implement)
0
 

Author Comment

by:tsp2002
ID: 35147444
You could use a thread to load the items from the database into memory then load the list from the items in memory.
How should I do this...I don´t know....Please advise....thanks
0
 
LVL 5

Expert Comment

by:JohnCz
ID: 35147470
Your problem is that you are handling vast amount of data in a tight loop.
This does not allow any message processing since a while loop takes all CPU time.
Create thread and move all code handling data retrieval to this thread.
0
 

Author Comment

by:tsp2002
ID: 35147505
Your problem is that you are handling vast amount of data in a tight loop.
This does not allow any message processing since a while loop takes all CPU time.
Create thread and move all code handling data retrieval to this thread.

...I don´t know what a thread is....sorry and how should I do this
0
 
LVL 44

Assisted Solution

by:AndyAinscow
AndyAinscow earned 250 total points
ID: 35147518
If you don't know how to do any threading then I would suggest you just hide the listview and show it once the filling is done, at least as a temporary measure.

Concerning threads - have a look at this article:
http://www.experts-exchange.com/Programming/System/Windows__Programming/MFC/A_3831-Sudoku-a-complete-MFC-application-Part-11.html

There is an example of using a thread and how to transfer data between a thread and the main application.  In reality multi threading isn't difficult, just don't pass MFC objects between threads.
0
 
LVL 32

Expert Comment

by:sarabande
ID: 35148433
you can start a thread from dialog class - for example in OnInitDialog - by

   AfxBeginThread(threadFunction, this);

where threadFunction can be a static member function of your dialog class

#include <vector>
...
class CLieferscheine_Dialog ...
{
    ...
public:
    bool m_threadDone;
    std::vector<MyRecord> m_records;    
    static UINT threadFunction(void * p)
    {
          // get pointer to dialog
          CLieferscheine_Dialog * pthis = (CLieferscheine_Dialog* )p; 
          pthis->Lieferscheine_lesen();
          pthis->m_threadDone = true;
    }
};

Open in new window


the 'Lieferscheine_lesen' must not fill the list control but would fill the m_records vector with 'MyRecord' objects. a MyRecord simply is a struct (you can add it to dialog class header) which has all attributes of a row of the list as members. you would do it like

   
MyRecord record;
   record.Holder_LieferscheinNr  = m_ptrRs->GetCollect("LieferscheinNr");
   ....
   m_records.push_back(record);

Open in new window



in the OnTimer you would check for the m_threadDone and if it was true you would fill the m_List1 from the m_records vector, what should be much faster.

Sara
 
0
 
LVL 5

Expert Comment

by:JohnCz
ID: 35149282
Passing a pointer to an MFC object that wraps a handle is not thread safe. Instead, windows handle should be passed and used to communicate with an object.

Follow this link to download fully functional sample. It uses direct call to populate list and thread to do the same thing.
You will see the difference. The code is self-explanatory. You can change handling to accommodate tou particular needs but a general concept remains the same.

Sample Download
0
 
LVL 32

Expert Comment

by:sarabande
ID: 35149687
it is threadsafe as long as the thread and mainthread do access members exclusively.

that is the case here. while the thread is running it exclusively uses the recordset, and the array of record data.

when it is done, the array of data can be accessed from OnTimer in main thread.

the only thing left is that the user may not close the dialog while the thread is running. that can be handled in OnOk and OnCancel.

Sara
0
 
LVL 5

Expert Comment

by:JohnCz
ID: 35149908
Hi Sara,
>>it is threadsafe as long as the thread and mainthread do access members exclusively.
What is multithread and how is it different from a thread? Please, define exclusive access.

You are missing a point. I am not referring to a recordset. I am referring to dialog object pointer that in your snippet is passed to a thread as parameter and used to access member functions and variables fro this thread.
This is what is unsafe. MFC used permanent and temporary handles. Thread stores temporary and permanent handles maps in a local thread storage and handle you are passing with an object may be mapped to a different object in local storage. The best case scenario would be that handles are mapped to the same object but it will take more time to access a member function than posting a message.
The worst case scenario is that application will crash trying to access function in the object that does not have it.
0
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 35150151
Two points with the code sample from sara.
1) As I pointed out earlier and JohnCz repeats - do NOT pass MFC objects between threads.  It is a recipe for disaster.
2) This looks like multithreading but it isn't because the thread instructs the MAIN THREAD to do the work.  So this is just a more complex way of doing what you first tried with everything in the dialog.
0
 
LVL 32

Expert Comment

by:sarabande
ID: 35150179
the 'this' pointer was passed for simplicity only.

if you look on the code that was processed for reading the data via odbc you will see that it doesn't need any dialog data (beside of filling the list which i told to move to the OnTimer).

instead, we could move all recordset and com stuff to a new class together with the vector of data and the flags to control the thread. also the member function to perform the query would be a member of that new class. then the dialog (or application or document or mainframe) could create an instance of that class and start the thread passing a pointer to that helper object. it also could provide a get function so that the CMeinDialog::OnTimer could access this pointer after the thread has read the data.

but beside that the design is better it is nothing won regarding thread-safety. and you will agree that it is not easier to understand than the above code where i tried to use as much of the existing as possible.

Sara
0
 
LVL 32

Expert Comment

by:sarabande
ID: 35150279
Andy, when the thread calls the Lieferschein_lesen this function runs in thread context.

Sara
0
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 35150802
@Sara
I stand corrected.  I had met something similar some time before where it did behave as I said.  I've just done a test with this and is does allow the dialog to respond.  I'm not quite certain what that 'buggy' application was doing differently.

It does however have the problem said by JohnCz - namely close the dialog and one has a crash.

In view of that I would strongly recommend having the thread procedure completely separated from the dialog.


Whichever I would not pass an MFC object into the thread, Microsoft explicitly states MFC objects are not thread safe (and it isn't just simultaneous access, there are thread local variables coming into play).  It might work at times, someone might 'optimise' the code at a later time and boom!!
0
 

Author Comment

by:tsp2002
ID: 35154696
Hi folks,
thanks for all the answers....but now I am confused.
Is there is easy and simple solution to my problem?
Thank you.
Best regards,
Thomas
0
What Should I Do With This Threat Intelligence?

Are you wondering if you actually need threat intelligence? The answer is yes. We explain the basics for creating useful threat intelligence.

 
LVL 44

Expert Comment

by:AndyAinscow
ID: 35154816
As I said earlier the very simplest is just to hide the listview and show it once it is filled.  You could also display a label - eg. "Bitte warten" and hide that once the list is displayed.

A better solution is to use a separate thread, but that is a little bit more complex.

Did you look at the article (link I posted) - that gives example code for the sort of things you require for a multi threaded solution.
0
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 35154837
One other point - check that the 'sort' property of the listview is set to none.  If it isn't then it can slow up the addition of items to the list tremendously.
0
 
LVL 32

Assisted Solution

by:sarabande
sarabande earned 125 total points
ID: 35154882
yes, you are right. but we should tsp2002 give a chance to say whether threading is the way to go or whether he (Thomas) wants to go with pseudo-threading in the OnTimer.

Thomas, the above code starts a new thread what is a complete separate unit which runs asynchronously to the main thread. your main thread currently is running an endless loop where it processes and produces messages. one of those messages for example is generated when clicking with the mouse to a button. or when a new dialog was started. or when a control needs to get drawn, and, and, and. there is no activity in your program which is not driven by a message and to keep the game going on you have to create new messages in your message handlers. with a thread you break the rule and the thread might do any other job which is not dependent on the message queue of the main thread (and therefore does not block and freeze your current dialog). so the idea of the concept above is to put the lengthy query into the thread-unit and return from OnTimer (what is the message handler of the WM_TIMER message). because the thread runs asynchronously it may not write to the message queue same time as the could do it cause that could lead to corruption. therefore the thread only is allowed to variables which were not currently used from the main thread as well. that is called thread-safety. there are some possibilities of the operation system to guarantee exclusive access to shared resources but it seems to me that your query very well could run independently from the main thread and collect the data into a container rather than putting it immediately to the list control. when the thread is done it should notify that it is done and then the main thread safely can access the container filled before.

if you want to go that way, we will help you to make that safe.

Sara
0
 
LVL 5

Assisted Solution

by:JohnCz
JohnCz earned 125 total points
ID: 35156039
>>yes, you are right. but we should tsp2002 give a chance to say whether threading is the way to go or whether he (Thomas) wants to go with pseudo-threading in the OnTimer.

Of course all of us have the right to decide. However people ask questions to get advice and advice should lead to resolving problem a proper way, learning good programming practices.
Anybody can grab red hot iron rod going against an advice to not to do it and the result?

>>Thomas, the above code starts a new thread what is a complete separate unit which runs asynchronously to the main thread.

That depends how asynchronous is understood. For sure bot threads: main thread and workers thread run independently (more below).

 >>main thread currently is running an endless loop where it processes and produces messages. one of those messages for example is generated when clicking with the mouse to a button. or when a new dialog was started. or when a control needs to get drawn, and, and, and. there is no activity in your program which is not driven by a message

I am sure you are referring to a main message loop that has to be established for any GUI thread. This message loop retrieves and dispatches messages that are placed in a message queue by the system for each message delivered by PostMessage. In addition Window procedure is called directly when message is delivered using SendMessage. All GUI elements are updated when thread is in an idle state when there is no user interaction.

>>and to keep the game going on you have to create new messages in your message handlers.

New message? Could you elaborate?

>>with a thread you break the rule and the thread might do any other job which is not dependent on the message queue of the main thread (and therefore does not block and freeze your current dialog).

I would say you follow the rule

Early Windows OS did not have a multithreading model, since it was not preemptive. That means that at any time CPU assigned a time slice for a process to handle messages, all other processes were waiting until the one that owned a time slice was done.
In a nutshell:
Newer version of Windows have multithreading capability, since OS is preemptive and process is receiving time slice of the predetermine length. If handling messages takes longer, system puts this process in halt until the next time slice is permitted. This allows branching execution to threads other than main thread of execution, each having own time slice.
Since then, all processing requiring long time could be handles in a different thread allowing main thread doing uninterrupted message handling including GUI elements update.
In your case as I have indicated in my previous post and Sara above, processing code in a while loop takes all CPU time allocated for a process and blocks main thread from processing messages. That is why, all application is frozen and GUI is not updated.
Using timer as an indicator that thread is done? Well, the timer messages are also blocked. But it will work without even checking m_threadDone variable, since as soon as while loop exits,  dialog will receive timer message that was not received when loop was executed.

>>because the thread runs asynchronously it may not write to the message queue same time as the could do it cause that could lead to corruption . . .

Thread does not write anything to the message queue; it can send/post messages though and that has nothing to do with accessing variables. This is completely different and requires thread synchronization when accessing common resources.

Thomas,

I do not know if you had a chance to download my sample. It does not require any synchronization, since worker thread is not accessing any resources; it simply fills the list controls with data.
You can use different approach but it is not recommended. You will sooner or later come across more difficult problem and tusing thread would be inevitable. My advice is: learn how to use threads.
To achieve similar results you can create local message loop as it was done before preemptive OS. Insert this piece of code inside of the while loop:

 		while(PeekMessage(&msg, m_hWnd, 0, 0, PM_REMOVE))
		{
			PreTranslateMessage(&msg);
			DispatchMessage(&msg);
		}

/

Open in new window



0
 
LVL 5

Expert Comment

by:JohnCz
ID: 35156058
Better yet use this (corrected) code:

		while(PeekMessage(&msg, m_hWnd, 0, 0, PM_REMOVE))
		{
			if(PreTranslateMessage(&msg))
			{
				continue;
			}
			DispatchMessage(&msg);
		}

Open in new window

0
 

Author Comment

by:tsp2002
ID: 35156417
Hello folks,
thank you so much for all the answers.
threads are to difficult for me, I think.

I am still learing to use MFC, c++ . I am sure that threads will work.

But I found a simple solution without any threads.
The hint came from Andy Ainscow. (" I would suggest you just hide the listview.....")

You see the mayor problem is that the user sees the CListCtrl like my picture above (1.BMP)
And this could take some seconds because of the large data to add.

So I did the following:
I create a new ListView
same size with all columns like the original

in OnInitDialog()
I hide the original ListView and show the user the kind of dummy ListView
CListCtrl *pList = new CListCtrl;
pList->Create(WS_CHILD|WS_VISIBLE|WS_BORDER|LVS_REPORT,CRect(12,16,1000,300),this,IDC_LIST99);

than.....after all data was added to the original
I hide the dummy and show the original ListView with all the data.
I also add a progress control so the user sees the loading.
The user never notice that there are to ListViews.
I now that is not a professional solution....but a simple one.

I will split the points for you all.

Thanks for the great help.
Best regards,
Thomas






 
0
 

Author Closing Comment

by:tsp2002
ID: 35156430
Thank you very much
0
 
LVL 32

Expert Comment

by:sarabande
ID: 35156636
John, the while(PeekMessage... also would dispatch WM_CLOSE and WM_DESTROY message and can close and destroy the current dialog by that.

then it could/would crash in the following.

Sara





0
 
LVL 32

Expert Comment

by:sarabande
ID: 35156660
Thomas, it is a good solution if you are happy with that.

Sara
0
 

Author Comment

by:tsp2002
ID: 35156755
Sara, thank you ....yes that works for me just fine.
Best regards,
Thomas
0
 
LVL 5

Expert Comment

by:JohnCz
ID: 35157393
John, the while(PeekMessage... also would dispatch WM_CLOSE and WM_DESTROY message and can close and destroy the current dialog by that.

100% correct. It would be wise to filter both out.
0
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 35157472
Out of interest do you see any speed improvement with having the listview hidden whilst being filled?
0
 
LVL 5

Expert Comment

by:JohnCz
ID: 35157811
My 2 pennies:

I do not think there will be any speed improvement. Besides, as a user. I would not want to see application frozen wirhout knowing what is going on.
Hiding one and creating and destroying another also do not resolve a problem of the app being not responsive. I think if in design using thread or other means to make app appearing life, would be calling LockWindowUpdate before entering a loop and UnlockWindowUpdate after a loop, showing hour glass curson (CWaitCursor for example).

I also think that using local message loop vs. thread impairs performance if processing data takes a long time.
Using thread makes all processing running in more concurrent like fashion.
0
 
LVL 44

Expert Comment

by:AndyAinscow
ID: 35157909
I'm not really expecting seeing any improvement, just interested to know if there was any - the listview does quite a few things as items are added.

Performance - well the very first part of the first comment was to use a thread.  Anything else isn't as good but may be good enough.  
0
 

Author Comment

by:tsp2002
ID: 35163374
John" I would not want to see application frozen without knowing what is going on."
That is not the case. I have added a progress control also I will add a hour glass cursor.
I also can add a smal message box "Please wait....loading all Lieferscheine" etc.
So the user will know what is going on there.

This way....it looks much better and that will do the trick.
Performance is the same.
It takes to much time and afford for me to create a threat that will work.
Best regards,
Thomas

0

Featured Post

What Is Threat Intelligence?

Threat intelligence is often discussed, but rarely understood. Starting with a precise definition, along with clear business goals, is essential.

Join & Write a Comment

Suggested Solutions

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 …
Introduction: Dialogs (2) modeless dialog and a worker thread.  Handling data shared between threads.  Recursive functions. Continuing from the tenth article about sudoku.   Last article we worked with a modal dialog to help maintain informat…
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.
In this tutorial you'll learn about bandwidth monitoring with flows and packet sniffing with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're interested in additional methods for monitoring bandwidt…

708 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