『ながっちゃん』のページ

気まぐれ兄ちゃんの独り言

#6[CPP]リストコントロールの項目をソートする

内容

リストコントロールの項目をソートする方法です。

解説

 リストコントロールの項目をソートするには、項目を比較して表示順を決定する比較関数を定義し、リストコントロールのメンバー関数SortItems( )を呼ぶことで行います。
 サンプルはリストコントロールのヘッダがクリックされた時にその列の文字列に応じて昇順でソートする例です。
 まず、クリックされたリストコントロールのヘッダーの列の番号を格納するメンバー変数m_iSubItemを定義し、更にリストコントロールに関連付けられたクラスもメンバー変数として定義します。次に、ダイアログのメッセージハンドラOnInitDialog( )でリストコントロールのヘッダーを設定し、リストコントロールに項目を追加します。
 リストコントロールのヘッダークリックに対応したメッセージハンドラOnLvnColumnclickList1( )においてSortItems( )を呼んでソートを実行します。SortItems( )の引数は比較関数と比較関数に渡す任意のパラメータです。サンプルでは比較関数はMyCompareProc( )です。SortItems( )の2番目の引数の値が比較関数の3番目の引数に渡されます。サンプルではダイアログに関連付けられたクラスへのポインタをSortItems( )の2番目の引数に渡しています。これによってダイアログのメンバー変数にアクセスできクリックされた列の番号を取得することができます。
 比較関数の1番目と2番目の引数は、表示順を比較する項目に関連付けられた32 ビット値、すなわちリストコントロールのメンバー変数SetItemData( )で設定される値です。サンプルの場合は、項目に関連付けられた32 ビット値に項目のインデックスを格納しています。これにより比較関数において、表示順を比較しようといている項目のインデックスを取得することができます。項目のインデックスと列の番号から比較しようといている文字列を取得できます。比較関数の戻り値は、1番目の引数に対応する項目が2番目の引数に対応する項目より前の場合は負の値で、2番目の引数に対応する項目の方が前の場合は正の値となるようにします。サンプルの場合は文字列の昇順に並ぶようにstrcmp( )を使って比較関数の戻り値をセットしています。降順に並ぶようにするにはstrcmp( )の1番目と2番目の引数を入れ替えるだけです。

サンプル

< ダイアログボックスの例>
リストコントロールを配置したダイアログサンプル

// CDlgListCtrl ダイアログ

class CDlgListCtrl : public CDialog
{
	DECLARE_DYNAMIC(CDlgListCtrl)

public:
	CDlgListCtrl(CWnd* pParent = NULL);   // 標準コンストラクタ
	virtual ~CDlgListCtrl();

// ダイアログ データ
	enum { IDD = IDD_DIALOGLISTCTRL };

protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV サポート

	DECLARE_MESSAGE_MAP()
public:
	virtual BOOL OnInitDialog();
	afx_msg void OnLvnColumnclickList1(NMHDR *pNMHDR, LRESULT *pResult);
	// ソート方法を定義。
	static int CALLBACK MyCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
private:
	CListCtrl m_List;
	int m_iSubItem;
};


// CDlgListCtrl ダイアログ

IMPLEMENT_DYNAMIC(CDlgListCtrl, CDialog)
CDlgListCtrl::CDlgListCtrl(CWnd* pParent /*=NULL*/)
	: CDialog(CDlgListCtrl::IDD, pParent)
	, m_iSubItem(-1)
{
}

CDlgListCtrl::~CDlgListCtrl()
{
}

void CDlgListCtrl::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_LIST1, m_List);
}


BEGIN_MESSAGE_MAP(CDlgListCtrl, CDialog)
	ON_NOTIFY(LVN_COLUMNCLICK, IDC_LIST1, OnLvnColumnclickList1)
END_MESSAGE_MAP()


// CDlgListCtrl メッセージ ハンドラ

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

	// TODO :  ここに初期化を追加してください
	m_List.SetExtendedStyle( m_List.GetExtendedStyle() | LVS_EX_FULLROWSELECT );

	// リストコントロールの列を設定
	int i;
	LV_COLUMN lvc;

	lvc.mask = LVCF_TEXT | LVCF_SUBITEM | LVCF_WIDTH | LVCF_FMT;

	i = 0;
	lvc.iSubItem = i;
	lvc.pszText = "品名";
	lvc.cx = 150;
	lvc.fmt = LVCFMT_LEFT;
	m_List.InsertColumn( i, &lvc );
	i++;
	lvc.iSubItem = i;
	lvc.pszText = "品番";
	lvc.cx = 100;
	lvc.fmt = LVCFMT_LEFT;
	m_List.InsertColumn( i, &lvc );
	i++;
	lvc.iSubItem = i;
	lvc.pszText = "価格";
	lvc.cx = 80;
	lvc.fmt = LVCFMT_RIGHT;
	m_List.InsertColumn( i, &lvc );

	// サンプルのデータを追加
	i = 0;
	m_List.InsertItem( i, "お掃除エアコン" );
	m_List.SetItemText( i, 1, "CS-22XYZ" );
	m_List.SetItemText( i++, 2, "\\220,000" );
	m_List.InsertItem( i, "普通のエアコン" );
	m_List.SetItemText( i, 1, "CS-22BAC" );
	m_List.SetItemText( i++, 2, "\\180,000" );
	m_List.InsertItem( i, "省エネエアコン" );
	m_List.SetItemText( i, 1, "CS-22ECO" );
	m_List.SetItemText( i++, 2, "\\200,000" );
	m_List.InsertItem( i, "冷蔵庫1" );
	m_List.SetItemText( i, 1, "NR-F600T" );
	m_List.SetItemText( i++, 2, "\\156,000" );
	m_List.InsertItem( i, "冷蔵庫2" );
	m_List.SetItemText( i, 1, "NR-P6650T" );
	m_List.SetItemText( i++, 2, "\\123,000" );
	m_List.InsertItem( i, "冷蔵庫3" );
	m_List.SetItemText( i, 1, "NR-E600T" );
	m_List.SetItemText( i++, 2, "\\145,000" );
	for ( i = 0; i < m_List.GetItemCount(); i++ )
			m_List.SetItemData( i, (DWORD)i );

	return TRUE;  // return TRUE unless you set the focus to a control
	// 例外 : OCX プロパティ ページは必ず FALSE を返します。
}

void CDlgListCtrl::OnLvnColumnclickList1(NMHDR *pNMHDR, LRESULT *pResult)
{
	LPNMLISTVIEW pNMLV = reinterpret_cast<LPNMLISTVIEW>(pNMHDR);
	// TODO : ここにコントロール通知ハンドラ コードを追加します。
	m_iSubItem = pNMLV->iSubItem;
	m_List.SortItems( MyCompareProc, (LPARAM)this );

	for ( int i = 0; i < m_List.GetItemCount(); i++ )
			m_List.SetItemData( i, (DWORD)i );

	*pResult = 0;
}

// ソート方法を定義。
int CALLBACK CDlgListCtrl::MyCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
	CDlgListCtrl* pMyDlg = (CDlgListCtrl*)lParamSort;
	int nSubItem  = pMyDlg->m_iSubItem;
	CString    strItem1 = pMyDlg->m_List.GetItemText(lParam1, nSubItem);
	CString    strItem2 = pMyDlg->m_List.GetItemText(lParam2, nSubItem);

	return strcmp(strItem1, strItem2);
}