Page 1 of 2

[Solved]How to iterate through ListBox

PostPosted: Tue Sep 13, 2011 2:55 am
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;
 
}
 

Re: How to iterate through ListBox

PostPosted: Tue Sep 13, 2011 10:38 am
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 {};

Re: [Solved]How to iterate through ListBox

PostPosted: Wed Sep 14, 2011 4:43 am
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|打球、下棋、听音乐 |
-----------------------------------------------------------------

Re: [Solved]How to iterate through ListBox

PostPosted: Wed Sep 14, 2011 10:35 am
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

Re: [Solved]How to iterate through ListBox

PostPosted: Wed Sep 14, 2011 8:15 pm
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 {};                               
 

Re: [Solved]How to iterate through ListBox

PostPosted: Wed Sep 14, 2011 8:53 pm
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

Re: [Solved]How to iterate through ListBox

PostPosted: Wed Sep 14, 2011 9:02 pm
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

Re: [Solved]How to iterate through ListBox

PostPosted: Wed Sep 14, 2011 9:33 pm
by samsam598
Thank you Jerome,all fixed!!

Re: [Solved]How to iterate through ListBox

PostPosted: Thu Sep 15, 2011 4:48 am
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);
}
 

Re: [Solved]How to iterate through ListBox

PostPosted: Thu Sep 15, 2011 9:13 am
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