2025-11-28 00:35:46 +09:00

572 lines
16 KiB
C++

/*************************************************************************************************
* Description: Implementation of the custom list control.
*
* See EntryPoint.cpp for a full description of this sample.
*
*
* Copyright (C) Microsoft Corporation. All rights reserved.
*
* This source code is intended only as a supplement to Microsoft
* Development Tools and/or on-line documentation. See these other
* materials for detailed information regarding Microsoft code samples.
*
* THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
* KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
* PARTICULAR PURPOSE.
*
*************************************************************************************************/
#include "CustomControl.h"
#include "UIAProviders.h"
// CustomListControl class.
//
CustomListControl::CustomListControl(HWND hwnd) :
m_selectedIndex(-1), m_pListProvider(NULL), m_controlHwnd(hwnd)
{
// Initialize the list items.
AddItem(Status_Online, L"Fred");
AddItem(Status_Offline, L"Prakash");
AddItem(Status_Online, L"Kim");
AddItem(Status_Online, L"Sandra");
AddItem(Status_Offline, L"Silvio");
SelectItem(0);
}
// Destructor.
//
CustomListControl::~CustomListControl()
{
if (m_pListProvider != NULL)
{
m_pListProvider->Release();
}
m_itemCollection.clear();
}
// Adds an item to the end of the list.
//
bool CustomListControl::AddItem(ContactStatus status, PCWSTR name)
{
if ((name == NULL) || (GetCount() >= MaxItems))
{
return false;
}
int id = CreateUniqueId();
CustomListItem* newItem = new (std::nothrow) CustomListItem(this, id, name);
if (newItem != NULL)
{
newItem->SetStatus(status);
// Add to collection.
m_itemCollection.push_back(newItem);
SelectItem(0);
// Raise UI Automation event.
ListItemProvider* itemProvider = newItem->GetListItemProvider();
itemProvider->NotifyItemAdded();
return true;
}
return false;
}
// Removes the selected item.
//
bool CustomListControl::RemoveSelected()
{
int index = GetSelectedIndex();
LISTITERATOR itemToDelete = GetItemAt(index);
// Don't allow deletion of the last remaining item. This is just to
// simplify the logic of the sample.
if (GetCount() == 1)
{
return false;
}
CustomListItem* pItem = static_cast<CustomListItem*>(*itemToDelete);
// Raise event.
ListItemProvider* itemProvider = pItem->GetListItemProvider();
itemProvider->NotifyItemRemoved();
// Remove from list.
m_itemCollection.erase(itemToDelete);
// Delete object.
delete pItem;
// Select at the same index; if we deleted the bottom item,
// the index will be decremented.
SelectItem(index);
return true;
}
LISTITERATOR CustomListControl::GetItemAt(int index)
{
return m_itemCollection.begin() + index;
}
// Gets the UI Automation provider for the list; creates it if necessary.
//
ListProvider* CustomListControl::GetListProvider()
{
if (m_pListProvider == NULL)
{
m_pListProvider = new (std::nothrow) ListProvider(this);
}
return m_pListProvider;
}
// Gets the index of the item at a point on the Y coordinate within the list.
//
int CustomListControl::IndexFromY(int y)
{
int index = y / ItemHeight;
if ((index < 0) || (GetCount() <= index))
{
index = -1;
}
return index;
}
// Sets the selected item and forces refresh.
//
void CustomListControl::SelectItem(int index)
{
m_selectedIndex = index;
if (m_selectedIndex >= static_cast<int>(m_itemCollection.size()))
{
m_selectedIndex = static_cast<int>(m_itemCollection.size()) - 1;
}
InvalidateRect(m_controlHwnd, NULL, false);
// Raise UI Automation event
if (m_pListProvider != NULL)
{
ListItemProvider* itemProvider = m_pListProvider->GetItemProviderByIndex(m_selectedIndex);
if (itemProvider != NULL)
{
itemProvider->NotifyElementSelected();
}
}
}
// Gets the index of the selected item.
//
int CustomListControl::GetSelectedIndex()
{
return m_selectedIndex;
}
// Gets the focused state.
//
bool CustomListControl::GetIsFocused()
{
return m_hasFocus;
}
// Sets the focused state.
//
void CustomListControl::SetIsFocused(bool isFocused)
{
m_hasFocus = isFocused;
}
// Gets the count of items in the list.
//
int CustomListControl::GetCount()
{
return static_cast<int>(m_itemCollection.size());
}
// Creates a unique identifier within this instance of the control.
//
int CustomListControl::CreateUniqueId()
{
static int uniqueId;
return uniqueId++;
};
// Gets the HWND of the control.
//
HWND CustomListControl::GetHwnd()
{
return m_controlHwnd;
}
// Registers the control class.
//
void RegisterListControl(HINSTANCE hInstance)
{
WNDCLASS wc = {};
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = ControlWndProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = L"CONTACTLIST";
RegisterClass(&wc);
}
// Helper function.
CustomListControl* GetControl(HWND hwnd)
{
return reinterpret_cast<CustomListControl*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
}
// Handles window messages for the HWND that contains the custom control.
//
LRESULT CALLBACK ControlWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_CREATE:
{
// Create the control object.
CustomListControl* pCustomList = new (std::nothrow) CustomListControl(hwnd);
// Save the class instance as window data so that its members
// can be accessed from within this function.
SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(pCustomList));
break;
}
case WM_DESTROY:
{
// Destroy the control so interfaces are released.
CustomListControl* pCustomList = GetControl(hwnd);
delete pCustomList;
break;
}
case WM_GETOBJECT:
{
// Register the control with UI Automation.
// If the lParam matches the RootObjectId, send back the list provider.
if (static_cast<long>(lParam) == static_cast<long>(UiaRootObjectId))
{
// Get the control.
CustomListControl* pCustomList = GetControl(hwnd);
// Return its associated UI Automation provider.
LRESULT lresult = UiaReturnRawElementProvider(
hwnd, wParam, lParam, pCustomList->GetListProvider());
return lresult;
}
return 0;
}
case WM_PAINT:
{
// Retrieve the control.
CustomListControl* pCustomList = GetControl(hwnd);
// Set up graphics context.
PAINTSTRUCT paintStruct;
HDC hdc = BeginPaint(hwnd, &paintStruct);
RECT clientRect;
GetClientRect(hwnd, &clientRect);
// Save the context.
HGDIOBJ oldHgdi = SelectObject(hdc, GetStockObject(BLACK_PEN));
// Draw items.
// Create and select a null pen so the rectangle isn't outlined.
HPEN nullPen = CreatePen(PS_NULL, 1, RGB(0,0,0));
SelectObject(hdc, nullPen);
// Erase the whole window.
Rectangle(hdc, clientRect.left, clientRect.top, clientRect.right,
clientRect.bottom);
// Create and select the font.
HFONT font = GetFont(hwnd, -11);
HGDIOBJ oldFont = SelectObject(hdc, font);
// Set transparency for text.
SetBkMode(hdc, TRANSPARENT);
int ItemHeight = pCustomList->ItemHeight;
// Create brushes
HBRUSH unfocusedFillBrush = GetSysColorBrush(COLOR_BTNFACE);
HBRUSH focusedFillBrush = GetSysColorBrush(COLOR_HIGHLIGHT);
HBRUSH onlineFillBrush = CreateSolidBrush(RGB(0, 192, 0)); // Green.
HBRUSH offlineFillBrush = CreateSolidBrush(RGB(255, 0, 0)); // Red.
if (pCustomList->GetCount() > 0)
{
for (int i = 0; i < pCustomList->GetCount(); i++)
{
// Get the rectangle for the item.
RECT itemRect;
itemRect.left = clientRect.left + 2;
itemRect.top = clientRect.top + 2 + ItemHeight * i;
itemRect.right = clientRect.right - 2;
itemRect.bottom = itemRect.top + ItemHeight;
// Set the default text color.
SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
// Set up the appearance of the focused item.
// It's different depending on whether the list control has focus.
if (i == pCustomList->GetSelectedIndex())
{
if (pCustomList->GetIsFocused())
{
SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
HGDIOBJ oldBrush = SelectObject(hdc, focusedFillBrush);
Rectangle(hdc, itemRect.left+1, itemRect.top+1,
itemRect.right, itemRect.bottom);
SelectObject(hdc, oldBrush);
}
else
{
HGDIOBJ oldBrush = SelectObject(hdc, unfocusedFillBrush);
Rectangle(hdc, itemRect.left, itemRect.top, itemRect.right, itemRect.bottom);
SelectObject(hdc, oldBrush);
}
DrawFocusRect(hdc, &itemRect);
}
// Get the item.
LISTITERATOR item = pCustomList->GetItemAt(i);
CustomListItem* pItem = static_cast<CustomListItem*>(*item);
// Draw the text.
TextOut(hdc, itemRect.left + pCustomList->ImageWidth + 5, itemRect.top + 2,
pItem->GetName(), static_cast<int>(wcslen(pItem->GetName())));
// Draw the status icon.
if (pItem->GetStatus() == Status_Online)
{
SelectObject(hdc, onlineFillBrush);
Rectangle(hdc, itemRect.left + 2, itemRect.top + 3,
itemRect.left + pCustomList->ImageWidth + 2, itemRect.top + 3 + pCustomList->ImageHeight);
}
else
{
SelectObject(hdc, offlineFillBrush);
Ellipse(hdc, itemRect.left + 2, itemRect.top + 3,
itemRect.left + pCustomList->ImageWidth + 2, itemRect.top + 3 + pCustomList->ImageHeight);
}
} // for each item.
}
EndPaint(hwnd, &paintStruct);
// Restore context.
SelectObject(hdc, oldFont);
SelectObject(hdc, oldHgdi);
// Clean brushes.
DeleteObject(font);
DeleteObject(nullPen);
DeleteObject(focusedFillBrush);
DeleteObject(unfocusedFillBrush);
DeleteObject(onlineFillBrush);
DeleteObject(offlineFillBrush);
break;
}
case WM_SETFOCUS:
{
CustomListControl* pCustomList = GetControl(hwnd);
if (pCustomList != NULL)
{
pCustomList->SetIsFocused(true);
InvalidateRect(hwnd, NULL, TRUE);
}
break;
}
case WM_KILLFOCUS:
{
CustomListControl* pCustomList = GetControl(hwnd);
pCustomList->SetIsFocused(false);
InvalidateRect(hwnd, NULL, TRUE);
break;
}
case CUSTOMLB_REMOVEITEM:
{
CustomListControl* pCustomList = GetControl(hwnd);
pCustomList->RemoveSelected();
break;
}
case CUSTOMLB_ADDITEM:
{
CustomListControl* pCustomList = GetControl(hwnd);
pCustomList->AddItem((ContactStatus)wParam, (WCHAR*)lParam);
break;
}
case WM_GETDLGCODE:
{
// Trap arrow keys.
return DLGC_WANTARROWS | DLGC_WANTCHARS;
break;
}
case WM_LBUTTONDOWN:
{
// Retrieve the control.
CustomListControl* pCustomList = GetControl(hwnd);
// Get the item under the cursor. This is -1 if the user clicked on a blank space.
int y = HIWORD(lParam);
int newItem = pCustomList->IndexFromY(y);
// Set the focus to the control regardless of whether the selection is valid.
SetFocus(hwnd);
if (newItem >= 0)
{
pCustomList->SelectItem(newItem);
}
break;
}
case WM_KEYDOWN:
// Move the selection with up/down arrows.
{
// Retrieve the control.
CustomListControl* pCustomList = GetControl(hwnd);
// Ignore keystrokes if listbox does not have focus.
switch (wParam)
{
case VK_UP:
if (pCustomList->GetSelectedIndex() > 0)
{
pCustomList->SelectItem(pCustomList->GetSelectedIndex() - 1);
}
return 0;
break;
case VK_DOWN:
if (pCustomList->GetSelectedIndex() < pCustomList->GetCount() - 1)
{
pCustomList->SelectItem(pCustomList->GetSelectedIndex() + 1);
}
return 0;
break;
}
break; // WM_KEYDOWN
}
} // switch (message)
return DefWindowProc(hwnd, message, wParam, lParam);
}
// CustomListItem class
//
// Constructor.
CustomListItem::CustomListItem(CustomListControl* pOwner, int id, PCWSTR name)
{
m_pOwnerControl = pOwner;
m_Id = id;
m_name = _wcsdup(name);
m_pListItemProvider = NULL;
}
// Destructor.
CustomListItem::~CustomListItem()
{
free(m_name);
if (m_pListItemProvider != NULL)
{
m_pListItemProvider->Release();
}
}
// Gets the index of this item in the collection.
//
int CustomListItem::GetItemIndex()
{
for (int i = 0; i < m_pOwnerControl->GetCount(); i++)
{
LISTITERATOR item = m_pOwnerControl->GetItemAt(i);
CustomListItem* pItem = static_cast<CustomListItem*>(*item);
if (pItem == this)
{
return i;
}
}
// Item not found; shouldn't happen.
return -1;
}
// Gets the UI Automation provider for the list item; creates it if necessary.
//
ListItemProvider* CustomListItem::GetListItemProvider()
{
if (m_pListItemProvider == NULL)
{
m_pListItemProvider = new (std::nothrow) ListItemProvider(this);
}
return m_pListItemProvider;
}
// Gets the custom list control that holds this item.
//
CustomListControl* CustomListItem::GetOwnerList()
{
return m_pOwnerControl;
}
// Gets the status (online/offline) of this contact.
//
ContactStatus CustomListItem::GetStatus()
{
return m_status;
}
// Sets the status (online/offline) of this contact.
//
void CustomListItem::SetStatus(ContactStatus status)
{
m_status = status;
}
// Gets the name of the contact.
//
WCHAR* CustomListItem::GetName()
{
return m_name;
}
// Gets the Id of the contact.
//
int CustomListItem::GetId()
{
return m_Id;
}
// Helper functions.
//
// Retrieves a font for list items.
//
HFONT GetFont(HWND hwnd, LONG height)
{
// Query the font metrics data for the message box font
static LOGFONT lf;
HTHEME hTheme = OpenThemeData(hwnd, L"WINDOW");
GetThemeSysFont(hTheme, TMT_MSGBOXFONT, &lf);
CloseThemeData(hTheme);
// Change the font size.
lf.lfHeight = height;
// Create the font and return its handle.
return CreateFont(lf.lfHeight, lf.lfWidth,
lf.lfEscapement, lf.lfOrientation, lf.lfWeight,
lf.lfItalic, lf.lfUnderline, lf.lfStrikeOut, lf.lfCharSet,
lf.lfOutPrecision, lf.lfClipPrecision, lf.lfQuality,
lf.lfPitchAndFamily, lf.lfFaceName);
}