CCtrlView 雑記

Last Updated: Feb.14,2007
使用しているコンパイラは、Microsoft Visual C++ 6.0 です。これ以外のバージョンでは記述と食い違うことがあるかも知れません。

1. CListBoxView インプリメンテーションファイル(.cpp)

CCtrlView から派生させて CListBoxView を作ってみました。ポイントになるのは、コンストラクタと OnChildNotify あたりでしょうか。編集機能とシリアライズ機能も付けてみましたが、MFC の CListView の方が便利なのでほとんど用途はないかもしれません。ソースを丸ごと載せておきます。ヘッダーファイルはこちら

// ListBoxView.cpp : インプリメンテーション ファイル
//

// Copyright (c) 2007 AOK. All Rights Reserved.
//

#include "stdafx.h"
#include "ListBoxView.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CLBEdit - 編集用のエディットコントロール

CLBEdit::CLBEdit()
: m_nCurIndex(-1)
{
}

CLBEdit::~CLBEdit()
{
}

BEGIN_MESSAGE_MAP(CLBEdit, CEdit)
    //{{AFX_MSG_MAP(CLBEdit)
    ON_CONTROL_REFLECT(EN_CHANGE, OnChange)
    ON_CONTROL_REFLECT(EN_KILLFOCUS, OnKillFocus)
    ON_WM_KEYDOWN()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

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

CListBoxView* CLBEdit::GetView() const
{
    CWnd* pWnd = GetParent();
    ASSERT_VALID(pWnd);
    ASSERT_KINDOF(CListBoxView, pWnd);
    return (CListBoxView*)pWnd;
}

void CLBEdit::BeginEdit(int nItem, LPCTSTR pszText)
{
    ASSERT(nItem != LB_ERR);
    m_nCurIndex = nItem;
    m_sPreText = pszText;
    SetFont(GetView()->GetFont());
    SetWindowText(pszText);
}

void CLBEdit::EndEdit()
{
    if (m_nCurIndex == -1) return;
    CString sText;
    GetWindowText(sText);
    CListBoxView* pView = GetView();
    if (sText.IsEmpty())
    {
        pView->GetListBox().DeleteString(m_nCurIndex);
        pView->AdjustListBox();
    }
    else if (m_sPreText.Compare(sText) != 0)
    {
        pView->GetListBox().InsertString(m_nCurIndex, sText);
        pView->GetListBox().DeleteString(m_nCurIndex+1);
        pView->AdjustListBox();
    }
    m_nCurIndex = -1;
}

void CLBEdit::OnChange() 
{
    CString sText;
    GetWindowText(sText);
    CListBoxView* pView = GetView();
    if (m_sPreText.Compare(sText) != 0)
        pView->GetDocument()->SetModifiedFlag(TRUE);
}

void CLBEdit::OnKillFocus()
{
    EndEdit();
    ShowWindow(SW_HIDE);
}

void CLBEdit::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
    if (nChar == VK_RETURN)
    {
        int nSave = m_nCurIndex;
        ShowWindow(SW_HIDE);
        if (nSave != -1)
            GetView()->GetListBox().SetCurSel(nSave);
    }
    CEdit::OnKeyDown(nChar, nRepCnt, nFlags);
}

/////////////////////////////////////////////////////////////////////////////
// CListBoxView

IMPLEMENT_DYNCREATE(CListBoxView, CCtrlView)

CListBoxView::CListBoxView()
: CCtrlView(_T("LISTBOX"), AFX_WS_DEFAULT_VIEW
            | WS_VSCROLL | WS_HSCROLL | WS_CLIPCHILDREN
            | LBS_NOINTEGRALHEIGHT | LBS_NOTIFY)
{
}

CListBoxView::~CListBoxView()
{
}

BEGIN_MESSAGE_MAP(CListBoxView, CCtrlView)
    //{{AFX_MSG_MAP(CListBoxView)
    ON_WM_CREATE()
    ON_WM_SIZE()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

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

int CListBoxView::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
    if (CCtrlView::OnCreate(lpCreateStruct) == -1)
        return -1;
    
    //SendMessage(WM_SETFONT, (WPARAM)::GetStockObject(DEFAULT_GUI_FONT), 0);
    
    m_edit.Create(WS_CHILD|WS_BORDER|ES_AUTOHSCROLL, 
                  CRect(0,0,0,0), this, GetDlgCtrlID()+1);
    
    return 0;
}

CListBox& CListBoxView::GetListBox() const
{
    return *(CListBox*)this;
}

void CListBoxView::AdjustListBox()
{
    ASSERT((GetStyle() & LBS_MULTICOLUMN) == 0);
    CListBox& lb = GetListBox();
    CString str;
    CSize size;
    int dx = 0;
    CDC* pDC = lb.GetDC();
    CFont *pOldFont = pDC->SelectObject(GetFont());
    int c = lb.GetCount();
    for (int i = 0; i < c; i++)
    {
        lb.GetText(i, str);
        size = pDC->GetTextExtent(str + _T(' '));
        if (size.cx > dx) dx = size.cx;
    }
    lb.SetHorizontalExtent(dx);
    pDC->SelectObject(pOldFont);
    lb.ReleaseDC(pDC);
}

void CListBoxView::SerializeRaw(CArchive& ar) 
{
    CListBox& lb = GetListBox();
    CString sText;
    if (ar.IsStoring())
    {
        int nCount = lb.GetCount();
        for (int i = 0; i < nCount; i++)
        {
            lb.GetText(i, sText);
            if (!sText.IsEmpty())
                ar.WriteString(sText + _T("\r\n"));
        }
    }
    else
    {
        lb.ResetContent();
        while (ar.ReadString(sText))
            lb.AddString(sText);
        AdjustListBox();
    }
}

void CListBoxView::OnSize(UINT nType, int cx, int cy) 
{
    CCtrlView::OnSize(nType, cx, cy);
    if (m_edit.GetSafeHwnd())
    {
        CRect rect;
        m_edit.GetWindowRect(&rect);
        m_edit.SetWindowPos(NULL, 0, 0, cx, rect.Height(),
                            SWP_NOMOVE|SWP_NOZORDER);
    }
}

BOOL CListBoxView::OnChildNotify(UINT message, WPARAM wParam,
                                 LPARAM lParam, LRESULT* pLResult) 
{
    if (message == WM_COMMAND)
    {
        switch (HIWORD(wParam))
        {
        case LBN_ERRSPACE:
            break;
        case LBN_SELCHANGE:
            OnSelChange(GetListBox().GetCurSel());
            return TRUE;
        case LBN_DBLCLK:
            OnDblClk(GetListBox().GetCurSel());
            return TRUE;
        case LBN_SELCANCEL:
            break;
        case LBN_SETFOCUS:
            break;
        case LBN_KILLFOCUS:
            break;
        default:
            break;
        }
    }
    return CCtrlView::OnChildNotify(message, wParam, lParam, pLResult);
}

void CListBoxView::OnSelChange(int nItem)
{
    if (nItem == LB_ERR) return;
    CString s;
    GetListBox().GetText(nItem, s);
    CWnd* pWnd = AfxGetMainWnd();
    ASSERT_VALID(pWnd);
    ASSERT_KINDOF(CFrameWnd, pWnd);
    ((CFrameWnd*)pWnd)->SetMessageText(s);
}

void CListBoxView::OnDblClk(int nItem)
{
    if (nItem == LB_ERR) return;
    CRect rect;
    GetListBox().GetItemRect(nItem, &rect);
    int cy = ::GetSystemMetrics(SM_CYBORDER);
    /* 全く同じサイズにすると編集状態かどうか分かりにくいため */
    rect.InflateRect(0, cy+1, 0, cy+1);
    CString sText;
    GetListBox().GetText(nItem, sText);
    m_edit.BeginEdit(nItem, sText);
    m_edit.MoveWindow(&rect, FALSE);
    m_edit.ShowWindow(SW_SHOWNORMAL);
    m_edit.SetFocus();
}

新規行挿入と行削除は実装していませんが、

  1. 新規行挿入は空行を挿入(GetListBox().InsertString())したあとで、CListBoxView::OnDblClk() を呼び出す。
  2. 行削除は単に行を削除(GetListBox().DeleteString())して、GetDocument()->SetModifiedFlag(TRUE) する。

で、OK です。


Back Copyright (c) 2007 AOK. All Rights Reserved.