wxListCtrl and XRC Topic is solved

If you are using the main C++ distribution of wxWidgets, Feel free to ask any question related to wxWidgets development here. This means questions regarding to C++ and wxWidgets, not compile problems.
Post Reply
damir
Earned a small fee
Earned a small fee
Posts: 19
Joined: Thu Jan 12, 2006 11:59 am

wxListCtrl and XRC

Post by damir » Thu Jan 12, 2006 10:41 pm

Hi!
I made my own listctrl with events from wxListCtrl control:

Code: Select all

class FBListCtrl : public wxListCtrl
{
public:

	//! Constructor
	FBListCtrl(wxWindow *parent, wxWindowID id = wxID_ANY);

	//! Destructor
	~FBListCtrl();

       .....

	//! events	
	void OnColClick(wxListEvent& event);
	void OnInsertItem(wxListEvent& event);
};

FBListCtrl::FBListCtrl(wxWindow *parent, wxWindowID id) : wxListCtrl(parent,id,wxDefaultPosition,wxDefaultSize,wxLC_REPORT)
{
	this->Connect(GetId(), wxEVT_COMMAND_LIST_COL_CLICK, (wxObjectEventFunction)(wxEventFunction)(wxCommandEventFunction)&FBListCtrl::OnColClick);
	mSelectedCol = 0;
	mImageList = new wxImageList(16, 16, true);
	mImageList->Add(wxIcon(sortup_xpm));
	mImageList->Add(wxIcon(sortdown_xpm));
	SetImageList(mImageList, wxIMAGE_LIST_SMALL);
}
Like you notice I don't use DECLARE_EVENT_TABLE() macro and other. Instead I made connection in constructor. And it works fine when I use control like this:

Code: Select all

....
mListCtrl = new FBListCtrl(this, ID_SOMEID);
GetSizer()->Add(mListCtrl, 1, wxEXPAND);
....
But when I build my interface with XRC the event is not working.

Code: Select all

....
mListCtrl = new FBListCtrl(this, XRCID("ID_SYNCLOGDLG_BUTTON_CANCEL"));
wxXmlResource::Get()->AttachUnknownControl(_T("ID_SYNCLOGDLG_LISTCTRL"), mListCtrl, this);
....
Where is the problem? Am I doing something wrong or it's XRC bug?

Thanks[/code]

benedicte
wxWorld Domination!
wxWorld Domination!
Posts: 1409
Joined: Wed Jan 19, 2005 3:44 pm
Location: Paris, France

Post by benedicte » Fri Jan 13, 2006 1:24 pm

You should put here the XRC code where you declare your unknown control, and also the whole FBListCtrl class declaration/definition.

I have many unknown controls in my XRC and everything works fine.

damir
Earned a small fee
Earned a small fee
Posts: 19
Joined: Thu Jan 12, 2006 11:59 am

Post by damir » Fri Jan 13, 2006 3:09 pm

fblistctrl.h:

Code: Select all

#ifndef _FBLISTCTRL_H_
#define _FBLISTCTRL_H_

#include <vector>

#include <wx/wx.h>
#include <wx/listctrl.h>

using namespace std;

struct FBExtraData;

enum FBCONST {
	FB_ORDER_ASC,
	FB_ORDER_DESC,
	FB_TYPE_TEXT,
	FB_TYPE_NUMBER
};

class FBListItem : public wxListItem
{
public:
	FBListItem();
	~FBListItem();

	void SetExtraData(long data) {
		mExtraData = data;
	}

	long GetExtraData() {
		return mExtraData;
	}

private:
	long mExtraData;

};

class FBListCtrl : public wxListCtrl
{
public:

	//! Constructor
	FBListCtrl(wxWindow *parent, wxWindowID id = wxID_ANY);

	//! Destructor
	~FBListCtrl();
	
	//! Items
	void SetText(int row, int col, wxString text);
	wxString GetText(int row, int col);
	void Sort(int col, int order, int type);
	//! compatibile with wxListCtrl
	bool SetItemData(long index, long data);
	long GetItemData(long index);

	//! compatibile with wxListCtrl
	bool SetData(long index, struct FBItemData *data);
	struct FBItemData *GetData(long index);

	long AppendItem(wxString text, int image = -1);
	
    //! [de]select an item
	void Select(long n, bool on = true);

    //! focus and show the given item
	void Focus(long index);

    //! get the currently focused item or -1 if none
    long GetFocusedItem() const;

    //! get first and subsequent selected items, return -1 when no more
	long GetNextSelected(long item) const;
    
	long GetFirstSelected() const;

    //! return true if the item is selected
	bool IsSelected(long index);

    //! columns
    // -------

	long InsertColumn(wxString title, long type = FB_TYPE_TEXT);

	void SetColumnImage(int col, int image);

	void ClearColumnImage(int col);
	
	int GetColumnImage(int col);
	
protected:
	// MenuWindow variables

	// TODO: add member variables...

private:

	wxImageList *mImageList;
	long mSelectedCol;
	
	vector<long> mColType;
	vector<long> mExtradata;

	//! events	
	void OnColClick(wxListEvent& event);
	void OnInsertItem(wxListEvent& event);

	//DECLARE_EVENT_TABLE();
};

#endif	//_BARCODECTRL_H_
fblistctrl.cpp

Code: Select all

#include "fblistctrl.h"
#include "up.xpm"
#include "down.xpm"


FBListItem::FBListItem() : wxListItem()
{
	mExtraData = 0;
}

FBListItem::~FBListItem()
{

}

struct FBSortInfo
{
	int order;
	int type;
};

typedef struct FBSortInfo FBSortInfo;

struct FBItemData
{
	wxString sortString;
	long data;
};

typedef struct FBItemData FBItemData;

int wxCALLBACK FBCompareFunction(long item1, long item2, long sortData)
{
	FBItemData *data1 = (FBItemData*)item1;
	FBItemData *data2 = (FBItemData*)item2;
	
	double num1, num2;

//	std::cout << *str1 << "\t" << *str2 << std::endl;
	
	struct FBSortInfo *info = (struct FBSortInfo*)(sortData);
	
	if (info->type == FB_TYPE_TEXT)
	{
		if (data1->sortString < data2->sortString)
			if (info->order == FB_ORDER_ASC)
				return -1;
			else
				return 1;

		if (data1->sortString > data2->sortString)
			if (info->order == FB_ORDER_ASC)
				return 1;
			else
				return -1;

		return 0;
	}
	else
	{
		data1->sortString.ToDouble(&num1);
		data2->sortString.ToDouble(&num2);
		
		if (num1 < num2)
			if (info->order == FB_ORDER_ASC)
				return -1;
			else
				return 1;

		if (num1 > num2)
			if (info->order == FB_ORDER_ASC)
				return 1;
			else
				return -1;

		return 0;

	}
}

FBListCtrl::FBListCtrl(wxWindow *parent, wxWindowID id) : wxListCtrl(parent,id,wxDefaultPosition,wxDefaultSize,wxLC_REPORT)
{
	this->Connect(GetId(), wxEVT_COMMAND_LIST_COL_CLICK, (wxObjectEventFunction)(wxEventFunction)(wxCommandEventFunction)&FBListCtrl::OnColClick);
	mSelectedCol = 0;
	mImageList = new wxImageList(16, 16, true);
	mImageList->Add(wxIcon(sortup_xpm));
	mImageList->Add(wxIcon(sortdown_xpm));
	SetImageList(mImageList, wxIMAGE_LIST_SMALL);
}


FBListCtrl::~FBListCtrl()
{
	delete mImageList;
}

void FBListCtrl::OnColClick(wxListEvent& event)
{
	int order;
	if (event.m_col != -1)
	{
		for (int i = 0; i < GetColumnCount(); i++)
		{
			if (event.m_col != mSelectedCol)
				ClearColumnImage(i);
		}

		if (GetColumnImage(event.m_col) == 0)
		{
			SetColumnImage(event.m_col, 1);
			order = FB_ORDER_DESC;
		}
		else
		{
			SetColumnImage(event.m_col, 0);
			order = FB_ORDER_ASC;			
		}
	
		mSelectedCol = event.m_col;
		
		Sort(mSelectedCol, order, mColType[event.m_col]);
	}
}

void FBListCtrl::Sort(int col, int order, int type)
{
	FBSortInfo sortInfo;
//	wxString *str;

	for (int i = 0; i < GetItemCount(); i++)
	{
/*		str = new wxString(_T("damir"));
		SetItemData(i, (long)str);

		str = (wxString*)(GetItemData(i));
		*str = GetText(i, col);*/
		GetData(i)->sortString = GetText(i, col);
	}
	
	sortInfo.order = order;
	sortInfo.type = type;

	SortItems(FBCompareFunction, (long int)(&sortInfo));

/*	for (int i = 0; i < GetItemCount(); i++)
	{
		delete (wxString*)(GetItemData(i));
		SetItemData(i, 0);
	}*/
}

void FBListCtrl::SetText(int row, int col, wxString text)
{
	SetItem(row, col, text);
}

wxString FBListCtrl::GetText(int row, int col)
{
	wxListItem item;
	item.SetId(row);
	item.SetColumn(col);
	item.m_mask = wxLIST_MASK_TEXT;
	GetItem(item);
	return item.GetText();
}

long FBListCtrl::InsertColumn(wxString title, long type)
{
	long col;
	if ( (col = wxListCtrl::InsertColumn(GetColumnCount(), title) ) != -1 )
	{
		if (col == 0)
			SetColumnImage(col, 0);
		mColType.push_back(type);
	}
	return col;
}

long FBListCtrl::AppendItem(wxString text, int image)
{
	long item = InsertItem(GetItemCount(), text, image);
	if (item != -1)
	{
		FBItemData *data = new FBItemData();
		data->data = 0;
		data->sortString=_T("damir");
		SetData(item, data);
	}
	return item;
	//mExtraData.push
}
	
    // [de]select an item
void FBListCtrl::Select(long n, bool on)
{
	SetItemState(n, on ? wxLIST_STATE_SELECTED : 0, wxLIST_STATE_SELECTED);
}

    // focus and show the given item
void FBListCtrl::Focus(long index)
{
	SetItemState(index, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED);
	EnsureVisible(index);
}

    // get the currently focused item or -1 if none
long FBListCtrl::GetFocusedItem() const
{
	return GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_FOCUSED);
}

    // get first and subsequent selected items, return -1 when no more
long FBListCtrl::GetNextSelected(long item) const
{
	return GetNextItem(item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
}
    
long FBListCtrl::GetFirstSelected() const
{
	return GetNextSelected(-1);
}

    // return true if the item is selected
bool FBListCtrl::IsSelected(long index)
{
	return GetItemState(index, wxLIST_STATE_SELECTED) != 0;
}

    // columns
    // -------

void FBListCtrl::SetColumnImage(int col, int image)
{
	wxListItem item;
	item.SetMask(wxLIST_MASK_IMAGE);
	item.SetImage(image);
	SetColumn(col, item);
}

void FBListCtrl::ClearColumnImage(int col)
{
	SetColumnImage(col, -1);
}
	
int FBListCtrl::GetColumnImage(int col)
{
	wxListItem item;
	item.SetMask(wxLIST_MASK_IMAGE);
	GetColumn(col, item);
	return item.GetImage();
}

bool FBListCtrl::SetItemData(long index, long data)
{
	//wxListCtrl();
	GetData(index)->data = data;
	return true;
}

long FBListCtrl::GetItemData(long index)
{
	return GetData(index)->data;
}

bool FBListCtrl::SetData(long index, FBItemData *data)
{
	return wxListCtrl::SetItemData(index, (long)data);
}

FBItemData *FBListCtrl::GetData(long index)
{
	return (FBItemData*)wxListCtrl::GetItemData(index);
}
and xrc for window containing my control:

Code: Select all

<object class="wxDialog" name="ID_SYNCLOGDLG" subclass="SyncLogDlg">
        <style>wxDEFAULT_DIALOG_STYLE|wxCAPTION|wxRESIZE_BORDER|wxSYSTEM_MENU|wxCLOSE_BOX|wxMAXIMIZE_BOX|wxMINIMIZE_BOX</style>
        <exstyle>wxWS_EX_BLOCK_EVENTS</exstyle>
        <size>400,300</size>
        <object class="wxBoxSizer">
            <orient>wxVERTICAL</orient>
            <object class="sizeritem">
                <flag>wxALIGN_LEFT|wxALL</flag>
                <border>5</border>
                <object class="wxButton" name="ID_SYNCLOGDLG_BUTTON_REMOVE">
                    <label>Delete</label>
                </object>
            </object>
            <object class="sizeritem">
                <flag>wxGROW|wxALL</flag>
                <border>5</border>
                <option>1</option>
                <object class="unknown" name="ID_SYNCLOGDLG_LISTCTRL">
                    <size>100,100</size>
                </object>
            </object>
            <object class="sizeritem">
                <flag>wxGROW|wxALL</flag>
                <border>5</border>
                <object class="wxStaticLine" name="wxID_USERSDLG_STATIC">
                    <style>wxLI_HORIZONTAL</style>
                </object>
            </object>
            <object class="sizeritem">
                <flag>wxALIGN_RIGHT|wxALL</flag>
                <border>5</border>
                <object class="wxBoxSizer">
                    <orient>wxHORIZONTAL</orient>
                    <object class="sizeritem">
                        <flag>wxALIGN_CENTER_VERTICAL|wxALL</flag>
                        <border>5</border>
                        <object class="wxButton" name="ID_SYNCLOGDLG_BUTTON_OK">
                            <label>Ok</label>
                            <default>1</default>
                        </object>
                    </object>
                    <object class="sizeritem">
                        <flag>wxALIGN_CENTER_VERTICAL|wxALL</flag>
                        <border>5</border>
                        <object class="wxButton" name="ID_SYNCLOGDLG_BUTTON_CANCEL">
                            <label>Cancel</label>
                        </object>
                    </object>
                </object>
            </object>
        </object>
    </object>

damir
Earned a small fee
Earned a small fee
Posts: 19
Joined: Thu Jan 12, 2006 11:59 am

Post by damir » Sat Jan 14, 2006 4:22 pm

Nobody?

benedicte
wxWorld Domination!
wxWorld Domination!
Posts: 1409
Joined: Wed Jan 19, 2005 3:44 pm
Location: Paris, France

Post by benedicte » Sat Jan 14, 2006 5:27 pm

sorry for the delay, I've been out for some time.

1/ for the XRC and unknown control, maybe you should have a look at this post, it may help you.

2/ AFAIK, event handlers are public functions...
I would put OnColClick and OnInsertItem in the public section.

3/ I would also declare and define the event table, even if empty.
in .h file

Code: Select all

DECLARE_EVENT_TABLE
in .cpp file

Code: Select all

BEGIN_EVENT_TABLE(FBListCtrl, wxListCtrl) 
END_EVENT_TABLE()

DavidHart
Site Admin
Site Admin
Posts: 4021
Joined: Thu Jan 12, 2006 6:23 pm
Location: IoW, UK

Post by DavidHart » Sat Jan 14, 2006 8:51 pm

Hi Damir,

I've never had to use "unknown" controls, but I suspect what I'm about to say is true for them too: when you load a control from xrc, your constructor isn't called. You need to move the code from your constructor into a separate init() method, and call it once the control is loaded.

Having said that, why pretend your control is unknown anyway --- it's just a subclassed wxListCtrl, not an entirely new animal. So instead why not say:

Code: Select all

 <object class="wxListCtrl" name="ID_SYNCLOGDLG_LISTCTRL" subclass="FBListCtrl">
                    <size>100,100</size>
</object>
You can then access it by
FBListCtrl* FBL = (FBListCtrl*)FindWindow(wxT("ID_SYNCLOGDLG_LISTCTRL"));

Two things you seem to need to do to make subclassing work.
  • You must have a constructor without parameters, even though it won't be called. So in the declaration: FBListCtrl(){}

    For some reason, the class must be dynamic. So in the declaration: DECLARE_DYNAMIC_CLASS(FBListCtrl)
    and in the .cpp file: IMPLEMENT_DYNAMIC_CLASS(FBListCtrl,wxListCtrl)
HTH,

David

damir
Earned a small fee
Earned a small fee
Posts: 19
Joined: Thu Jan 12, 2006 11:59 am

Post by damir » Sun Jan 15, 2006 7:46 pm

Hi everybody!
First of all I would like to thank you all for your answers. I solved the problem. It's so obvious that I'm a little bit shamed :oops:
The problem was in the code below:

Code: Select all

....
mListCtrl = new FBListCtrl(this, XRCID("ID_SYNCLOGDLG_BUTTON_CANCEL"));
wxXmlResource::Get()->AttachUnknownControl(_T("ID_SYNCLOGDLG_LISTCTRL"), mListCtrl, this);
....
 
Nobody noticed (including me) that when I created my control I didn't put right ID for it. The XRCs ID for control is ID_SYNCLOGDLG_LISTCTRL and in time of creation it was ID_SYNCLOGDLG_BUTTON_CANCEL.

Once more, thank you for answers!

All the best,
Damir

Post Reply