📄 doublelistpicker.cpp
字号:
////////////////////////////////////////////////////////////////////////////
// File: DoubleListPicker.cpp
// Version: 2
// Created: 07-Feb-2003
//
// Author: Paul S. Vickery
// E-mail: paul@vickeryhome.freeserve.co.uk
//
// CStatic derived control to encapsulate two lists, and buttons, to allow
// users to pick from one list and add to the other
//
// You are free to use or modify this code, with no restrictions, other than
// you continue to acknowledge me as the original author in this source code,
// or any code derived from it.
//
// If you use this code, or use it as a base for your own code, it would be
// nice to hear from you simply so I know it's not been a waste of time!
//
// Copyright (c) 2002-2003 Paul S. Vickery
//
////////////////////////////////////////////////////////////////////////////
// Version History:
//
// Version 2 - 07-Feb-2003
// =======================
// Added facility to show the lists horizontally. Uses code based on code by
// Anneke Sicherer-Roetman (drawing to a bitmap device context) and Stefan
// Schwedt (drawing rotated text)
//
// Version 1 - 31-Jul-2002
// =======================
// Initial version
//
////////////////////////////////////////////////////////////////////////////
// PLEASE LEAVE THIS HEADER INTACT
////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "DoubleListPicker.h"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#define CX_BTN 30
#define CY_BTN 20
#define TITLE_LABEL_GAP 3
#define EDGE_GAP_X 0
#define EDGE_GAP_Y 0
#define LABEL_LIST_GAP 0
#define LIST_BTN_GAP 5
#define BTN_BTN_GAP 10
#define BTN_BTN_GAP_SML 2
#define CY_LABEL 14
const DWORD dwStyleCommon = WS_CHILD;
const DWORD dwStyleCommonTab = dwStyleCommon | WS_TABSTOP;
const DWORD dwStyleLabel = dwStyleCommon;
const DWORD dwStyleTitle = dwStyleCommon;
const DWORD dwStyleButton = dwStyleCommonTab | BS_PUSHBUTTON;
const DWORD dwStyleList = dwStyleCommonTab | WS_BORDER | LBS_NOINTEGRALHEIGHT | LBS_NOTIFY | WS_VSCROLL;
/////////////////////////////////////////////////////////////////////////////
// Helper functions
// recreate the list box by copying styles etc, and list items
// and applying them to a newly created control
static BOOL RecreateListBox(CListBox* pList, LPVOID lpParam = NULL)
{
if (pList == NULL)
return FALSE;
if (pList->GetSafeHwnd() == NULL)
return FALSE;
CWnd* pParent = pList->GetParent();
if (pParent == NULL)
return FALSE;
// get current attributes
DWORD dwStyle = pList->GetStyle();
DWORD dwStyleEx = pList->GetExStyle();
CRect rc;
pList->GetWindowRect(&rc);
pParent->ScreenToClient(&rc); // map to client co-ords
UINT nID = pList->GetDlgCtrlID();
CFont* pFont = pList->GetFont();
CWnd* pWndAfter = pList->GetNextWindow(GW_HWNDPREV);
// create the new list box and copy the old list box items
// into a new listbox along with each item's data, and selection state
CListBox listNew;
if (! listNew.CreateEx(dwStyleEx, _T("LISTBOX"), _T(""), dwStyle, rc, pParent, nID, lpParam))
return FALSE;
listNew.SetFont(pFont);
int nNumItems = pList->GetCount();
BOOL bMultiSel = (dwStyle & LBS_MULTIPLESEL || dwStyle & LBS_EXTENDEDSEL);
for (int n = 0; n < nNumItems; n++)
{
CString sText;
pList->GetText(n, sText);
int nNewIndex = listNew.AddString(sText);
listNew.SetItemData(nNewIndex, pList->GetItemData(n));
if (bMultiSel && pList->GetSel(n))
listNew.SetSel(nNewIndex);
}
if (! bMultiSel)
{
int nCurSel = pList->GetCurSel();
if (nCurSel != -1)
{
CString sSelText;
// get the selection in the old list
pList->GetText(nCurSel, sSelText);
// now find and select it in the new list
listNew.SetCurSel(listNew.FindStringExact(-1, sSelText));
}
}
// destroy the existing window, then attach the new one
pList->DestroyWindow();
HWND hwnd = listNew.Detach();
pList->Attach(hwnd);
// position correctly in z-order
pList->SetWindowPos(pWndAfter == NULL ? &CWnd::wndBottom : pWndAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////
// CDoubleListPicker
CDoubleListPicker::CDoubleListPicker()
{
m_bSortListLeft = TRUE;
m_bSortListRight = TRUE;
m_bMultiSelLeft = TRUE;
m_bMultiSelRight = TRUE;
m_apCtrls[0] = &m_labelTitle;
m_apCtrls[1] = &m_labelLeft;
m_apCtrls[2] = &m_listLeft;
m_apCtrls[3] = &m_btnMoveRight;
m_apCtrls[4] = &m_btnMoveAllRight;
m_apCtrls[5] = &m_btnMoveLeft;
m_apCtrls[6] = &m_btnMoveAllLeft;
m_apCtrls[7] = &m_labelRight;
m_apCtrls[8] = &m_listRight;
m_btnMoveRight.m_pPicker = this;
m_btnMoveAllRight.m_pPicker = this;
m_btnMoveLeft.m_pPicker = this;
m_btnMoveAllLeft.m_pPicker = this;
m_listLeft.m_pPicker = this;
m_listRight.m_pPicker = this;
m_bHorizontal = FALSE;
}
BEGIN_MESSAGE_MAP(CDoubleListPicker, CStatic)
//{{AFX_MSG_MAP(CDoubleListPicker)
ON_WM_CREATE()
ON_WM_SETFOCUS()
ON_WM_ENABLE()
ON_WM_SHOWWINDOW()
ON_WM_WINDOWPOSCHANGED()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
BEGIN_MESSAGE_MAP(CDoubleListPicker::CButtonDLP, CButton)
ON_CONTROL_REFLECT_EX(BN_CLICKED, CDoubleListPicker::CButtonDLP::OnClick)
END_MESSAGE_MAP()
BEGIN_MESSAGE_MAP(CDoubleListPicker::CListBoxDLP, CListBox)
ON_CONTROL_REFLECT_EX(LBN_DBLCLK, CDoubleListPicker::CListBoxDLP::OnDblClk)
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CDoubleListPicker message handlers
BOOL CDoubleListPicker::Create(LPCTSTR lpszTitle, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID)
{
m_sTitle = lpszTitle;
dwStyle |= WS_CHILD;
return CStatic::Create("", dwStyle, rect, pParentWnd, nID);
}
BOOL CDoubleListPicker::CreateList(BOOL bLeft, CWnd* pParent)
{
CRect rc(0, 0, 0, 0);
CListBox& list = GetListCtrl(bLeft);
int nID = ID_LIST_LEFT;
BOOL bSort = m_bSortListLeft;
BOOL bMulti = m_bMultiSelLeft;
if (! bLeft)
{
nID = ID_LIST_RIGHT;
bSort = m_bSortListRight;
bMulti = m_bMultiSelRight;
}
DWORD dwStyle = dwStyleList | (bSort ? LBS_SORT : 0) | (bMulti ? LBS_EXTENDEDSEL : 0);
dwStyle |= (GetStyle() & WS_VISIBLE);
if (! list.Create(dwStyle, rc, pParent, nID))
return FALSE;
list.ModifyStyleEx(0, WS_EX_CLIENTEDGE);
return TRUE;
}
static void SetZOrder(CWnd* pCtrl, const CWnd* pWndAfter)
{
if (pCtrl != NULL && pWndAfter != NULL)
pCtrl->SetWindowPos(pWndAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
}
void CDoubleListPicker::SetHorizontal(BOOL bHorizontal/*=TRUE*/)
{
if (::IsWindow(m_hWnd) && ! bHorizontal != ! m_bHorizontal)
{
m_bHorizontal = bHorizontal;
if (bHorizontal)
{
SetRotatedButtonText(m_btnMoveRight);
SetRotatedButtonText(m_btnMoveAllRight);
SetRotatedButtonText(m_btnMoveLeft);
SetRotatedButtonText(m_btnMoveAllLeft);
}
else
{
m_btnMoveRight.SetBitmap(NULL);
m_btnMoveAllRight.SetBitmap(NULL);
m_btnMoveLeft.SetBitmap(NULL);
m_btnMoveAllLeft.SetBitmap(NULL);
m_btnMoveRight.ModifyStyle(BS_BITMAP, 0);
m_btnMoveAllRight.ModifyStyle(BS_BITMAP, 0);
m_btnMoveLeft.ModifyStyle(BS_BITMAP, 0);
m_btnMoveAllLeft.ModifyStyle(BS_BITMAP, 0);
}
DoSizing();
}
m_bHorizontal = bHorizontal;
}
void CDoubleListPicker::SetRotatedButtonText(CButton& btn, LPCTSTR lpszText/*=NULL*/, int nAngle/*=270*/)
{
CString sText(lpszText);
if (sText.IsEmpty())
btn.GetWindowText(sText);
btn.ModifyStyle(0, BS_BITMAP);
// code for drawing to a bitmap device context from code by Anneke Sicherer-Roetman
// (see http://www.codeproject.com/bitmap/bitmapdc.asp)
CDC dc;
CWindowDC dcWin(NULL);
CBitmap bmp;
VERIFY(bmp.CreateCompatibleBitmap(&dcWin, CX_BTN, CY_BTN));
VERIFY(dc.CreateCompatibleDC(&dcWin));
int nSavedDC = dc.SaveDC();
dc.SelectObject(&bmp);
dc.SetMapMode(dcWin.GetMapMode());
dc.FillSolidRect(0, 0, CX_BTN, CY_BTN, GetSysColor(COLOR_BTNFACE));
// code to draw rotated text from code by Stefan Schwedt
// (see http://www.codeproject.com/gdi/textrotation.asp)
// get the centre of the rotated text
// (only works for true-type fonts, or angles not divisible by 90)
LOGFONT lf;
CFont font;
font.CreateStockObject(DEFAULT_GUI_FONT);
font.GetLogFont(&lf);
// can't use MS Sans Serif, so use a true-type font
// that is likely to be found on all platforms, and
// that looks OK. (On 2K/XP the default should be OK.)
if (stricmp(lf.lfFaceName, "MS Sans Serif") == 0)
strcpy(lf.lfFaceName, "Tahoma");
font.DeleteObject();
lf.lfEscapement = lf.lfOrientation = 2700; // = 90 degrees clockwise
VERIFY(font.CreateFontIndirect(&lf));
dc.SelectObject(&font);
CRect rcText(0, 0, CX_BTN, CY_BTN);
CSize TextSize = dc.GetTextExtent(sText);
CPoint rcenter;
rcenter.x = TextSize.cy / 2;
rcenter.y = -(TextSize.cx / 2);
// draw the text and move it to the centre of the rectangle
dc.SetTextAlign(TA_BOTTOM);
dc.SetBkMode(TRANSPARENT);
dc.ExtTextOut(rcText.left + rcText.Width() / 2 - rcenter.x,
rcText.top + rcText.Height() / 2 + rcenter.y,
0, rcText, sText, NULL);
dc.RestoreDC(nSavedDC);
// now associate the bitmap with the button
btn.SetBitmap((HBITMAP)bmp.Detach());
}
BOOL CDoubleListPicker::CreateChildControls()
{
if (m_labelTitle.GetSafeHwnd() != NULL)
return TRUE; // already created controls
CWnd* pParent = GetParent();
if (pParent == NULL)
return FALSE;
// create child controls
CRect rc(0, 0, 0, 0);
if (! m_labelTitle.Create(m_sTitle, dwStyleTitle, rc, pParent, ID_TITLE) ||
! m_labelLeft.Create(m_sLabelLeft, dwStyleLabel, rc, pParent, ID_LABEL_LEFT) ||
! CreateList(TRUE, pParent) ||
! m_btnMoveRight.Create(">", dwStyleButton, rc, pParent, ID_BTN_MOVE_RIGHT) ||
! m_btnMoveAllRight.Create(">>", dwStyleButton, rc, pParent, ID_BTN_MOVE_ALL_RIGHT) ||
! m_btnMoveLeft.Create("<", dwStyleButton, rc, pParent, ID_BTN_MOVE_LEFT) ||
! m_btnMoveAllLeft.Create("<<", dwStyleButton, rc, pParent, ID_BTN_MOVE_ALL_LEFT) ||
! m_labelRight.Create(m_sLabelRight, dwStyleLabel, rc, pParent, ID_LABEL_RIGHT) ||
! CreateList(FALSE, pParent))
return FALSE;
CFont* pFont = GetFont();
BOOL bEnable = IsWindowEnabled();
BOOL bShow = (GetStyle() & WS_VISIBLE);
// set font, z-order, visibility, and enabled state
for (int n = 0; n < NUM_CTRLS; n++)
{
m_apCtrls[n]->SetFont(pFont);
SetZOrder(m_apCtrls[n], n == 0 ? this : m_apCtrls[n - 1]);
m_apCtrls[n]->EnableWindow(bEnable);
m_apCtrls[n]->ShowWindow(bShow ? SW_SHOW : SW_HIDE);
}
BOOL bHorizontal = m_bHorizontal;
m_bHorizontal = FALSE;
SetHorizontal(bHorizontal);
return TRUE;
}
int CDoubleListPicker::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CStatic::OnCreate(lpCreateStruct) == -1)
return -1;
if (! CreateChildControls())
return -1;
return 0;
}
void CDoubleListPicker::DoSizing()
{
// put child controls in the correct places
if (m_labelTitle.GetSafeHwnd() == NULL)
return; // controls not yet created
if (GetParent() == NULL)
return;
CRect rc;
GetWindowRect(&rc);
GetParent()->ScreenToClient(&rc);
int nTitleLabelGap = 0;
if (! m_sTitle.IsEmpty())
{
// title extends full width, bar the offsets
m_labelTitle.ShowWindow(GetStyle() & WS_VISIBLE ? SW_SHOW : SW_HIDE);
int nTitleWidth = rc.Width() - (EDGE_GAP_X * 2);
CRect rcTitle(0, 0, nTitleWidth, rc.Height());
CDC* pDC = m_labelTitle.GetDC();
ASSERT(pDC);
CFont* pOldFont = (CFont*)pDC->SelectObject(m_labelTitle.GetFont());
int nTitleHeight = pDC->DrawText(m_sTitle, &rcTitle,
DT_CALCRECT | DT_EXTERNALLEADING | DT_WORDBREAK | DT_LEFT | DT_NOPREFIX);
pDC->SelectObject(pOldFont);
m_labelTitle.MoveWindow(rc.left + EDGE_GAP_X, rc.top + EDGE_GAP_Y, nTitleWidth, nTitleHeight);
m_labelTitle.SetWindowText(m_sTitle);
rc.top += nTitleHeight + EDGE_GAP_Y;
nTitleLabelGap = TITLE_LABEL_GAP;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -