[Solved]How to iterate through ListBox

General help with the Ecere Cross Platform GUI toolkit: Window, common controls, events, etc.
Help with the 2D Graphics library: Surface, Display, Bitmap, Font and others.
samsam598
Posts: 212
Joined: Thu Apr 14, 2011 9:44 pm

[Solved]How to iterate through ListBox

Post by samsam598 »

Hi,

I want to pick every row of a given listBox but are not able to find the right method.Could you please figure me out?

Code: Select all

 
 
double Calculate()
{  
     uint i;
     uint counts=listBox1.rowCount;
     double result=0.0f;
 
     for(i=0;i<counts;i++)
    {
        result+=atof(listBox1.row[i].string);//===>how to iterate each row?
    }
    if(counts)
         result/=counts;
   return result;
 
}
 
Last edited by samsam598 on Wed Sep 14, 2011 4:38 am, edited 1 time in total.
jerome
Site Admin
Posts: 608
Joined: Sat Jan 16, 2010 11:16 pm

Re: How to iterate through ListBox

Post by jerome »

Sam,

Just like with child windows, you can use ListBox::firstRow and DataRow::next to iterate through the rows:

Code: Select all

   double Calculate()
   {  
      uint counts=listBox1.rowCount;
      double result=0.0f;
      DataRow row;
 
      for(row = listBox1.firstRow; row; row = row.next)
         result+=atof(row.string);
      if(counts)
         result/=counts;
      return result;    
   }
It might be interesting to point out that a ListBox in Ecere is a flexible data control that can work with any datatypes(s). You could store the number as doubles within the list box this way:

Code: Select all

import "ecere"
 
class Form1 : Window
{
   text = "Form1";
   background = activeBorder;
   borderStyle = sizable;
   hasMaximize = true;
   hasMinimize = true;
   hasClose = true;
   size = { 640, 480 };
 
   ListBox listBox1 { this, text = "listBox1", size = { 156, 132 }, position = { 128, 96 } };
   DataField df { class(double) };
   Form1()
   {
      listBox1.AddField(df);
      listBox1.AddRow().SetData(df, 4.5);
      listBox1.AddRow().SetData(df, 2.4);
      listBox1.AddRow().SetData(df, 5.9);
 
      PrintLn(Calculate());
   }
 
   double Calculate()
   {  
      uint counts=listBox1.rowCount;
      double result = 0;
      DataRow row;
      for(row = listBox1.firstRow; row; row = row.next)
         result += row.GetData(df);
      if(counts)
         result/=counts;
      return result;    
   }
}
 
Form1 form1 {};
samsam598
Posts: 212
Joined: Thu Apr 14, 2011 9:44 pm

Re: [Solved]How to iterate through ListBox

Post by samsam598 »

Cool.

How to make use of ListBox and DataField to create a database grid?Or any other solution should be fine also.Please note I need the headers as well.Thanks.
-----------------------------------------------------------------
ID |姓名 |出生日期 |喜好 |
-----------------------------------------------------------------
0001 | 王小早 |1987/06/26|打球、下棋、听音乐 |
-----------------------------------------------------------------
jerome
Site Admin
Posts: 608
Joined: Sat Jan 16, 2010 11:16 pm

Re: [Solved]How to iterate through ListBox

Post by jerome »

Sam,

Here's sample code for a grid view ListBox. It uses 'ShortDate.ec' in sdk/extras, you need to add the file to the project.

Code: Select all

import "ecere"
import "ShortDate"
 
class Form1 : Window
{
   text = "Form1";
   background = activeBorder;
   borderStyle = sizable;
   hasMaximize = true;
   hasMinimize = true;
   nativeDecorations = true;
   hasClose = true;
   size = { 640, 480 };
 
   ListBox listBox1 { this, hasHeader = true, moveFields = true, moveRows = true, resizable = true, sortable = true, size = { 500, 132 }, position = { 20, 96 } };
 
   DataField dfId { class(uint), width = 40, header = "ID" };
   DataField dfName { class(String), width = 100, header = "姓名", editable = true };
   DataField dfBirthday { class(ShortDate), width = 80, header = "出生日期", editable = true };
   DataField dfHobbies { class(String), width = 200, header = "喜好", editable = true };
 
   Form1()
   {
      DataRow row;
 
      listBox1.AddField(dfId);
      listBox1.AddField(dfName);
      listBox1.AddField(dfBirthday);
      listBox1.AddField(dfHobbies);
 
      row = listBox1.AddRow();
      row.tag = 1;
      row.SetData(dfId, 1);
      row.SetData(dfName, "王小早");
      row.SetData(dfBirthday, Date { 1987, june, 26 });
      row.SetData(dfHobbies, "打球、下棋、听音乐");
 
      row = listBox1.AddRow();
      row.tag = 2;
      row.SetData(dfId, 2);
      row.SetData(dfName, "王小早 2");
      row.SetData(dfBirthday, Date { 1988, june, 26 });
      row.SetData(dfHobbies, "打球、下棋、听音乐");
   }
}
 
Form1 form1 {};
The ListBox control is also a TreeView :D (treeBranches = true, collapseControl = true)

You can also use EDA to plug in a database directly, e.g. with eda::Table::GUIListBoxAddFields and eda::Table::GUIListBoxAddRowsField. This will automatically add DataFields and DataRows to the ListBox.

Regards,

Jerome
samsam598
Posts: 212
Joined: Thu Apr 14, 2011 9:44 pm

Re: [Solved]How to iterate through ListBox

Post by samsam598 »

Thanks Jerome,that's great!
After adding double-click event,noticed there is a sligth problem,when double click on the second row,the sort order of the both row changed.Don't know why.

Code: Select all

 
import "ecere"
import "ShortDate" 
class Form1 : Window
{
   text = "Form1";
   background = activeBorder;
   borderStyle = sizable;
   hasMaximize = true;
   hasMinimize = true;
   hasClose = true;
   size = { 640, 480 };
   nativeDecorations = true;
 
   DataField dfId 
      {
          class(uint), width = 40, 
          header = "ID" 
      };
   DataField dfName 
      {
          class(String), width = 100, 
          header = "姓名", editable = true 
      };
   DataField dfBirthday 
      {
          class(ShortDate), width = 80, 
          header = "出生日期", editable = true 
      };
   DataField dfHobbies 
      {
          class(String), width = 200, 
          header = "喜好", editable = true 
      };
   ListBox listBox1 
      {
      this, size = { 500, 132 }, position = { 24, 24 }, moveRows = true, true, true, hasHeader = true, sortable = true;
 
      bool NotifyDoubleClick(ListBox listBox, int x, int y, Modifiers mods)
      {
         char* name=listBox.currentRow.GetData(dfName);
         int id=listBox.currentRow.GetData(dfId);
         char IDstr[3];
         char temp[20];
         sprintf(IDstr,":ID=%d",id);
         strcpy(temp,name);
         strcat(temp,IDstr);
 
         MessageBox{contents=temp}.Modal();
         delete name;
         return true;
      }
   };
 
   Form1()   
      {
               DataRow row;       
               listBox1.AddField(dfId);      
               listBox1.AddField(dfName);      
               listBox1.AddField(dfBirthday);      
               listBox1.AddField(dfHobbies);       
               row = listBox1.AddRow();      
               row.tag = 1;      
               row.SetData(dfId, 1);      
               row.SetData(dfName, "王小早");      
               row.SetData(dfBirthday, Date { 1987, june, 26 });      
               row.SetData(dfHobbies, "打球、下棋、听音乐");       
               row = listBox1.AddRow();      
               row.tag = 2;      
               row.SetData(dfId, 2);      
               row.SetData(dfName, "王小早 2");      
               row.SetData(dfBirthday, Date { 1988, june, 26 });      
               row.SetData(dfHobbies, "打球、下棋、听音乐");   
        }
} 
Form1 form1 {};
 
And one more question,trying win32 API PlaySound in a new added button event caused the app failed to build.

Code: Select all

 
Compiling...
grid.ec
   grid.ec:1:1: error: Couldn't open obj/debug.win32\grid.sym
 
grid (Debug) - 1 error, no warning          
Last time I encountered the same issue when I tried win32 API ShellExecute,since it failed,I use ShellOpen in the eC SDK.Below is the full source code with the issue.Please also note I've added winmm(.a) in the linker and placed the sound file in the project folder.

Code: Select all

 
#ifdef __WIN32__
#define WIN32_LEAN_AND_MEAN
#include <windefs.h>
#include <windows.h> 
#include <mmsystem.h>
#endif       
 
 
import "ecere"
import "ShortDate" 
class Form1 : Window
{
   text = "Form1";
   background = activeBorder;
   borderStyle = sizable;
   hasMaximize = true;
   hasMinimize = true;
   hasClose = true;
   size = { 640, 480 };
   nativeDecorations = true;
 
   DataField dfId 
      {
          class(uint), width = 40, 
          header = "ID" 
      };
   DataField dfName 
      {
          class(String), width = 100, 
          header = "姓名", editable = true 
      };
   DataField dfBirthday 
      {
          class(ShortDate), width = 80, 
          header = "出生日期", editable = true 
      };
   DataField dfHobbies 
      {
          class(String), width = 200, 
          header = "喜好", editable = true 
      };
   Button button1
   {
      this, text = "button1", position = { 384, 192 };
 
      bool NotifyClicked(Button button, int x, int y, Modifiers mods)
      {
         PlaySound("hellowin.wav", NULL, SND_FILENAME | SND_ASYNC);
         return true;
      }
   };
   ListBox listBox1 
      {
      this, size = { 500, 132 }, position = { 24, 24 }, moveRows = true, true, true, hasHeader = true, sortable = true;
 
      bool NotifyDoubleClick(ListBox listBox, int x, int y, Modifiers mods)
      {
         char* name=listBox.currentRow.GetData(dfName);
         int id=listBox.currentRow.GetData(dfId);
         char IDstr[3];
         char temp[20];
         sprintf(IDstr,":ID=%d",id);
         strcpy(temp,name);
         strcat(temp,IDstr);
 
         MessageBox{contents=temp}.Modal();
         delete name;
         return true;
      }
   };
 
   Form1()   
      {
               DataRow row;       
               listBox1.AddField(dfId);      
               listBox1.AddField(dfName);      
               listBox1.AddField(dfBirthday);      
               listBox1.AddField(dfHobbies);       
               row = listBox1.AddRow();      
               row.tag = 1;      
               row.SetData(dfId, 1);      
               row.SetData(dfName, "王小早");      
               row.SetData(dfBirthday, Date { 1987, june, 26 });      
               row.SetData(dfHobbies, "打球、下棋、听音乐");       
               row = listBox1.AddRow();      
               row.tag = 2;      
               row.SetData(dfId, 2);      
               row.SetData(dfName, "王小早 2");      
               row.SetData(dfBirthday, Date { 1988, june, 26 });      
               row.SetData(dfHobbies, "打球、下棋、听音乐");   
        }
} 
Form1 form1 {};                               
 
jerome
Site Admin
Posts: 608
Joined: Sat Jan 16, 2010 11:16 pm

Re: [Solved]How to iterate through ListBox

Post by jerome »

Hi Sam,

I noticed a couple of problems in your NotifyDoubleClick method.

First, you're deleting 'name' which was obtained through 'DataRow::GetData'.
The data belongs to the ListBox, so you should not delete that (it causes all kinds of problems when I ran your sample).

Then you have a buffer overflow when using sprintf with IDstr, you're putting at least 6 characters (:,I,D,=,1,\0) in that buffer and you only reserved space for 3. I suggest you make it bigger, like 30. temp is a bit on the small size as well, if you have a bigger name, keep in mind most Chinese characters will take up 3 bytes each in UTF-8 encoding.

Finally, since the ListBox has 'moveRows' set to true, when you click and drag a row it automatically drags it around. That's the behavior that you're seeing as changing the sort order, it's triggered after the NotifyDoubleClick finishes. To prevent this from happening, simply return 'false' from NotifyDoubleClick, and the ListBox will not continue processing the current mouse event. Of course you can also disable the moveRows functionality (it's normally not used in conjunction with 'sortable').

Here's your updated NotifyDoubleClick:

Code: Select all

      bool NotifyDoubleClick(ListBox listBox, int x, int y, Modifiers mods)
      {
         char* name=listBox.currentRow.GetData(dfName);
         int id=listBox.currentRow.GetData(dfId);
         char IDstr[100];
         char temp[200];
         sprintf(IDstr,":ID=%d",id);
         strcpy(temp,name);
         strcat(temp,IDstr);
 
         MessageBox{contents=temp}.Modal();
         return false;
      }
Regards,

Jerome
jerome
Site Admin
Posts: 608
Joined: Sat Jan 16, 2010 11:16 pm

Re: [Solved]How to iterate through ListBox

Post by jerome »

Sam,

The following will build:

Code: Select all

#ifdef __WIN32__
#define WIN32_LEAN_AND_MEAN
#include <windows.h> 
#include <mmsystem.h>
#endif
#undef MessageBox
It seems that windefs.h is conflicting with a lot of eC data types, so it it would just be a matter of singling which ones are problematic and renaming them temporarily with #define / #undef macros around the #include command, but you don't need to include it anyways.

Cheers,

Jerome
samsam598
Posts: 212
Joined: Thu Apr 14, 2011 9:44 pm

Re: [Solved]How to iterate through ListBox

Post by samsam598 »

Thank you Jerome,all fixed!!
samsam598
Posts: 212
Joined: Thu Apr 14, 2011 9:44 pm

Re: [Solved]How to iterate through ListBox

Post by samsam598 »

One more question,sorry!
If I wrote a separate function which will fill in the listBox with data and then pass the function to the form's constructor,the app will compile,but when runs,it will crash.So what makes this difference?

Code: Select all

 
Form1()
{
  //fill in listBox with data,fine
}
 
but this one won't work properly:

Code: Select all

 
void FillListBox()
{
   //fill in listBox with data
}
Form1()
{
    FillListBox();
}
 
This won't neither:

Code: Select all

 
void FillListBox(ListBox listBox1)
{
   //same code as in the original example
}
Form1()
{
    FillListBox(listBox1);
}
 
jerome
Site Admin
Posts: 608
Joined: Sat Jan 16, 2010 11:16 pm

Re: [Solved]How to iterate through ListBox

Post by jerome »

Sam,

It really doesn't make any difference. The problem must be somewhere else.
Could you please paste the entire sample that crashes?

EDIT: I've managed to reproduce it, and found that the problem is a char buffer (name[10], in this case) too small again. This works:

Code: Select all

 
import "ecere"
import "ShortDate" 
class Form1 : Window
{
   text = "Form1";
   background = activeBorder;
   borderStyle = sizable;
   hasMaximize = true;
   hasMinimize = true;
   hasClose = true;
   hasStatusBar = true;
   size = { 640, 480 };
   anchor = { horz = -3, vert = -5 };
   nativeDecorations = true;
 
   DataField dfId 
   {
       class(uint), 
       width = 40, header = "ID" 
   };
   DataField dfName 
   {
       class(String), 
       width = 100, header = "姓名", editable = true  ;  
   };
   DataField dfBirthday 
   {
       class(ShortDate), 
       width = 80, header = "出生日期", editable = true 
   };
   DataField dfHobbies 
   {
       class(String), 
       width = 200, header = "喜好", editable = true 
   };
   ListBox listBox1 
   {
      this, foreground = darkCyan, font = { "Tahoma", 10 }, anchor = { left = 0, top = 0, right = 0, bottom = 0 }, moveRows = true, hasHeader = true, sortable = true;
 
      bool NotifyDoubleClick(ListBox listBox, int x, int y, Modifiers mods)
      {
         char* name=listBox.currentRow.GetData(dfName);
         int id=listBox.currentRow.GetData(dfId);
         char IDstr[100];
         char temp[200];
         sprintf(IDstr,":ID=%d",id);
         strcpy(temp,name);
         strcat(temp,IDstr);
 
         //show name and ID you've selected in the statusBar:
         statusBar.text=temp;
 
         return false;
      }
   };
 
   void FillListBoxes()
   {
       DataRow row;
       int i;
       char name[100];  
       char hobbies[200];  
       listBox1.AddField(dfId);      
       listBox1.AddField(dfName);      
       listBox1.AddField(dfBirthday);      
       listBox1.AddField(dfHobbies);   
       for(i=0;i<50;i++)
       {
          sprintf(name,"王小早(%d)",i+1); 
          sprintf(hobbies,"打球、下棋、听音乐 (%d)",i+1);  
          row = listBox1.AddRow();      
          row.tag = i+1;      
          row.SetData(dfId, i+1);      
          row.SetData(dfName,name);      
          row.SetData(dfBirthday, Date { 1960+i, june, 26 });      
          row.SetData(dfHobbies, hobbies);       
 
       }              
   }
 
   Form1()   
   {
      FillListBoxes();
   }
} 
Form1 form1 {};
Thanks,

Jerome
Post Reply