Why
would some
one want to
use
GDI+
in an
ActiveX
control?
GDI+
offers many
attractive
graphics
features
that, though
possible
with
GDI
alone, would
require a
significant
amount of
additional
work to
implement
them.
Therefore,
coupling
GDI+
with
ActiveX
controls
makes it
possible to
create
visually
appealing,
portable,
reusable
controls in
a relatively
short period
of time.
It seems
worth
mentioning
that
GDI+
will work in
both
windowed,
and
windowless
ActiveX
controls.
However, in
my opinion,
windowless
controls
really show
the value of
GDI+
in
this
context.
With a
windowless
control, you
can use
GDI+�s
built in
alpha
blending and
anti-aliasing
to make a
non-rectangular
control fit
seamlessly
onto its
container�s
window.
Here is a
quick list
of
examples:
-
Curved
Controls
Using
GDI
to
generate
a curve
at low
resolution
results
in a
rather
jagged
object.
It would
be
possible
to make
the
control
as a
bitmap
image
and
dither
the
edges of
the
curve to
the
background
color.
However,
this
only
works if
the
color
isn�t
going to
change,
and the
image
isn�t
going to
be
resized
(unless
you
don't
mind
interpolation).
Now,
with
GDI+
that
same
control
could,
through
anti-aliasing
and
windowless
activation,
be used
on all
backgrounds,
at any
size,
because
the
anti-aliasing
occurs
at
runtime.
-
Drop
Shadows/Highlights/Glows
With a
windowless
control
and
GDI+,
it is
easy to
add
translucent
effects
that
incorporate
the
container's
background.
For
instance,
blending
a dark
color
with the
color
behind
it gives
the
impression
that it
is a
shadow
being
cast.
Alpha
blending
enables
this,
because
a
windowless
control
is able
to
translucently
fill
over its
container's
background.
What is
needed to do
this?
As of this
writing, the
libraries
and header
files for
GDI+
are
available in
the
Microsoft
Platform SDK.
There are
several
other
articles
available on
code project
that
describe how
to get and
install
GDI+,
so I will
quickly
enumerate
the steps
required to
get a simple
app up and
running in
VC++6:
-
Install
the
Platform
SDK
- In
VC++6
go to
tools->options->directories,
and for
both
include
files
and
library
files,
add the
path for
the
Platform
SDK
([install
path]\include
for
includes,
and
[install
path]\lib
for
libraries)
to the
top of
the
directory
list.
- For
each
project
go to
project->settings->link
and
select
Input
under
the
"category"
drop
down.
Then
type
gdiplus.lib
into the
"Object/library
modules"
field.
- Add
#include
"gdiplus.h"
to
include
the
headers.
I
usually
add it
to
stdafx.h
so I can
use
GDI+
anywhere
in my
program.
- Call
Gdiplus::GdiplusStartup
before
you do
any
GDI+
calls.
- Call
Gdiplus::GdiplusShutdown
after
you are
done
with
GDI+.
How to
do this?
I�m sure
there are
several ways
to
accomplish
this goal. I
personally
used
VC++6
with the
MFC
ActiveX
Control
Wizard to
create a
windowless
ActiveX
control,
so that is
what this
article
is going to
deal with.
Using
GDI+
in an
ActiveX
control
is very much
like using
it in a
standard
Windows
application.
One very
important
difference
that I found
is in
starting and
stopping
GDI+.
In a Windows
application,
I typically
start
GDI+
when the
application
starts, and
shut it down
right before
the
application
exits. I
found that
this method
does not
seem to work
in ActiveX
controls.
There seem
to be very
particular
times in
which it is
ok to load
GDI+.
In my
testing, if
GDI+
is
initialized
in the
COleControl
derived
class's
constructor,
and shut
down in its
destructor,
then
everything
seems to
work.
The project
I have
submitted
with this
article has
a class
named
InitGDIPlus
specifically
designed to
deal with
starting up
and shutting
down
GDI+.
It is
designed to
ensure that
only one
call to
Gdiplus::GdiplusStartup
is issued
per process,
because I
have read
several
posts that
seem to
indicate
that people
have run
into
problems
when
Gdiplus::GdiplusStartup
is called
too many
times. In
order to
properly
start and
stop
GDI+
in an
ActiveX
control,
I
initialized
GDI+
in the
constructor
of my
COleControl
derived
class, and
shut it down
in the
destructor
using
methods
provided by
the
InitGDIPlus
class.
Collapse
Copy
Code
class InitGDIPlus {
private:
HANDLE m_hMap;
bool m_bInited, m_bInitCtorDtor;
ULONG_PTR m_gdiplusToken;
Gdiplus::GdiplusStartupInput m_gdiplusStartupInput;
long m_initcount;
public:
InitGDIPlus(bool bInitCtorDtor = false) : m_bInitCtorDtor(bInitCtorDtor),
m_bInited(false), m_hMap(NULL), m_gdiplusToken(NULL),
m_gdiplusStartupInput(NULL), m_initcount(0)
{
if (m_bInitCtorDtor) {
Initialize();
}
}
virtual ~InitGDIPlus() {
if (m_bInitCtorDtor) {
Deinitialize();
}
}
void Initialize() {
if (!m_bInited) {
char buffer[1024];
sprintf(buffer, "GDIPlusInitID=%x", GetCurrentProcessId());
m_hMap = CreateFileMapping((HANDLE) INVALID_HANDLE_VALUE, NULL,
PAGE_READWRITE | SEC_COMMIT, 0, sizeof(long), buffer);
if (m_hMap != NULL) {
if (GetLastError() == ERROR_ALREADY_EXISTS) {
CloseHandle(m_hMap);
} else {
m_bInited = true;
Gdiplus::GdiplusStartup(&m_gdiplusToken,
&m_gdiplusStartupInput, NULL);
TRACE("Inited GDIPlus\n");
}
}
}
m_initcount++;
}
void Deinitialize()
{
m_initcount--;
if (m_bInited && m_initcount == 0) {
TRACE("GDIPlus shutdown\n");
Gdiplus::GdiplusShutdown(m_gdiplusToken);
CloseHandle(m_hMap);
m_bInited = false;
}
}
};
Collapse
Copy
Code
CGDIPlusControlCtrl::CGDIPlusControlCtrl() : m_isClicked(false), m_center(50, 50)
{
InitializeIIDs(&IID_DGDIPlusControl, &IID_DGDIPlusControlEvents);
GDI_Plus_Controler.Initialize(); }
CGDIPlusControlCtrl::~CGDIPlusControlCtrl()
{
GDI_Plus_Controler.Deinitialize(); }
One thing to
note is that
windowless
activation
is dependent
on the
ActiveX
container,
and some
containers
do not
support it.
I know it
works in
VB6, but as
far as I can
tell, the
default
container
support
offered by
MFC
does not
support
windowless
activation.
Once
initialized,
GDI+
can
be utilized
just as
though it
were in a
standard
Windows
application.
For my test
project, I
added some
simple
GDI+
calls to my
OnDraw
function to
show
GDI+
in
action. Due
to the fact
that this
OnDraw
function
draws on a
transparent
background,
this could
cause
problems if
this control
does not use
windowless
activation.
Collapse
Copy
Code
void CGDIPlusControlCtrl::OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
using namespace Gdiplus;
Graphics graphics(pdc->m_hDC);
Bitmap bmp(rcBounds.Width(), rcBounds.Height());
Graphics* pMemoryGraphics = Graphics::FromImage(&bmp);
LinearGradientBrush blueGradient(Point(1,1), Point(rcBounds.Width()-2,
rcBounds.Height()-2), Color(0, 0, 0, 255), Color(192, 0, 0, 255));
GraphicsPath gp;
gp.StartFigure();
gp.AddEllipse(2,2, rcBounds.Width() -4, rcBounds.Height() - 4);
PathGradientBrush whiteGradientHighlight(&gp);
whiteGradientHighlight.SetCenterColor(Color(255, 255, 255, 255));
whiteGradientHighlight.SetCenterPoint(Point(m_center.x, m_center.y));
whiteGradientHighlight.SetFocusScales(0.1f, 0.1f);
Color surroundColors[] = {Color(0, 255, 255, 255)};
int surroundColorsCount = 1;
whiteGradientHighlight.SetSurroundColors(surroundColors, &surroundColorsCount);
if(m_antiAliased)
{
pMemoryGraphics->SetSmoothingMode(SmoothingModeAntiAlias);
}
pMemoryGraphics->FillPath(&blueGradient, &gp);
pMemoryGraphics->FillPath(&whiteGradientHighlight, &gp);
if(m_border)
{
Pen pen(Color(255,0,0,0), 2);
pMemoryGraphics->DrawPath(&pen, &gp);
}
graphics.DrawImage(&bmp, rcBounds.left, rcBounds.top);
}
Included
with my
project
files, I
have
enclosed a
VB6 project
that I used
to test my
control. My
control uses
a few
translucent
gradient
fills, so it
is easy to
see how the
windowless
control
blends with
the
container's
background.
In VB6 it is
easy to
change the
background,
so feel free
to play
around with
it to see
how the
control
incorporates
the new
color or
background
image. Here
is a screen
shot of my
container
with a
background
image:
What are
the obvious
drawbacks to
this?
The added
benefits of
GDI+
do
not come
without a
reduction in
performance.
Additionally,
GDI+
only became
a standard
part of
Windows with
Windows XP.
According to
Microsoft,
it will not
run on
Windows 95,
but it can
run on
Windows 98+,
and Windows
NT4+ with an
additional
DLL. The
need for an
additional
DLL could be
a real
problem for
ActiveX
controls
designed for
the web. I�m
sure
additional
drawbacks
exist, they
just weren�t
obvious to
me.
Conclusion
As with
anything,
there are
situations
where this
is useful,
and
situations
where it is
not. If your
goal is to
create a
�cool�
non-standard
UI, this
could be a
good way to
build
reusable
components.
If however,
you are
designing
ActiveX
controls
exclusively
for the web,
then this
may not be
the way to
go.