using
System;
using
System.ComponentModel;
using
System.Drawing;
using
System.Drawing.Text;
using
System.Windows.Forms;
using
Microsoft.DirectX;
using
Microsoft.DirectX.DirectDraw;
namespace
WellFormed
{
///
<summary>
/// Summary
description for DXMarquee.
///
</summary>
public class
DXMarquee : Control
{
Device
_device;
Surface
_primary;
Surface _backBuffer;
Surface _textBuffer;
Clipper _primaryClipper;
int
_interval=20;
[
Category("Behavior"),
Description("Gets
or sets the timer interval in milliseconds")
]
public
int Interval
{
get{return
_interval;}
set
{
_interval=value;
if(_interval<1)
_interval=1;
if(t!=null)
t.Interval=value;
}
}
int
_step=1;
[
Category("Behavior"),
Description("Gets
or Sets the number of pixels the text will move per tick")
]
public
int Step
{
get{return
_step;}
set
{
_step=value;
if(_step<1)
_step=1;
}
}
Timer t=new
Timer();
public
DXMarquee()
{
this.SetStyle(ControlStyles.AllPaintingInWmPaint
| ControlStyles.UserPaint, true);
//Create
the device and set the cooperative level.
_device=new Device();
_device.SetCooperativeLevel(this,CooperativeLevelFlags.Normal);
t.Tick+=new
EventHandler(t_Tick);
t.Interval=Interval;
t.Enabled=true;
}
protected
override void
OnPaintBackground(PaintEventArgs pevent)
{
//Don't
paint the background
}
protected
void CreateSurfaces(Bitmap bm)
{
SurfaceDescription
sd=new SurfaceDescription();
//Create
DirectX surfaces. First the primary surface.
if(_primary==null)
{
sd.SurfaceCaps.PrimarySurface=true;
_primary=new Surface(sd,_device);
}
//This
can be called if the control changes size so we always create a new backbuffer
if(_backBuffer!=null)
_backBuffer.Dispose();
//now
the back buffer surface as an offscreen plain buffer built from the bitmap
handed in
sd.Clear();
sd.Width=this.Width;
sd.Height=this.Height;
sd.SurfaceCaps.OffScreenPlain=true;
_backBuffer=new
Surface(sd,_device);
if(_textBuffer!=null)
_textBuffer.Dispose();
//now
the image surface from which the text is copied
sd.Clear();
sd.SurfaceCaps.OffScreenPlain=true;
sd.Height=bm.Height;
sd.Width=bm.Width;
_textBuffer=new
Surface(bm,sd,_device);
//Finally,
the clipper is set up to ensure the output is limited to the area of this
control
if(_primaryClipper!=null)
_primaryClipper.Dispose();
_primaryClipper=new
Clipper(_device);
_primaryClipper.Window=this;
_primary.Clipper=_primaryClipper;
}
Bitmap _bm;
int
_textWidth;
int
_marqueeOffset=0;
protected
void CreateText()
{
if(this.Height==0
|| this.Width==0)
return;
Font=new
Font(Font.FontFamily,0.6f*this.Height);
Graphics g=CreateGraphics();
g.TextRenderingHint=TextRenderingHint.AntiAlias;
SizeF
sf=g.MeasureString(this.Text,this.Font,2048,StringFormat.GenericTypographic);
_textWidth=(int)(0.5f+sf.Width);
if(_textWidth==0)
return;
if(_bm!=null)
_bm.Dispose();
_bm=new
Bitmap(_textWidth,this.Height);
g=Graphics.FromImage(_bm);
g.Clear(this.BackColor);
g.TextRenderingHint=TextRenderingHint.AntiAlias;
SolidBrush
sb=new SolidBrush(this.ForeColor);
g.DrawString(Text,Font,sb,0,0,StringFormat.GenericTypographic);
sb.Dispose();
_marqueeOffset=0;
this.CreateSurfaces(_bm);
}
protected
override void
OnSizeChanged(EventArgs e)
{
this.CreateText();
base.OnSizeChanged
(e);
}
protected
override void
OnTextChanged(EventArgs e)
{
this.CreateText();
base.OnTextChanged
(e);
}
protected
override void
OnBackColorChanged(EventArgs e)
{
this.CreateText();
base.OnBackColorChanged
(e);
}
protected
override void
OnFontChanged(EventArgs e)
{
this.CreateText();
base.OnFontChanged
(e);
}
protected
override void
OnForeColorChanged(EventArgs e)
{
this.CreateText();
base.OnForeColorChanged
(e);
}
private
void t_Tick(object
sender, EventArgs e)
{
_marqueeOffset+=Step;
if(_marqueeOffset>this.Width+_textWidth)
_marqueeOffset=0;
}
protected
override void
OnPaint(PaintEventArgs e)
{
//the
back bufffer is copied to the primary surface.
//First a quick check to see if there's actually anything to do.
if(_primary==null
|| this._backBuffer==null)
return;
//define
the destination rectangle
Rectangle dest=new Rectangle(this.ClientSize.Width-_marqueeOffset,0,this._textWidth,this.Height);
dest.Intersect(new
Rectangle(0,0,this.Width,this.Height));
//The
source is a moving window onto the pre-drawn bitmap.
//The
marquee starts from the right and scrolls left
//so
the offset is used to calculate the amount of text seen
Rectangle src=new
Rectangle(0,0,Math.Min(dest.Width,_textWidth),this.Height);
if(_marqueeOffset>this.Width)
src.Offset(this._marqueeOffset-this.Width,0);
src.Intersect(new
Rectangle(0,0,_textWidth,this.Height));
_backBuffer.ColorFill(this.BackColor);
if(src.Width!=0
&& dest.Width!=0)
{
_backBuffer.Draw(dest,_textBuffer,src,DrawFlags.DoNotWait);
_primary.Draw(RectangleToScreen(this.ClientRectangle),_backBuffer,this.ClientRectangle,DrawFlags.DoNotWait);
}
base.OnPaint(e);
}
}
}
Imports
System
Imports
System.ComponentModel
Imports
System.Drawing
Imports
System.Drawing.Text
Imports
System.Windows.Forms
Imports
Microsoft.DirectX
Imports
Microsoft.DirectX.DirectDraw
Namespace
WellFormed
'/ <summary>
'/ Summary description for DXMarquee.
'/ </summary>
Public Class
DXMarquee
Inherits
Control
Dim
_device As Device
Dim
_primary As Surface
Dim
_backBuffer As Surface
Dim
_textBuffer As Surface
Dim
_primaryClipper As Clipper
Dim
_interval As Integer
= 20
<Category("Behavior"),
_
Description("Gets
or sets the timer interval in milliseconds")> _
Public
Property Interval() As
Integer
Get
Return
_interval
End
Get
Set(ByVal
Value As Integer)
_interval = value
If
(_interval < 1) Then
_interval = 1
End
If
If
Not t Is
Nothing Then
t.Interval
= Value
End
If
End
Set
End
Property
Dim
_step As Integer
= 1
<Category("Behavior"),
_
Description("Gets
or Sets the number of pixels the text will move per tick")> _
Public
Property Nstep() As
Integer
Get
Return
_step
End
Get
Set(ByVal
Value As Integer)
_step
= value
If
(_step < 1) Then
_step = 1
End
If
End
Set
End
Property
Dim
t As Timer = New
Timer
Public
Sub New()
Me.SetStyle(ControlStyles.AllPaintingInWmPaint
Or ControlStyles.UserPaint,
True)
'Create
the device and set the cooperative level.
_device
= New Device
_device.SetCooperativeLevel(Me,
CooperativeLevelFlags.Normal)
AddHandler
t.Tick, AddressOf t_Tick
t.Interval
= Interval
t.Enabled
= True
End
Sub
Protected
Overrides Sub
OnPaintBackground(ByVal pevent
As PaintEventArgs)
'Don't
paint the background
End
Sub
Protected
Sub CreateSurfaces(ByVal
bm As Bitmap)
Dim
sd As New
SurfaceDescription
'Create
DirectX surfaces. First the primary surface.
If
_primary Is Nothing
Then
sd.SurfaceCaps.PrimarySurface
= True
_primary = New Surface(sd, _device)
End
If
'this
can be called if the control changes size so we always create a new backbuffer
If
Not _backBuffer Is
Nothing Then
_backBuffer.Dispose()
End
If
'now
the back buffer surface as an offscreen plain buffer built from the bitmap
handed in
sd.Clear()
sd.Width
= Me.Width
sd.Height
= Me.Height
sd.SurfaceCaps.OffScreenPlain
= True
_backBuffer
= New Surface(sd, _device)
If
Not _textBuffer Is
Nothing Then
_textBuffer.Dispose()
End
If
'now
the image surface from which the text is copied
sd.Clear()
sd.SurfaceCaps.OffScreenPlain
= True
sd.Height
= bm.Height
sd.Width
= bm.Width
_textBuffer
= New Surface(bm, sd, _device)
'Finally,
the clipper is set up to ensure the output is limited to the area of me control
If
Not _primaryClipper Is
Nothing Then
_primaryClipper.Dispose()
End
If
_primaryClipper
= New Clipper(_device)
_primaryClipper.Window
= Me
_primary.Clipper
= _primaryClipper
End
Sub
Private
_bm As Bitmap
Private
_textWidth As Integer
Private
_marqueeOffset As
Integer = 0
Protected
Sub CreateText()
If
Me.Height = 0 Or
Me.Width = 0 Then
Return
End
If
Font =
New Font(Font.FontFamily, 0.6F *
Me.Height)
Dim
g As Graphics = CreateGraphics()
g.TextRenderingHint
= TextRenderingHint.AntiAlias
Dim
sf As SizeF = g.MeasureString(Me.Text,
Me.Font, 2048, StringFormat.GenericTypographic)
_textWidth
= CInt(0.5F + sf.Width)
If
_textWidth = 0 Then
Return
End
If
If
Not _bm Is
Nothing Then
_bm.Dispose()
End
If
_bm =
New Bitmap(_textWidth,
Me.Height)
g =
Graphics.FromImage(_bm)
g.Clear(Me.BackColor)
g.TextRenderingHint
= TextRenderingHint.AntiAlias
Dim
sb As New
SolidBrush(Me.ForeColor)
g.DrawString(Text,
Font, sb, 0, 0, StringFormat.GenericTypographic)
sb.Dispose()
_marqueeOffset
= 0
Me.CreateSurfaces(_bm)
End
Sub
Protected
Overrides Sub
OnSizeChanged(ByVal e
As EventArgs)
Me.CreateText()
MyBase.OnSizeChanged(e)
End
Sub
Protected
Overrides Sub
OnTextChanged(ByVal e
As EventArgs)
Me.CreateText()
MyBase.OnTextChanged(e)
End
Sub
Protected
Overrides Sub
OnBackColorChanged(ByVal e
As EventArgs)
Me.CreateText()
MyBase.OnBackColorChanged(e)
End
Sub
Protected
Overrides Sub
OnFontChanged(ByVal e
As EventArgs)
Me.CreateText()
MyBase.OnFontChanged(e)
End
Sub
Protected
Overrides Sub
OnForeColorChanged(ByVal e
As EventArgs)
Me.CreateText()
MyBase.OnForeColorChanged(e)
End
Sub
Private
Sub t_Tick(ByVal
sender As Object,
ByVal e As
EventArgs)
_marqueeOffset
+= Nstep
If
_marqueeOffset > Me.Width + _textWidth
Then
_marqueeOffset
= 0
End
If
Invalidate()
End
Sub
Protected
Overrides Sub
OnPaint(ByVal e As
System.Windows.Forms.PaintEventArgs)
'the
back bufffer is copied to the primary surface.
If
_primary Is Nothing
Or Me._backBuffer
Is Nothing
Then
Return
End
If
'define
the destination rectangle
Dim
dest As New
Rectangle(Me.ClientSize.Width - _marqueeOffset,
0, Me._textWidth, Me.Height)
dest.Intersect(New
Rectangle(0, 0, Me.Width,
Me.Height))
'The
source is a moving window onto the pre-drawn bitmap.
'The
marquee starts from the right and scrolls left
'so
the offset is used to calculate the amount of text seen
Dim
src As New
Rectangle(0, 0, Math.Min(dest.Width, _textWidth), Me.Height)
If
_marqueeOffset > Me.Width
Then
src.Offset(Me._marqueeOffset
- Me.Width, 0)
End
If
src.Intersect(New
Rectangle(0, 0, _textWidth, Me.Height))
_backBuffer.ColorFill(Me.BackColor)
If
(src.Width <> 0 And dest.Width <> 0)
Then
_backBuffer.Draw(dest,
_textBuffer, src, DrawFlags.DoNotWait)
_primary.Draw(RectangleToScreen(Me.ClientRectangle),
_backBuffer, Me.ClientRectangle,
DrawFlags.DoNotWait)
End
If
End
Sub
End Class
End
Namespace
Hit you back button to return to the article.