Board index » cppbuilder » ComboBox as ListItem

ComboBox as ListItem


2004-09-03 05:58:10 PM
cppbuilder54
I'm wondering if anyone know how to put a ComboBox or something like it in
one of the columns.
You know the ones you can change the text in an Item but only to some
pre-defined texts.
Preferably the ability to select one Item named "..." or something so a box
pops up and you can type in whatever too.
Hope I made some sense
Any questions, just ask, ok?
/Stefan
 
 

Re:ComboBox as ListItem

"Stefan L" < XXXX@XXXXX.COM >wrote:
Quote

I'm wondering if anyone know how to put a ComboBox or
something like it in one of the columns. [...]
Converted from Delphi to C++ from a post by Peter Below (TeamB)
Untested:
//-------------------------------------------------------------
private: // User declarations
int HeaderHeight;
TWndMethod OldListViewWndProc;
void __fastcall NewListViewWndProc( TMessage& );
void __fastcall AttachCombobox();
public: // User declarations
__fastcall TForm1::~TForm1();
// in the form's class
//-------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
TRect R;
// You might need to populate the ListView here
HWND hWnd = ::GetWindow( ListView1->Handle, GW_CHILD );
::GetWindowRect( hWnd, &R );
HeaderHeight = R.bottom - R.top;
OldListViewWndProc = ListView1->WindowProc;
ListView1->WindowProc = NewListViewWndProc;
AttachCombobox();
}
//-------------------------------------------------------------
__fastcall TForm1::~TForm1()
{
ListView1->WindowProc = OldListViewWndProc;
}
//-------------------------------------------------------------
void __fastcall TForm1::NewListViewWndProc( TMessage &Message )
{
OldListViewWndProc( Message );
if( Message.Msg == WM_VSCROLL || Message.Msg == WM_HSCROLL )
{
if( Message.WParamLo != SB_ENDSCROLL )
{
AttachCombobox();
}
}
}
//-------------------------------------------------------------
void __fastcall TForm1::AttachCombobox()
{
TRect R = Rect( 0, 0, 0, 0 );
::ListView_GetSubItemRect( ListView1->Handle, 4, 1, LVIR_BOUNDS, &R );
ComboBox1->Parent = ListView1;
ComboBox1->BoundsRect = R;
ComboBox1->Visible = R.top>= HeaderHeight;
}
//-------------------------------------------------------------
~ JD
 

Re:ComboBox as ListItem

It almost worked :)
It compiles up to the point in AttachComboBox() where it says
::ListView_GetSubItemRect( ListView1->Handle, 4, 1, LVIR_BOUNDS, &R );
there it stops and complains about "Call to undefined function". I searched
through the helpfiles
in hope of finding some similar code, but the function doesn't exist
anywhere.
But thanks anyway.
"JD" < XXXX@XXXXX.COM >skrev i meddelandet
Quote

"Stefan L" < XXXX@XXXXX.COM >wrote:
>
>I'm wondering if anyone know how to put a ComboBox or
>something like it in one of the columns. [...]

Converted from Delphi to C++ from a post by Peter Below (TeamB)
Untested:

//-------------------------------------------------------------
private: // User declarations
int HeaderHeight;
TWndMethod OldListViewWndProc;
void __fastcall NewListViewWndProc( TMessage& );
void __fastcall AttachCombobox();
public: // User declarations
__fastcall TForm1::~TForm1();

// in the form's class
//-------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
TRect R;
// You might need to populate the ListView here
HWND hWnd = ::GetWindow( ListView1->Handle, GW_CHILD );
::GetWindowRect( hWnd, &R );
HeaderHeight = R.bottom - R.top;
OldListViewWndProc = ListView1->WindowProc;
ListView1->WindowProc = NewListViewWndProc;
AttachCombobox();
}
//-------------------------------------------------------------
__fastcall TForm1::~TForm1()
{
ListView1->WindowProc = OldListViewWndProc;
}
//-------------------------------------------------------------
void __fastcall TForm1::NewListViewWndProc( TMessage &Message )
{
OldListViewWndProc( Message );
if( Message.Msg == WM_VSCROLL || Message.Msg == WM_HSCROLL )
{
if( Message.WParamLo != SB_ENDSCROLL )
{
AttachCombobox();
}
}
}
//-------------------------------------------------------------
void __fastcall TForm1::AttachCombobox()
{
TRect R = Rect( 0, 0, 0, 0 );
::ListView_GetSubItemRect( ListView1->Handle, 4, 1, LVIR_BOUNDS, &R );
ComboBox1->Parent = ListView1;
ComboBox1->BoundsRect = R;
ComboBox1->Visible = R.top>= HeaderHeight;
}
//-------------------------------------------------------------

~ JD

 

{smallsort}

Re:ComboBox as ListItem

"Stefan L" < XXXX@XXXXX.COM >wrote:
Quote
It almost worked :)
It compiles up to the point in AttachComboBox() where it says

::ListView_GetSubItemRect( ListView1->Handle, 4, 1, LVIR_BOUNDS, &R );

there it stops and complains about "Call to undefined function". I searched
through the helpfiles
in hope of finding some similar code, but the function doesn't exist
anywhere.
It's a macro defined in commctrl.h. Just:
#include <commctrl.h>
~ JD
 

Re:ComboBox as ListItem

What the heck, tried to compile again, while it still doesn't work I got
some other errors.
Errors
{
E2272: Identifier Expected
E2108: Improper use of typedef 'BOOL'
E2379: Statement missing ;
}
the last one I understand, that one you usually get when the rest of the
code is erroneous. But the rest I don't quite get.
"JD" < XXXX@XXXXX.COM >skrev i meddelandet
Quote

"Stefan L" < XXXX@XXXXX.COM >wrote:
>
>I'm wondering if anyone know how to put a ComboBox or
>something like it in one of the columns. [...]

Converted from Delphi to C++ from a post by Peter Below (TeamB)
Untested:

//-------------------------------------------------------------
private: // User declarations
int HeaderHeight;
TWndMethod OldListViewWndProc;
void __fastcall NewListViewWndProc( TMessage& );
void __fastcall AttachCombobox();
public: // User declarations
__fastcall TForm1::~TForm1();

// in the form's class
//-------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
TRect R;
// You might need to populate the ListView here
HWND hWnd = ::GetWindow( ListView1->Handle, GW_CHILD );
::GetWindowRect( hWnd, &R );
HeaderHeight = R.bottom - R.top;
OldListViewWndProc = ListView1->WindowProc;
ListView1->WindowProc = NewListViewWndProc;
AttachCombobox();
}
//-------------------------------------------------------------
__fastcall TForm1::~TForm1()
{
ListView1->WindowProc = OldListViewWndProc;
}
//-------------------------------------------------------------
void __fastcall TForm1::NewListViewWndProc( TMessage &Message )
{
OldListViewWndProc( Message );
if( Message.Msg == WM_VSCROLL || Message.Msg == WM_HSCROLL )
{
if( Message.WParamLo != SB_ENDSCROLL )
{
AttachCombobox();
}
}
}
//-------------------------------------------------------------
void __fastcall TForm1::AttachCombobox()
{
TRect R = Rect( 0, 0, 0, 0 );
::ListView_GetSubItemRect( ListView1->Handle, 4, 1, LVIR_BOUNDS, &R );
ComboBox1->Parent = ListView1;
ComboBox1->BoundsRect = R;
ComboBox1->Visible = R.top>= HeaderHeight;
}
//-------------------------------------------------------------

~ JD

 

Re:ComboBox as ListItem

Great! Thanks for the help. I'm kind of new to programming, so I really
appreciate what you did for me :)
/Stefan
"JD" < XXXX@XXXXX.COM >skrev i meddelandet
Quote

"Stefan L" < XXXX@XXXXX.COM >wrote:
>What the heck, tried to compile again, while it still doesn't work I got
>some other errors.
>
>Errors
>{
>E2272: Identifier Expected
>E2108: Improper use of typedef 'BOOL'
>E2379: Statement missing ;
>}

Remove the '::' that preceeds the call to the macro. I just presumed that
it was a Win32 API. Had it been, the double colon would be correct but
because it's a macro that returns
Quote
a BOOL ... the compiler doesn't like it.

~ JD

BTW : You should be t{*word*220} you posts. No need to clutter the
server when this thread gets archived.

 

Re:ComboBox as ListItem

One other thing, is it possible to add it to every row?
I tried using some kind of a for-loop, but it only adds it to the final row.
I'm loading the listview as instructed at the place you pointed out in the
other code.
Here's the for-loop which I thought would work.
[start code]
TRect R = Rect( 0, 0, 0, 0 );
for(int row=0; row<ListView1->Items->Count; row++)
{
ListView_GetSubItemRect( ListView1->Handle, row, 3, LVIR_BOUNDS,
&R );
}
ComboBox1->BoundsRect = R;
ComboBox1->Visible = R.top>= HeaderHeight;
ComboBox1->Parent = ListView1;
[end code]
Thanks again
/Stefan
"JD" < XXXX@XXXXX.COM >skrev i meddelandet
Quote

"Stefan L" < XXXX@XXXXX.COM >wrote:
>What the heck, tried to compile again, while it still doesn't work I got
>some other errors.
>
>Errors
>{
>E2272: Identifier Expected
>E2108: Improper use of typedef 'BOOL'
>E2379: Statement missing ;
>}

Remove the '::' that preceeds the call to the macro. I just presumed that
it was a Win32 API. Had it been, the double colon would be correct but
because it's a macro that returns
Quote
a BOOL ... the compiler doesn't like it.

~ JD

BTW : You should be t{*word*220} you posts. No need to clutter the
server when this thread gets archived.

 

Re:ComboBox as ListItem

"Stefan L" < XXXX@XXXXX.COM >wrote:
Quote
One other thing, is it possible to add it to every row? [...]
I wouldn't reccommend it because of the drain on system
resources (TComboBox uses 2 handles) and the fact that now you
need to maintain some kind of list to keep track of all of
them.
What you could do is draw the items yourself so that you can
draw a glyph that represents a ComboBox and when clicked,
bring the real one into play.
Quote
>BTW : You should be t{*word*220} you posts. No need to clutter the
>server when this thread gets archived.
Please trim your posts.
~ JD
 

Re:ComboBox as ListItem

Sorry if it sounds like I'm using you now (please tell me if I'm pushy), but
do you know where I can get the code to do that?
A link is ok if you don't want to write the code for it.
And sorry about the t{*word*220}, will do that from now on :)
/Stefan
"JD" < XXXX@XXXXX.COM >skrev i meddelandet
Quote

"Stefan L" < XXXX@XXXXX.COM >wrote:
>One other thing, is it possible to add it to every row? [...]

I wouldn't reccommend it because of the drain on system
resources [...]
 

Re:ComboBox as ListItem

"Stefan L" < XXXX@XXXXX.COM >wrote:
Quote
Sorry if it sounds like I'm using you now
I don't have to answer unless I want to so don't feel like
you're impossing.
Quote
but do you know where I can get the code to do that?
Tamaracha and google advanced search are great places for
sample code. I just picked off that other sample I have you
from Tamaracha this AM.
groups.google.com/advanced_group_search
In the NewsGroup field, add the group that you want to search,
For example:
borland.public.cppbuilder.vcl.components.using
The rest of the fields are self explainitory.
www.tamaracka.com/search.htm
Set the 'Record Limit' to the max allowed and the "Results by'
to thread.
Also, if you google (without the avdanced search) and find
samples that are Delphi, it's easy enough to convert so even
if you can't, someone here can.
As for you're specific problem: Set the OwnerDraw property to
true and add a OnDrawItem event where you would use the Win32
API DrawFrameControl to draw the button that looks like a
ComboBox button (You could also draw a frame around it all and
make it look like they're all ComboBoxes).
Tamaracka has lots of samples for 'TListView OnDrawItem' but
none that included DrawFrameControl. Just get one of those
sample working first before you move onto the DrawFrameControl.
Then just add in there:
::DrawFrameControl( ListView1->Canvas->Handle, &Rect, DFC_SCROLL, DFCS_SCROLLCOMBOBOX );
You'll need to adjust Rect or use a seperate one for both the
text and the drop down arrow. Then the rest of the behavior is
up to you. What I would do is just use the code from before and
overlay the real ComboBox. That way you don't have to worry
about getting the OnMouseDown/Up/Move right and a whole bunch
of other stuff.
~ JD
 

Re:ComboBox as ListItem

Hi again, I got it working somehow. Don't ask :)
But I got another problem now, when I try to change the value I get an error
"<project>Raised exception class EStringListError with message 'List index
out of bounds (n)'."
'n' is changing between 1 through 3. The first 3 rows works great with the
combobox, the text is getting updated and all, but the rest just gives me
that error.
 

Re:ComboBox as ListItem

"Stefan L" < XXXX@XXXXX.COM >wrote:
Quote

[...] Raised exception class EStringListError with message
'List index out of bounds (n)'." [...]
The index is zero based. IOW, if you have 3 entries, they are
indexed zero thru 2. Also be aware that the ItemIndex for a
TComboBox is -1 when no items from it's list match the Text.
If that doesn't solve your problem, post some code.
~ JD
 

Re:ComboBox as ListItem

"JD" < XXXX@XXXXX.COM >skrev i meddelandet
Quote

[...]post some code.

~ JD

Here goes, I'll try to explain my code. I got a ComboBox with 4 items at the
moment, the style is csOwnerDrawFixed but that shouldn't matter right? I got
it so because it looked better :)
On the 3 first rows in the ListView, the code works great, it changes the
text in SubItem nr 2(3) to the one I have selected in the combobox. But when
I try to select any of the items while not on the 3 first rows, I get the
OOB-error.
//---------------------------------------------------------------------------
void __fastcall TForm1::ComboBox1Select(TObject *Sender)
{
int sel=ListView1->ItemFocused->Index; //Something wrong with
this code perhaps? I got a strange feeling that it has something to do with
the error.
ListView1->Items->Item[sel]->SubItems->Strings[2] = ComboBox1->Text;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::ListView1DblClick(TObject *Sender)
{
AttachCombobox();
}
//---------------------------------------------------------------------------
Anything else you need? Just ask, ok :)
 

Re:ComboBox as ListItem

"Stefan L" < XXXX@XXXXX.COM >wrote:
Quote

[...] the style is csOwnerDrawFixed but that shouldn't
matter right?
Not if you're doing it correctly but that error could be
produced by the ComboBox.
Quote
int sel=ListView1->ItemFocused->Index;
I haven't worked with ListViews before but I'm quite sure that
this is bad code. ItemFocused returns a pointer to a TListItem
(or NULL).
It should be something like:
void __fastcall TForm1::ComboBox1Select(TObject *Sender)
{
TListItem *pItem = ListView1->ItemFocused;
if( pItem )
{
ListView1->Items->Item[ pItem->Index ]->SubItems->Strings[2] = ComboBox1->Text;
}
}
Quote
On the 3 first rows in the ListView, the code works great,
it changes the text in SubItem nr 2(3) to the one I have
selected in the combobox. But when I try to select any of
the items while not on the 3 first rows, I get the
OOB-error.
Strings will throw that error as well as the ComboBox so it
difficult to say where it happening. What you need to do is
isolate where the error is happening.
I would put break points right before and after the ComboBox
drawing code and right before and after assigning the ComboBox
text in the above.
If you don't know how, ShowMessage works just as well and you
can use litteral text to tell you exactly where you are in the
code.
~ JD
 

Re:ComboBox as ListItem

"JD" < XXXX@XXXXX.COM >skrev i meddelandet
Quote

I would put break points right before and after the ComboBox
drawing code and right before and after assigning the ComboBox
text in the above.

If you don't know how, ShowMessage works just as well and you
can use litteral text to tell you exactly where you are in the
code.

~ JD

I tried your code snippet, but it unforunatly didn't work. But I think the
problem is in that very row of code.
I changed the String[value] to something non-existant just to test, and sure
enough the OOB changed to that value.
Could it be my code that is wrong? I looked through some other code with
subitems and assumed that the String[value] was which subitem it should
change the text of.
Perhaps that's a mistake?
I tried using ShowMessages(I don't know how to use BPs) before and after the
Text changing code, it crashes after the first message, i.e when it gets to
the ListView1->[...] Change the text.
/Stefan