Introduction
This is an ActiveX control for Poker games. I added several functions to it so that it can fulfill most responsibilities a card should take in a Poker game. Since it's an ActiveX control, practically, it can be used in programs written by any ActiveX-supported languages, like VC++, VB...even in a webpage. Furthermore, with the code I provide to you, you can easily revise it to satisfy your specific requirements, of course, with your knowledge of ActiveX control.
About the control
Each instance of the control class represents a card. It has the following functions and properties attached to it, which a user can manipulate.
Functions
BOOL SetValue(short nNewValue)
: sets the value of the card. The relationship between the value and the card is like: spade 2, 3,..., 10, J, Q, K, A --> 1 ~ 13
heart 2, 3,..., 10, J, Q, K, A --> 14 ~ 26
club 2, 3,..., 10, J, Q, K, A --> 27 ~ 39
diamond 2, 3,..., 10, J, Q, K, A --> 40 ~ 52
small joker --> 53
big joker --> 54
(Note: sorry for my English if I don't use a right term in the Poker game because I am not a native speaker.)
short GetValue()
: gets the value of the card.
BOOL SetBackID(short nNewID)
: sets the backside picture of the card. There are two pictures to choose from, which have IDs 1 and 2.
short GetBackID()
: gets the current backside picture's ID( 1 or 2).
Properties
These are the properties I added to the control. However, you can't access them directly except at the visual design time. You have to do it with the help of the following functions.
BOOL GetBackSide()
: to see whether the card is showing the backside now. TRUE
means it's showing the backside, and vice versa.
void SetBackSide(BOOL)
: make the card show its backside or front side.
BOOL GetIsMovable()
: to see whether the card can be dragged by a mouse. TRUE
means it can, and vice versa.
void SetIsMovable(BOOL)
: make the card can or cannot be draggable.
BOOL GetIsReturn()
: to see whether the card can return to its original place after dragging. TRUE
means it can, and vice versa.
void SetIsReturn(BOOL)
: make the card can or cannot return to its original place after dragging.
BOOL GetIsSelected()
: to see whether the card is currently selected. A selected card have a red frame around it.
void SetIsSelected(BOOL)
: make the card selected or not selected.
For functions, you can call them in your program. For the properties, of course you can also call them in your program. Furthermore, you can set their values at the design time, say, in the resource editor. When choosing the properties of the ActiveX control, you can see some checkboxes for the properties I introduced above. You set the initial values over there and most possibly, change it later in your code by calling the corresponding GetXXX()
and SetXXX()
methods.
Note: I added another function to the control. Whenever you click on it with the left mouse button, it will be selected, which is indicated by a red frame around it.
Stock ActiveX events
At last, I added five stock ActiveX events to the control. They are Click
, DblClick
, MouseDown
, MouseMove
and MouseUp
. In your program, you can process any number of these events using event sink maps. For example, in my test program, I processed the MouseDown
event: when you click the card with the right mouse button, you flip it.
Some coding details
I had drawn the card according to its current value. I saved 54 bitmaps in the program and load them programmatically like this:
void CPokerCtrl::OnDraw(CDC* pdc, const CRect& rcBounds,
const CRect& rcInvalid)
{
CBitmap* pOldBmp = NULL;
CBitmap bmp;
CDC dcMem;
dcMem.CreateCompatibleDC(pdc);
if(m_bBackSide)
{
if(GetBackID() == 1)
bmp.LoadBitmap(IDB_BACKSIDE1);
else
bmp.LoadBitmap(IDB_BACKSIDE2);
}
else
{
bmp.LoadBitmap(IDB_BITMAP1 + GetValue() - 1);
}
pOldBmp = dcMem.SelectObject(&bmp);
pdc->StretchBlt(0, 0, rcBounds.Width(), rcBounds.Height(),
&dcMem, 0, 0, 71, 96, SRCCOPY);
dcMem.SelectObject(pOldBmp);
if(m_bIsSelected)
{
CBrush* pOldBrush = (CBrush*)pdc->SelectStockObject(NULL_BRUSH);
CPen pen(PS_SOLID, 4, RGB(192, 0, 0));
CPen* pOldPen = pdc->SelectObject(&pen);
CRect rect;
GetClientRect(&rect);
pdc->Rectangle(&rect);
pdc->SelectObject(pOldBrush);
pdc->SelectObject(pOldPen);
}
}
To move the card, I processed MouseDown
, MouseMove
and MouseUp
events. Please don't mix them up with those same-name stock ActiveX events. They're different concepts.
void CPokerCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
m_bIsSelected = TRUE;
if(m_bIsMovable)
{
m_bIsMoving = TRUE;
SetCapture();
m_ptMouseInitPos = point;
GetClientRect(&m_rtHomeRect);
::ClientToScreen(m_hWnd, &m_rtHomeRect.TopLeft());
m_ptOldLeftTop = m_rtHomeRect.TopLeft();
CRect rect;
GetClientRect(&rect);
MoveWindow(0, 0, rect.Width(), rect.Height(), TRUE);
GetClientRect(&m_rtHomeRect);
::ClientToScreen(m_hWnd, &m_rtHomeRect.TopLeft());
::ClientToScreen(m_hWnd, &m_rtHomeRect.BottomRight());
}
COleControl::OnLButtonDown(nFlags, point);
}
void CPokerCtrl::OnMouseMove(UINT nFlags, CPoint point)
{
if(m_bIsMoving)
{
CPoint myPoint = point;
ClientToScreen(&myPoint);
MoveWindow(myPoint.x - m_ptMouseInitPos.x - m_rtHomeRect.left,
myPoint.y - m_ptMouseInitPos.y - m_rtHomeRect.top,
m_rtHomeRect.Width(), m_rtHomeRect.Height(), TRUE);
}
COleControl::OnMouseMove(nFlags, point);
}
void CPokerCtrl::OnLButtonUp(UINT nFlags, CPoint point)
{
if(m_bIsMoving)
{
ReleaseCapture();
m_bIsMoving = FALSE;
if(m_bIsReturn)
{
MoveWindow(m_ptOldLeftTop.x - m_rtHomeRect.left,
m_ptOldLeftTop.y - m_rtHomeRect.top,
m_rtHomeRect.Width(), m_rtHomeRect.Height(), TRUE);
}
}
COleControl::OnLButtonUp(nFlags, point);
}
To use the control
Register the control
Like any COM object, an ActiveX control can't be used unless it is registered on the host system. If you build the control with Visual C++ yourself, the control is automatically registered as part of the build process. If you want to use the control directly without bothering to compile it, you need to register it on your system, before it can be used. Here are two ways to register a control on your system.
The first way is to register the control programmatically. Because an OCX is a self-registering in-proc COM server, the program can load the OCX as if it were an ordinary DLL, find the address of its DllRegisterServer
function, and call the function. DllRegisterServer
, in turn, will register any and all of the controls in the OCX. The following code demonstrates how this is done if the OCX is named Poker.ocx:
HINSTANCE hOcx = ::LoadLibrary (_T ("Poker.ocx"));
if (hOcx != NULL)
{
FARPROC lpfn = ::GetProcAddress (hOcx, _T ("DllRegisterServer"));
if (lpfn != NULL)
(*lpfn) ();
::FreeLibrary (hOcx);
}
The second way is to use the Regsvr32 utility that comes with Visual C++. If Poker.ocx is in the current directory, typing the following command into a command prompt window will register the OCX's controls:
Regsvr32 Poker.ocx
By the same token, passing a /U switch to Regsvr32 unregisters the controls in an OCX:
Regsvr32 /U Poker.ocx
Use it
You add it to your program just like an ordinary control, say, add it to your dialog. You can size it as you like. You can set the properties in the properties menu entry. Later in your code, you can change them by calling the corresponding functions.
In my test program, I added three controls to the dialog, and three member variables, one for each control.
CPoker m_ctlPoker1;
CPoker m_ctlPoker2;
CPoker m_ctlPoker3;
DDX_Control(pDX, IDC_POKER1, m_ctlPoker1);
DDX_Control(pDX, IDC_POKER2, m_ctlPoker2);
DDX_Control(pDX, IDC_POKER3, m_ctlPoker3);
I also added two checkboxes to demonstrate how to toggle the properties IsMovable
and IsReturn
.
void CPokerTestDlg::OnCheckMovable()
{
m_bMovable = !m_bMovable;
m_ctlPoker1.SetIsMovable(m_bMovable);
m_ctlPoker2.SetIsMovable(m_bMovable);
m_ctlPoker3.SetIsMovable(m_bMovable);
}
void CPokerTestDlg::OnCheckReturn()
{
m_bReturn = !m_bReturn;
m_ctlPoker1.SetIsReturn(m_bReturn);
m_ctlPoker2.SetIsReturn(m_bReturn);
m_ctlPoker3.SetIsReturn(m_bReturn);
}
In the stock control events, I only processed MouseDown
. I used it to make sure that at most one card gets selected at a time and a right click on it will flip the card. Of course, you can choose to process more events to fit your needs.
void CPokerTestDlg::OnMouseDownPoker1(short Button,
short Shift, long x, long y)
{
m_ctlPoker1.SetIsSelected(TRUE);
m_ctlPoker2.SetIsSelected(FALSE);
m_ctlPoker3.SetIsSelected(FALSE);
if(Button == MK_RBUTTON)
{
m_ctlPoker1.SetBackSide(!m_ctlPoker1.GetBackSide());
}
}
void CPokerTestDlg::OnMouseDownPoker2(short Button,
short Shift, long x, long y)
{
m_ctlPoker1.SetIsSelected(FALSE);
m_ctlPoker2.SetIsSelected(TRUE);
m_ctlPoker3.SetIsSelected(FALSE);
if(Button == MK_RBUTTON)
{
m_ctlPoker2.SetBackSide(!m_ctlPoker2.GetBackSide());
}
}
void CPokerTestDlg::OnMouseDownPoker3(short Button,
short Shift, long x, long y)
{
m_ctlPoker1.SetIsSelected(FALSE);
m_ctlPoker2.SetIsSelected(FALSE);
m_ctlPoker3.SetIsSelected(TRUE);
if(Button == MK_RBUTTON)
{
m_ctlPoker3.SetBackSide(!m_ctlPoker3.GetBackSide());
}
}
Well, so far, I think I have covered most aspects about this control and how to use it. You can also use it in your VB program, webpage and so on although I haven't tried it myself.
Since I suppose the readers have a certain knowledge about ActiveX control, I skipped some unnecessary details. For those of you who is not quite familiar with ActiveX control, the last chapter of "Programming Windows with MFC", 2nd Edition by Jeff Prosise, is a good resource to learn. Enjoy!