How To center a logical drawing space much bigger than your screen?

R

raylopez99

I have a logical drawing space much bigger than the viewport (the
screen) and I'd like to center the viewport (the screen) to be at the
center of the logical drawing space. After following the excellent
transforms specified on Bob Powell's site, I still wonder if there's
an easier way of centering it than the following procedure?

Here is what I do now (it's awkward but it works):

1) I follow the transforms specified on Bob Powell's site, which are
required to get the scroll bars to work correctly, and the mouse
coordinates to work with the viewport (screen) and also to zoom.

2) to get the scrollbars to zoom to the very end of the logical
drawing space, I create a series of three tiny buttons (size (1,1) and
invisible) in the constructor of the form (make sure they are declared
before InitializeComponent(); in your partial class or you might get a
runtime error): button 1 = Top left corner of logical drawing space;
button 2 = bottom right corner, and button 3 = midway of your logical
drawing space, which you have to know the dimensions of your logical
drawing space in pixels ahead of time. I also adjusted the button 3 a
bit using some logic to center it better, especially if you are, as
here, using a split panel.

3) In the constructor, and ONLY in the constructor (I have no idea
why), you do this:

button3Center = new Point(xCenterofBoard, yCenterofBoard); //calculate
center of board from your logical drawing space ahead of time
button1= new Point(0,0);
button1= new Size(1,1);
//etc for button2...

this.mySplitContainer1.Panel2.Controls.Add(this.button1);
this.splitContainer1.Panel2.Controls.Add(this.button3Center;)
this.splitContainer1.Panel2.Controls.Add(this.button2);

//need next two if statements together to ensure stable
behavior
//you can also add the below if statements when resizing, in a On
Resize handler, but only the two below if statements, not any of the
above

if (button1.CanFocus)
{
button1.Focus();
}

if (button3Center.CanFocus)
{
button3Center.Focus();
}
//

//now, the form when it loads up will have the logical drawing space
that is near button3Center show up on the screen. This way the
viewport (screen) is 'centered' at around the middle or center of the
logical drawing space.

For some reason you cannot put this logic anywhere else (like Resize
or Scroll). Further, you cannot set any 'anchor' properties for your
three buttons, such as: button1.Anchor =
System.Windows.Forms.AnchorStyles.None; // this will mess up your
centering.

Anyway, the above works fine, though I'd like to know if somebody has
a cleaner way. I tried playing around in the OnPaint handler with
Matrix mx = new Matrix (1,0,0,0,clientsize.Width/2, clientsize.Height/
2); and variations (such as making clientsize negative, which had
promise as it shifted the logical drawing space but I could not quite
get the mouse coordinates to work, see Bob Powell's site on this).

RL
 
R

raylopez99

button3Center = new Point(xCenterofBoard, yCenterofBoard); //calculate
center of board from your logical drawing space ahead of time
button1= new Point(0,0);
button1= new Size(1,1);
//etc for button2...

Small correction: above should read:

button3Center.location = new Point(xCenterofBoard, yCenterofBoard);
button1.location = new Point(0,0);

//etc

RL
 
R

raylopez99

Further, if you have several resolutions, such as with Zoom (i.e. 50%,
75%, 100%, 150%), you have to set up a complicated Case statement and
setup buttons for each such resolution every time you switch
resolution, using :
this.mysplitContainer.Panel`.Controls.Add(Button1);
and .Controls.Remove(...), for each pairs of these buttons.

There has to be a better way of doing this I would imagine.

RL
 
B

Bob Powell [MVP]

You're working too hard..
What is that with the buttons all about? I have no clue what you're
explaining...

How can percentages require a complicated case statement?? you divide by 100
and multiply by whatever percentage you like...

See after my signature for some inspiration...


--
Bob Powell [MVP]
Visual C#, System.Drawing

Ramuseco Limited .NET consulting
http://www.ramuseco.com

Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm

All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.





/// <summary>
/// The canvas class
/// </summary>
[
ToolboxItem(true),
ToolboxBitmap(typeof(Canvas),"CanvasIcon.bmp")
]
public class Canvas : ScrollableControl
{

/// <summary>
/// Constructor
/// </summary>
public Canvas()
{
this.SetStyle(
ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint |
ControlStyles.ResizeRedraw ,true);
}

private bool _doubleBuffer;

[
Category("Behavior"),
Description("Set true to enable double buffering")
]
public bool DoubleBuffer
{
get{return _doubleBuffer;}
set
{
_doubleBuffer=value;
if(value)
{
SetStyle(ControlStyles.DoubleBuffer,true);
}
else
{
SetStyle(ControlStyles.DoubleBuffer,false);
}
Invalidate();
}
}

private Color _pageColor=Color.White;

[
Category("Appearance"),
Description("The base color of the page")
]
public Color PageColor
{
get{return _pageColor;}
set
{
_pageColor=value;
Invalidate();
}
}

private bool _clipToPage;

[
Category("Behavior"),
Description("Gets or sets the clipping flag. When true no drawing is
allowed outside page boundaries")
]
public bool ClipToPage
{
get{return _clipToPage;}
set
{
_clipToPage=value;
Invalidate();
}
}

private Size _pageSize=new Size(640,480);

[
Category("Appearance"),
Description("The size of the virtual page")
]
public Size PageSize
{
get{return _pageSize;}
set
{
_pageSize=value;
OnPageSizeChanged(EventArgs.Empty);
}
}

protected virtual void OnPageSizeChanged(EventArgs e)
{
CalcScroll();
if(PageSizeChanged!=null)
PageSizeChanged(this,e);
}

public event EventHandler PageSizeChanged;

private float _zoom=1.0f;

[Category("Behavior"),
Description("The multiplying factor for the zoom level"),
DefaultValue(1.0f)]
public float Zoom
{
get{return _zoom;}
set{
_zoom = value;
//A zoom may not be negative
if(_zoom<0)
_zoom=Math.Abs(_zoom);
//a zoom may be very small but never zero.
if(_zoom==0)
_zoom=0.000001f;
//The scrollbars should be recalculated
CalcScroll();
//and the host application code informed if needs be
OnZoomChanged(EventArgs.Empty);
}
}

void CalcScroll()
{
Size cs = new
Size((int)(this._pageSize.Width*_zoom),(int)(this._pageSize.Height*_zoom));
this.AutoScrollMinSize=cs;
Invalidate();
}

protected override void OnSizeChanged(EventArgs e)
{
CalcScroll();
base.OnSizeChanged(e);
}

protected override void OnPaint(PaintEventArgs e)
{
base.OnPaintBackground(e);

Matrix mx=new Matrix(_zoom,0,0,_zoom,0,0);

Size s=new
Size((int)(this.ClientSize.Width*(1f/_zoom)),(int)(this.ClientSize.Height*(1f/_zoom)));

if(s.Width>PageSize.Width)
mx.Translate((s.Width/2)-(_pageSize.Width/2),0);
else
mx.Translate((float)this.AutoScrollPosition.X*(1f/_zoom),0);

if(s.Height>PageSize.Height)
mx.Translate(0,(s.Height/2)-(this._pageSize.Height/2)+(this.AutoScrollPosition.Y));
else
mx.Translate(0,(float)this.AutoScrollPosition.Y*(1f/_zoom));

e.Graphics.Transform=mx;

SolidBrush b=new SolidBrush(Color.FromArgb(64,Color.Black));
e.Graphics.FillRectangle(b,new Rectangle(new Point(20,20),PageSize));
b.Color=PageColor;
e.Graphics.FillRectangle(b,new Rectangle(new Point(0,0),PageSize));

if(ClipToPage)
e.Graphics.SetClip(new Rectangle(0,0,PageSize.Width,PageSize.Height));

base.OnPaint(e);

}

public event EventHandler ZoomChanged;

protected virtual void OnZoomChanged(EventArgs e)
{
if(this.ZoomChanged!=null)
ZoomChanged(this,e);
Invalidate();
}

protected virtual MouseEventArgs BacktrackMouse(MouseEventArgs e)
{
Matrix mx=new Matrix(_zoom,0,0,_zoom,0,0);

Size s=new Size( (int)(this.ClientSize.Width*(1f/_zoom)),
(int)(this.ClientSize.Height*(1f/_zoom)));

if(s.Width>PageSize.Width)
mx.Translate((s.Width/2)-(_pageSize.Width/2),0);
else
mx.Translate((float)this.AutoScrollPosition.X*(1f/_zoom),0);

if(s.Height>PageSize.Height)
mx.Translate(0,(s.Height/2)-(this._pageSize.Height/2)+
(this.AutoScrollPosition.Y));
else
mx.Translate(0,(float)this.AutoScrollPosition.Y*(1f/_zoom));

mx.Invert();

Point[] px=new Point[]{new Point(e.X,e.Y)};

mx.TransformPoints(px);

MouseEventArgs et=new
MouseEventArgs(e.Button,e.Clicks,px[0].X,px[0].Y,e.Delta);

return et;
}

protected override void OnMouseMove(MouseEventArgs e)
{
MouseEventArgs et=this.BacktrackMouse(e);
base.OnMouseMove(et);
}

protected override void OnMouseUp(MouseEventArgs e)
{
MouseEventArgs et=this.BacktrackMouse(e);
base.OnMouseUp(et);
}

protected override void OnMouseDown(MouseEventArgs e)
{
MouseEventArgs et=this.BacktrackMouse(e);
base.OnMouseDown(et);
}
}
 
R

raylopez99

You're working too hard..
What is that with the buttons all about? I have no clue what you're
explaining...

How can percentages require a complicated case statement?? you divide by 100
and multiply by whatever percentage you like...

See after my signature for some inspiration...


WOW! Bob Powell has emailed me! This is cool. Bob you are a real
inspiration and my program would not work without your BacktrackMouse
method, which I got from your site. I notice in your example above
(which I will compile and examine later) it's also present.

WHat I notice (just looking at your code) is that the OnPaint and
Mouse handlers have the same matrix transform, which is great, and
same as in my program. You have to sync screen and mouse.

What I do, which you also do with different and better logic, is to
try and get the scroll bars to work to scroll past my drawing object:
a big rectangle. I have a huge rectangle that is bigger than the
screen. How to get to the bottom of this rect? Well as you know if
you just paint it, it will be truncated (only the upper left corner
will be shown). So to get to the lower right corner, I do the
following: place a small invisible button (create it, add it) at the
lower right corner. This works. But, if you change the _zoom factor
(I have a series of set zooms, e.g. a normal, a small, a large, extra
large, etc), you must .remove this button and .add it again to the new
rect., otherwise you cannot scroll to the lower right corner of the
new rectangle.

It's complicated (involving numerous enums and bit flags) but it
works. I can add any number of fixed (set) resolutions this way:
i.e., 25% of normal, 75% of normal, 100% (default) and 200%, etc, but
I cannot do a "variable" resolution using a slider bar for example--is
this what your example shows? If so, your way is clearly superior.

Any words appreciated. Thanks again Bob! I think MSDN should pay you
money for providing your service--it's really helpful and MSDN had
nothing on Matrices (as easy to understand as yours, or I could not
find it).

RL

PS--besides getting to the bottom of the rectangle I also want the
viewing area (viewport) to be centered at the center of the
rectangle. I do this by adding yet another small invisible button to
the center of the rectangle, using .add, .remove as necessary,
everytime the resolution is changed. If you have any better way of
doing this please let me know. Thanks again!

I plan on looking at your code but feel free to comment anyway--just a
few words of encouragement is all I need.
 
B

Bob Powell [MVP]

If you look at the canvas code I sent you, the control is quite happy to
deal with large images.

The scroll bars are set using the info from my "understanding autoscroll"
article.

Centering on the screen is easy. Just set the origin of the picture to:

center of screen - width/height of picture /2


--
--
Bob Powell [MVP]
Visual C#, System.Drawing

Ramuseco Limited .NET consulting
http://www.ramuseco.com

Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm

All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.


You're working too hard..
What is that with the buttons all about? I have no clue what you're
explaining...

How can percentages require a complicated case statement?? you divide by
100
and multiply by whatever percentage you like...

See after my signature for some inspiration...


WOW! Bob Powell has emailed me! This is cool. Bob you are a real
inspiration and my program would not work without your BacktrackMouse
method, which I got from your site. I notice in your example above
(which I will compile and examine later) it's also present.

WHat I notice (just looking at your code) is that the OnPaint and
Mouse handlers have the same matrix transform, which is great, and
same as in my program. You have to sync screen and mouse.

What I do, which you also do with different and better logic, is to
try and get the scroll bars to work to scroll past my drawing object:
a big rectangle. I have a huge rectangle that is bigger than the
screen. How to get to the bottom of this rect? Well as you know if
you just paint it, it will be truncated (only the upper left corner
will be shown). So to get to the lower right corner, I do the
following: place a small invisible button (create it, add it) at the
lower right corner. This works. But, if you change the _zoom factor
(I have a series of set zooms, e.g. a normal, a small, a large, extra
large, etc), you must .remove this button and .add it again to the new
rect., otherwise you cannot scroll to the lower right corner of the
new rectangle.

It's complicated (involving numerous enums and bit flags) but it
works. I can add any number of fixed (set) resolutions this way:
i.e., 25% of normal, 75% of normal, 100% (default) and 200%, etc, but
I cannot do a "variable" resolution using a slider bar for example--is
this what your example shows? If so, your way is clearly superior.

Any words appreciated. Thanks again Bob! I think MSDN should pay you
money for providing your service--it's really helpful and MSDN had
nothing on Matrices (as easy to understand as yours, or I could not
find it).

RL

PS--besides getting to the bottom of the rectangle I also want the
viewing area (viewport) to be centered at the center of the
rectangle. I do this by adding yet another small invisible button to
the center of the rectangle, using .add, .remove as necessary,
everytime the resolution is changed. If you have any better way of
doing this please let me know. Thanks again!

I plan on looking at your code but feel free to comment anyway--just a
few words of encouragement is all I need.
 
R

raylopez99

Bob,

Thanks again. I tried to compile your code in your OP this morning,
and though it compiled I could not get it to work as a 'form' (I tried
making it a form and could not get it to load as a form, after trying
for example Canvas myCanvasForm = new Canvas();. It might be missing
some resource files or something (I notice it references some image
file).

Anyway, it did compile, and I printed it out and will go through it.
Any further comments appreciated. I really do need one complete
example for my library (for future reference--right now my kludge way
of doing things 'works' and that's 'fine for now').

Cheers from a time zone about -7 hours behind yours...

RL
 
R

raylopez99

If you look at the canvas code I sent you, the control is quite happy to
deal with large images.

No, it did not work, though I finally got it to work past just
compilation. Reproduced below. It did give a nice white screen with
a grey border that can be resized to any arbitrary size, using the
_zoom, but if you set _zoom to a very large number, say 2.0F, then no
scroll bars show up (despite, using the Designer Wizard, I did check
the scrollbars property to True).
The scroll bars are set using the info from my "understanding autoscroll"
article.

Is that in your canvas code?
Centering on the screen is easy. Just set the origin of the picture to:

center of screen - width/height of picture /2

--

Where do I add this? In what function call / event handler?


Con permission, I have a few more optional questions (you can feel
free to answer as many or few as you please; I appreciate your time):

1) below is my code. I notice that the Color.White background seems
to not allow a very bright overlay of say a red rectangle (see the
rectangle I draw below). It comes out (or so it seems) a bit pink,
like the white background is bleeding through.

2) How to use scrollbars, as I mentioned above. AutoScrollPosition is
shown throughout the code, but, unlike my working example in the
original thread (which was inspired by your website examples), the one
that uses hidden tiny buttons, this time the form doesn't scroll. If
you can point out in your code where this feature is, maybe I can
check that I didn't copy and paste wrong--since to make the program
work past the compile stage I had to create a form from scratch and
copy and paste selectively, until everything (I hope) was copied from
your code.

3). Minor but important for me: this line made things work without
the invalidation problem: "this.SetStyle
( ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |
ControlStyles.ResizeRedraw ,true); "
How to set this using the Wizard (I like to use the wizard whenever
possible)?

Thank you.

I like your "canvas" example for my library because, though the
scrollbars don't work, for _zoom < 1.0F you can resize the window by
grapping a corner, and the white canvas with grey border will
automatically stay in the middle of the resized window, and, of
course, the BackTrackMouse function and Matrix will correctly adjust
your mouse coordinates (I added a function for right-button down to
test this, as you can see below). Very nice, but I do like the idea of
scrollbars and want to add them.

RL

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace Canvas2
{
public partial class Canvas : Form
{
private Size _pageSize; // = new Size(640, 480);
private bool _clipToPage;
private float _zoom;
private Color _pageColor; // = Color.White;

public Canvas()
{
InitializeComponent();
_pageSize = new Size(640, 480);
_zoom = 0.5F;
_pageColor = Color.White; //Color.Transparent //
Color.Empty; //do not work, give dark background, use Color.White
//

this.SetStyle( ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint | ControlStyles.ResizeRedraw ,true);

}

// private Color _pageColor = Color.White;
[
Category("Appearance"),
Description("The base color of the page")
]
public Color PageColor
{
get { return _pageColor; }
set
{
_pageColor = value;
Invalidate();
}
}



// private bool _clipToPage;
[
Category("Behavior"),
Description("Gets or sets the clipping flag. When true no
drawing is allowed outside page boundaries")
]
public bool ClipToPage
{
get { return _clipToPage; }
set
{
_clipToPage = value;
Invalidate();
}
}
// private Size _pageSize = new Size(640, 480);
[
Category("Appearance"),
Description("The size of the virtual page")
]
public Size PageSize
{
get { return _pageSize; }
set
{
_pageSize = value;
OnPageSizeChanged(EventArgs.Empty);
}
}

protected virtual void OnPageSizeChanged(EventArgs e)
{
CalcScroll();
if (PageSizeChanged != null)
PageSizeChanged(this, e);
}
public event EventHandler PageSizeChanged;

void CalcScroll()
{
Size cs = new Size((int)(this._pageSize.Width * _zoom),
(int)(this._pageSize.Height * _zoom));
this.AutoScrollMinSize = cs;
Invalidate();
}

[Category("Behavior"),
Description("The multiplying factor for the zoom level"),
DefaultValue(1.0f)]
public float Zoom
{
get { return _zoom; }
set
{
_zoom = value;
//A zoom may not be negative
if (_zoom < 0)
_zoom = Math.Abs(_zoom);
//a zoom may be very small but never zero.
if (_zoom == 0)
_zoom = 0.000001f;
//The scrollbars should be recalculated
CalcScroll();
//and the host application code informed if needs be
OnZoomChanged(EventArgs.Empty);
}
}

public event EventHandler ZoomChanged;
protected virtual void OnZoomChanged(EventArgs e)
{
if (this.ZoomChanged != null)
ZoomChanged(this, e);
Invalidate();
}

protected override void OnPaint(PaintEventArgs e)
{
base.OnPaintBackground(e);

Matrix mx = new Matrix(_zoom, 0, 0, _zoom, 0, 0);
Size s = new Size((int)(this.ClientSize.Width * (1f /
_zoom)), (int)(this.ClientSize.Height * (1f / _zoom)));
if (s.Width > PageSize.Width)
mx.Translate((s.Width / 2) - (_pageSize.Width / 2),
0);
else
mx.Translate((float)this.AutoScrollPosition.X * (1f /
_zoom), 0);
if (s.Height > PageSize.Height)
mx.Translate(0, (s.Height / 2) -
(this._pageSize.Height / 2) + (this.AutoScrollPosition.Y));
else
mx.Translate(0, (float)this.AutoScrollPosition.Y *
(1f / _zoom));
e.Graphics.Transform = mx;
SolidBrush b = new SolidBrush(Color.FromArgb(64,
Color.Black));
e.Graphics.FillRectangle(b, new Rectangle(new Point(20,
20), PageSize));
b.Color = PageColor;
e.Graphics.FillRectangle(b, new Rectangle(new Point(0, 0),
PageSize));
if (ClipToPage)
e.Graphics.SetClip(new Rectangle(0, 0, PageSize.Width,
PageSize.Height));

//SolidBrush b1 = new SolidBrush(Color.FromArgb(64,
Color.Red));
//e.Graphics.FillRectangle(b1, new Rectangle(new Point(75,
75), new Size(20, 30)));

base.OnPaint(e);
}


protected virtual MouseEventArgs BacktrackMouse(MouseEventArgs e)
{
Matrix mx=new Matrix(_zoom,0,0,_zoom,0,0);
Size s=new Size( (int)(this.ClientSize.Width*(1f/_zoom)),
(int)(this.ClientSize.Height*(1f/_zoom)));
if(s.Width>PageSize.Width)
mx.Translate((s.Width/2)-(_pageSize.Width/2),0);
else
mx.Translate((float)this.AutoScrollPosition.X*(1f/_zoom),0);
if(s.Height>PageSize.Height)
mx.Translate(0,(s.Height/2)-(this._pageSize.Height/2)+
(this.AutoScrollPosition.Y));
else
mx.Translate(0,(float)this.AutoScrollPosition.Y*(1f/_zoom));
mx.Invert();
Point[] px=new Point[]{new Point(e.X,e.Y)};
mx.TransformPoints(px);
MouseEventArgs et=new
MouseEventArgs(e.Button,e.Clicks,px[0].X,px[0].Y,e.Delta);
return et;
}
protected override void OnMouseMove(MouseEventArgs e)
{
MouseEventArgs et=this.BacktrackMouse(e);
base.OnMouseMove(et);
}
protected override void OnMouseUp(MouseEventArgs e)
{
MouseEventArgs et=this.BacktrackMouse(e);
base.OnMouseUp(et);
}
protected override void OnMouseDown(MouseEventArgs e)
{
MouseEventArgs et=this.BacktrackMouse(e);
base.OnMouseDown(et);
}

private void Canvas_Paint(object sender, PaintEventArgs e)
{
SolidBrush b1 = new SolidBrush(Color.FromArgb(64, Color.Red));
e.Graphics.FillRectangle(b1, new Rectangle(new Point(75, 75),
new Size(20, 30)));
}

private void Canvas_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
MessageBox.Show("pt.X,Y = " + e.X.ToString() + "," +
e.Y.ToString());
}
}
}


}
 
B

Bob Powell [MVP]

Hi Ray,
Actually the canvas should be based on Control and dragged onto the form
from the toolbox..

The code I sent should have built except for perhaps the icon which is easy
enough to remove.

The article on autoscroll is on my site.

--
--
Bob Powell [MVP]
Visual C#, System.Drawing

Ramuseco Limited .NET consulting
http://www.ramuseco.com

Find great Windows Forms articles in Windows Forms Tips and Tricks
http://www.bobpowell.net/tipstricks.htm

Answer those GDI+ questions with the GDI+ FAQ
http://www.bobpowell.net/faqmain.htm

All new articles provide code in C# and VB.NET.
Subscribe to the RSS feeds provided and never miss a new article.


If you look at the canvas code I sent you, the control is quite happy to
deal with large images.

No, it did not work, though I finally got it to work past just
compilation. Reproduced below. It did give a nice white screen with
a grey border that can be resized to any arbitrary size, using the
_zoom, but if you set _zoom to a very large number, say 2.0F, then no
scroll bars show up (despite, using the Designer Wizard, I did check
the scrollbars property to True).
The scroll bars are set using the info from my "understanding autoscroll"
article.

Is that in your canvas code?
Centering on the screen is easy. Just set the origin of the picture to:

center of screen - width/height of picture /2

--

Where do I add this? In what function call / event handler?


Con permission, I have a few more optional questions (you can feel
free to answer as many or few as you please; I appreciate your time):

1) below is my code. I notice that the Color.White background seems
to not allow a very bright overlay of say a red rectangle (see the
rectangle I draw below). It comes out (or so it seems) a bit pink,
like the white background is bleeding through.

2) How to use scrollbars, as I mentioned above. AutoScrollPosition is
shown throughout the code, but, unlike my working example in the
original thread (which was inspired by your website examples), the one
that uses hidden tiny buttons, this time the form doesn't scroll. If
you can point out in your code where this feature is, maybe I can
check that I didn't copy and paste wrong--since to make the program
work past the compile stage I had to create a form from scratch and
copy and paste selectively, until everything (I hope) was copied from
your code.

3). Minor but important for me: this line made things work without
the invalidation problem: "this.SetStyle
( ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint |
ControlStyles.ResizeRedraw ,true); "
How to set this using the Wizard (I like to use the wizard whenever
possible)?

Thank you.

I like your "canvas" example for my library because, though the
scrollbars don't work, for _zoom < 1.0F you can resize the window by
grapping a corner, and the white canvas with grey border will
automatically stay in the middle of the resized window, and, of
course, the BackTrackMouse function and Matrix will correctly adjust
your mouse coordinates (I added a function for right-button down to
test this, as you can see below). Very nice, but I do like the idea of
scrollbars and want to add them.

RL

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;

namespace Canvas2
{
public partial class Canvas : Form
{
private Size _pageSize; // = new Size(640, 480);
private bool _clipToPage;
private float _zoom;
private Color _pageColor; // = Color.White;

public Canvas()
{
InitializeComponent();
_pageSize = new Size(640, 480);
_zoom = 0.5F;
_pageColor = Color.White; //Color.Transparent //
Color.Empty; //do not work, give dark background, use Color.White
//

this.SetStyle( ControlStyles.AllPaintingInWmPaint |
ControlStyles.UserPaint | ControlStyles.ResizeRedraw ,true);

}

// private Color _pageColor = Color.White;
[
Category("Appearance"),
Description("The base color of the page")
]
public Color PageColor
{
get { return _pageColor; }
set
{
_pageColor = value;
Invalidate();
}
}



// private bool _clipToPage;
[
Category("Behavior"),
Description("Gets or sets the clipping flag. When true no
drawing is allowed outside page boundaries")
]
public bool ClipToPage
{
get { return _clipToPage; }
set
{
_clipToPage = value;
Invalidate();
}
}
// private Size _pageSize = new Size(640, 480);
[
Category("Appearance"),
Description("The size of the virtual page")
]
public Size PageSize
{
get { return _pageSize; }
set
{
_pageSize = value;
OnPageSizeChanged(EventArgs.Empty);
}
}

protected virtual void OnPageSizeChanged(EventArgs e)
{
CalcScroll();
if (PageSizeChanged != null)
PageSizeChanged(this, e);
}
public event EventHandler PageSizeChanged;

void CalcScroll()
{
Size cs = new Size((int)(this._pageSize.Width * _zoom),
(int)(this._pageSize.Height * _zoom));
this.AutoScrollMinSize = cs;
Invalidate();
}

[Category("Behavior"),
Description("The multiplying factor for the zoom level"),
DefaultValue(1.0f)]
public float Zoom
{
get { return _zoom; }
set
{
_zoom = value;
//A zoom may not be negative
if (_zoom < 0)
_zoom = Math.Abs(_zoom);
//a zoom may be very small but never zero.
if (_zoom == 0)
_zoom = 0.000001f;
//The scrollbars should be recalculated
CalcScroll();
//and the host application code informed if needs be
OnZoomChanged(EventArgs.Empty);
}
}

public event EventHandler ZoomChanged;
protected virtual void OnZoomChanged(EventArgs e)
{
if (this.ZoomChanged != null)
ZoomChanged(this, e);
Invalidate();
}

protected override void OnPaint(PaintEventArgs e)
{
base.OnPaintBackground(e);

Matrix mx = new Matrix(_zoom, 0, 0, _zoom, 0, 0);
Size s = new Size((int)(this.ClientSize.Width * (1f /
_zoom)), (int)(this.ClientSize.Height * (1f / _zoom)));
if (s.Width > PageSize.Width)
mx.Translate((s.Width / 2) - (_pageSize.Width / 2),
0);
else
mx.Translate((float)this.AutoScrollPosition.X * (1f /
_zoom), 0);
if (s.Height > PageSize.Height)
mx.Translate(0, (s.Height / 2) -
(this._pageSize.Height / 2) + (this.AutoScrollPosition.Y));
else
mx.Translate(0, (float)this.AutoScrollPosition.Y *
(1f / _zoom));
e.Graphics.Transform = mx;
SolidBrush b = new SolidBrush(Color.FromArgb(64,
Color.Black));
e.Graphics.FillRectangle(b, new Rectangle(new Point(20,
20), PageSize));
b.Color = PageColor;
e.Graphics.FillRectangle(b, new Rectangle(new Point(0, 0),
PageSize));
if (ClipToPage)
e.Graphics.SetClip(new Rectangle(0, 0, PageSize.Width,
PageSize.Height));

//SolidBrush b1 = new SolidBrush(Color.FromArgb(64,
Color.Red));
//e.Graphics.FillRectangle(b1, new Rectangle(new Point(75,
75), new Size(20, 30)));

base.OnPaint(e);
}


protected virtual MouseEventArgs BacktrackMouse(MouseEventArgs e)
{
Matrix mx=new Matrix(_zoom,0,0,_zoom,0,0);
Size s=new Size( (int)(this.ClientSize.Width*(1f/_zoom)),
(int)(this.ClientSize.Height*(1f/_zoom)));
if(s.Width>PageSize.Width)
mx.Translate((s.Width/2)-(_pageSize.Width/2),0);
else
mx.Translate((float)this.AutoScrollPosition.X*(1f/_zoom),0);
if(s.Height>PageSize.Height)
mx.Translate(0,(s.Height/2)-(this._pageSize.Height/2)+
(this.AutoScrollPosition.Y));
else
mx.Translate(0,(float)this.AutoScrollPosition.Y*(1f/_zoom));
mx.Invert();
Point[] px=new Point[]{new Point(e.X,e.Y)};
mx.TransformPoints(px);
MouseEventArgs et=new
MouseEventArgs(e.Button,e.Clicks,px[0].X,px[0].Y,e.Delta);
return et;
}
protected override void OnMouseMove(MouseEventArgs e)
{
MouseEventArgs et=this.BacktrackMouse(e);
base.OnMouseMove(et);
}
protected override void OnMouseUp(MouseEventArgs e)
{
MouseEventArgs et=this.BacktrackMouse(e);
base.OnMouseUp(et);
}
protected override void OnMouseDown(MouseEventArgs e)
{
MouseEventArgs et=this.BacktrackMouse(e);
base.OnMouseDown(et);
}

private void Canvas_Paint(object sender, PaintEventArgs e)
{
SolidBrush b1 = new SolidBrush(Color.FromArgb(64, Color.Red));
e.Graphics.FillRectangle(b1, new Rectangle(new Point(75, 75),
new Size(20, 30)));
}

private void Canvas_MouseUp(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Right)
{
MessageBox.Show("pt.X,Y = " + e.X.ToString() + "," +
e.Y.ToString());
}
}
}


}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top