Groups | Blog | Home
all groups > dotnet drawing api > may 2005 >

dotnet drawing api : Scrolling again


Bob Powell [MVP]
5/24/2005 12:00:00 AM
You're setting the location of the A object to the autoscroll positions
which are negative.

See my article on understanding auto-scroll in Windows Forms Tips and
Tricks.

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

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.





[quoted text, click to view]

James Westgate
5/24/2005 12:00:00 AM
Offset the element by the value in the Control.DisplayRectangle property
* -1.

This will move the element by the scroll offset, makign it appear
stationery.

Suddenly this question has come up a lot recently, and Im starting to wonder
why!

--
Create interactive diagrams and flowcharts with ERM Diagram at
http://www.crainiate.net

Take the ERM Tour at http://www.flowchartcontrol.com



[quoted text, click to view]

James Westgate
5/24/2005 12:00:00 AM
The problem is basically the panel control, you dont have fine enough
control over the painting and scrolling cycle. Create a control based on
scrollable control and do your painting in the OnPaint override. Then you
have much better control over what is drawn to the display. Place this
control on your form.

James

[quoted text, click to view]

Steve Magoon
5/24/2005 12:38:58 PM
Hey,

Could someone help me with the following code? I would like ObjectB to
remain stationary on the panel while scrolling ObjectA, but I can't figure
out how to do it. If I'm saving the Graphics state, it seems to me that any
scrolling should not affect ObjectB when I draw it, but only ObjectA. What
am I missing here or not understanding?

Thanks,

Steve

using System;

using System.Drawing;

using System.Drawing.Drawing2D;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

namespace Scroll2Objects

{

public class Form1 : System.Windows.Forms.Form

{

private System.Windows.Forms.Panel panel1;

private System.ComponentModel.Container components = null;

ObjectA A;

ObjectB B;

public Form1()

{

InitializeComponent();

A = new ObjectA();

B = new ObjectB();

}

protected override void Dispose( bool disposing )

{

if( disposing )

{

if (components != null)

{

components.Dispose();

}

}

base.Dispose( disposing );

}

#region Windows Form Designer generated code

private void InitializeComponent()

{

this.panel1 = new System.Windows.Forms.Panel();

this.SuspendLayout();

//

// panel1

//

this.panel1.AutoScroll = true;

this.panel1.AutoScrollMinSize = new System.Drawing.Size(400, 300);

this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;

this.panel1.Location = new System.Drawing.Point(64, 80);

this.panel1.Name = "panel1";

this.panel1.Size = new System.Drawing.Size(288, 168);

this.panel1.TabIndex = 0;

this.panel1.Paint += new
System.Windows.Forms.PaintEventHandler(this.panel1_Paint);

//

// Form1

//

this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);

this.ClientSize = new System.Drawing.Size(432, 350);

this.Controls.Add(this.panel1);

this.Name = "Form1";

this.Text = "Form1";

this.ResumeLayout(false);

}

#endregion

[STAThread]

static void Main()

{

Application.Run(new Form1());

}

private void panel1_Paint(object sender, System.Windows.Forms.PaintEventArgs
e)

{

B.DrawB(e.Graphics);

A.location = new Point(panel1.AutoScrollPosition.X,
panel1.AutoScrollPosition.Y);

A.DrawA(e.Graphics);

}

}

public class ObjectA

{

private Size _size;

public Size size

{

set

{

_size = value;

}

get

{

return _size;

}

}

private Point _location;

public Point location

{

set

{

_location = value;

}

get

{

return _location;

}

}

GraphicsState oldstate;

public void DrawA(Graphics g)

{

oldstate=g.Save();

Rectangle rect = new Rectangle(100 + this.location.X,100 +
this.location.Y,100,100);

using (Pen pen = new Pen(Color.Green, 1))

{

g.DrawRectangle(pen, rect);

}

g.Restore(oldstate);

}

}



public class ObjectB

{

private Size _size;

public Size size

{

set

{

_size = value;

}

get

{

return _size;

}

}

private Point _location;

public Point location

{

set

{

_location = value;

}

get

{

return _location;

}

}

GraphicsState oldstate;

public void DrawB(Graphics g)

{

oldstate=g.Save();

Rectangle rect = new Rectangle(50,50,20,20);

using (Pen pen = new Pen(Color.Red, 1))

{

g.DrawRectangle(pen, rect);

}

g.Restore(oldstate);

}

}

}

Steve Magoon
5/24/2005 1:16:02 PM

[quoted text, click to view]


But isn't that correct? In your article on auto-scroll, you recommend
setting the object with:

e.Graphics.DrawEllipse(Brushes.Red,10+AutoScrollPosition.X,10+AutoScrollPosition.Y,100,100).

So you offset the drawn object with the autoscroll posiition (a negative
number), thus making it appear to scroll, right?

If I want Object B to remain stationary, I shouldn't offset it at all,
right?

I guess I'm still not getting what you're trying to tell me.

Thanks,

Steve


Steve Magoon
5/24/2005 5:35:43 PM

[quoted text, click to view]

James,

Could you give me a few lines of code to help me see how to do this? I tried
your suggestion several ways and couldn't get it to work.

Thanks for your trouble,

Steve

Steve Magoon
5/24/2005 8:55:12 PM

[quoted text, click to view]


Thanks James. I created a custom control from ScrollableControl and
unfortunately, seemed to have the same problems. I can post that code if
that would help. Could you (or anyone?) give me a few lines of code that
would help me implement what I'm trying to accomplish (see previous posts in
this thread)?

Appreciate your help,

Steve


Bob Powell [MVP]
5/25/2005 12:00:00 AM
Steve,
You seem to be missing some fundamental concepts here. I'll try to explain.

The positioning of an object is not automatically managed by scrolling, your
code has to be aware of that and make adjustments accordingly. If you're
drawing things in a scrollable panel the way to go is to set up a transform
for the whole panel and draw your objects according to that transform. This
means that all objects are under control of the scrollbars and don't care if
they are scrolled or not, they just draw themselves...

Saving the Graphics state is something you should do if your objects alter
that state individually, such as if an object supports rotation or scaling.
You don't need to save the state if you never intend to alter the state in
the draw routine and doing so gets you no benefit.

If an object is maintained outside of the scrollable list, maybe as some
sort of floating indicator, then it's probably better to consider it as
something apart from the list of scrollable objects and deal with it on its
own. Objects such as this may also better qualify for being created as
floating windows. This is particularly true for auto scrolled windows
because they set a clipping region that only covers the invalid part of the
window, therefore other objects are often not drawn at-all.

To manage scrolling, AutoScroll can be used to create the transform such
as...

Matrix mx=new Matrix(1,0,0,1,this.AutoScrollPosition.X,
this.AutoScrollPosition.Y);
e.Graphics.Transform=mx;

Now, drawing a rectangle at 10,10,100,75 will draw a rectangle that tracks
the scroll bars and moves with the viewpoint.

If you want an object that doesn't move in relation to the scrollbars you
can set up it's own transform such as...
public void DrawB(Graphics g)

{

GraphicsState state=g.Save();

g.Transform=new Matrix(1,0,0,1,0,0); // an identity matrix will stop it from
scrolling with the window

Rectangle rect = new Rectangle(50,50,20,20);

using (Pen pen = new Pen(Color.Red, 1))

{

g.DrawRectangle(pen, rect);

}

g.Restore(state);

}

However, because the panel only invalidates the bit that's just scrolled
into view, this never gets drawn unless the whole window is refreshed making
it look as though the item isn't drawn at-all. This means that a floating
window or a draw cycle that you manage totally is needed. For the latter you
need to be deriving your own control and not relying on the Panels Paint
event.

After my signature I include the modified version of your old code. Note how
the panel window is smaller. this lets you see that the red square is
actually drawn. It doesen't however fix your problem which, as I say
requires a more complex solution.


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

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.


using System;

using System.Drawing;

using System.Drawing.Drawing2D;

using System.Collections;

using System.ComponentModel;

using System.Windows.Forms;

using System.Data;

namespace Scroll2Objects

{

public class Form1 : System.Windows.Forms.Form

{

private System.Windows.Forms.Panel panel1;

private System.ComponentModel.Container components = null;

ObjectA A;

ObjectB B;

public Form1()

{

InitializeComponent();

A = new ObjectA();

B = new ObjectB();

}

protected override void Dispose( bool disposing )

{

if( disposing )

{

if (components != null)

{

components.Dispose();

}

}

base.Dispose( disposing );

}

#region Windows Form Designer generated code

private void InitializeComponent()

{

this.panel1 = new System.Windows.Forms.Panel();

this.SuspendLayout();

//

// panel1

//

this.panel1.AutoScroll = true;

this.panel1.AutoScrollMinSize = new System.Drawing.Size(400, 300);

this.panel1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;

this.panel1.Location = new System.Drawing.Point(208, 120);

this.panel1.Name = "panel1";

this.panel1.Size = new System.Drawing.Size(136, 128);

this.panel1.TabIndex = 0;

this.panel1.Paint += new
System.Windows.Forms.PaintEventHandler(this.panel1_Paint);

//

// Form1

//

this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);

this.ClientSize = new System.Drawing.Size(432, 350);

this.Controls.Add(this.panel1);

this.Name = "Form1";

this.Text = "Form1";

this.ResumeLayout(false);

}

#endregion

[STAThread]

static void Main()

{

Application.Run(new Form1());

}

private void panel1_Paint(object sender, System.Windows.Forms.PaintEventArgs
e)

{

Matrix mx=new
Matrix(1,0,0,1,this.panel1.AutoScrollPosition.X,this.panel1.AutoScrollPosition.Y);

e.Graphics.Transform=mx;

A.DrawA(e.Graphics); // A draws according to the current transform so will
track with the scrollbars

B.DrawB(e.Graphics); // B sets up it's own transform and so does not...

}

}

public class ObjectA

{

private Size _size;

public Size size

{

set

{

_size = value;

}

get

{

return _size;

}

}

private Point _location;

public Point location

{

set

{

_location = value;

}

get

{

return _location;

}

}

//GraphicsState oldstate;

public void DrawA(Graphics g)

{

// chopped out by bob oldstate=g.Save();

Rectangle rect = new Rectangle(100 ,100 ,100 ,100);

using (Pen pen = new Pen(Color.Green, 1))

{

g.DrawRectangle(pen, rect);

}

// chopped out by bob g.Restore(oldstate);

}

}





public class ObjectB

{

private Size _size;

public Size size

{

set

{

_size = value;

}

get

{

return _size;

}

}

private Point _location;

public Point location

{

set

{

_location = value;

}

get

{

return _location;

}

}



public void DrawB(Graphics g)

{

GraphicsState state=g.Save();

g.Transform=new Matrix(1,0,0,1,0,0); // an identity matrix will stop it from
scrolling with the window

Rectangle rect = new Rectangle(50,50,20,20);

using (Pen pen = new Pen(Color.Red, 1))

{

g.DrawRectangle(pen, rect);

}

g.Restore(state);

}

}

}



[quoted text, click to view]
Bob Powell [MVP]
5/25/2005 12:00:00 AM
If you derived your own control you could catch the scroll events and
invalidate the whole window... that'd do the trick.

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

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.





[quoted text, click to view]

Steve Magoon
5/25/2005 12:00:00 AM

[quoted text, click to view]

Thanks Bob!

AddThis Social Bookmark Button