开发者

C#. Displaying and moving icons on a windows form

I need to display a set of Icons on a windows form and move them around at run-time. I have used the PictureBox control to disp开发者_如何学运维lay the icons but I would have to either not change the picturebox.SizeMode to Stretched, which results in very small icons, Or they would look blurred and horrible at run-time even though they are high quality icons.

I've googled intensively for the solution to no avail although many people had the same problem. Do you have a solution?

If not, how would YOU go about displaying icons on a windows form so that they can be moved around easily during run-time?

Thanks for sharing your time and knowledge.

EDIT Here are some screen-shots: Upper one is the PictureBox in design-time and lower run-time

alt text http://www.imagechicken.com/uploads/1266687694012129500.png

alt text http://www.imagechicken.com/uploads/1266687487098617800.png


I would use bitmaps instead of icons, as others have said. Here is how I would do it:

First off, create a class which holds the bitmap and a delegate which will point to a method which will get the position of the bitmap as a function of time.

delegate Point PositionFunction(int time);

class MovingBitmap
{
    private Bitmap bitmap;
    private PositionFunction positionFunction;

    public MovingBitmap(Bitmap bitmap, PositionFunction positionFunction)
    {
        if (bitmap == null)
        {
            throw new ArgumentNullException("bitmap");
        }
        else if (positionFunction == null)
        {
            throw new ArgumentNullException("bitmap");
        }

        this.bitmap = bitmap;
        this.positionFunction = positionFunction;
    }

    public Bitmap Bitmap
    {
        get { return this.bitmap; }
    }

    public PositionFunction PositionFunction
    {
        get { return this.positionFunction; }
    }
}

For the position function, you will need to decide how you want the bitmaps to move. (Note: time expressed in milliseconds.) It could be as simple as:

private Point SimpleTimeFunction(int time)
{
    return new Point(time / 5, time / 5);
}

private Point ParabolaFunction(int time)
{
    return new Point(time / 5, (time / 5) * (time / 5));
}

Or it could be a piecewise function composed of multiple equations, such as this:

if (time < 5000)
{
    return new Point(time / 5, 2 * (time / 5));
}
else
{
    return new Point(time / 5, time / 5) * time);
}

It all comes down to how you want it to move. Hopefully, you like mathematics. :)

Then, in the control which will be holding the bitmaps, add a List<MovingBitmap> field.

private List<MovingBitmap> bitmaps = new List<MovingBitmap>();

Then you need a bitmap the size of the parent control to use as a buffer, so the user experience will be flicker-less. You will draw all the moving bitmaps on the buffer, and then in turn draw that bitmap on the control in OnPaint.

private int startTime;  // set this to System.Environment.TickCount when you start

// Add this line to the constructor
this.UpdateBufferSize();

private void UpdateBufferSize()
{
    if (this.buffer != null)
    {
        this.buffer.Dispose();
    }

    this.buffer = new Bitmap(this.ClientSize.Width, this.ClientSize.Height);
}

private void RefreshBuffer()
{
    int timeElapsed = Environment.TickCount - this.startTime;

    using (Graphics g = Graphics.FromImage(this.buffer))
    {
        g.Clear(this.BackColor);

        foreach (MovingBitmap movingBitmap in this.bitmaps)
        {
            Rectangle destRectangle = new Rectangle(
                movingBitmap.PositionFunction(timeElapsed), 
                movingBitmap.Bitmap.Size);

            g.DrawImage(movingBitmap.Bitmap, destRectangle);
        }
    }
}

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    e.Graphics.DrawImage(this.buffer, Point.Empty);
}

To achieve the animation effect, you will need a timer. When the timer elapses, refresh the buffer and then the control.

private System.Timers.Timer timer;
private ParameterlessVoid refreshMethod;
private delegate void ParameterlessVoid();

// Add these four lines to the constructor
this.timer = new System.Timers.Timer(1000 / 20);  // 20 times per second
this.timer.AutoReset = true;
this.timer.Elapsed += this.HandleTimerElapsed;
this.refreshMethod = new ParameterlessVoid(this.Refresh);

private void HandleTimerElapsed(object sender, EventArgs e)
{
    this.RefreshBuffer();
    this.Invoke(this.refreshMethod);
}

private void Start()
{
    this.startTime = System.Environment.TickCount;
    this.timer.Start();
}

I think that's basically all you need to do. It's not fully tested and debugged, but it should point you in the right direction.


Override OnPaint() and draw the icons with e.Graphics.DrawImageUnscaled() where you want them.


I do this with a set of 32x32 .png icons. I use a FlowLayoutPanel as the container and add PictureBox controls to it. In this snippet, uxIconPanel is a FlowLayoutPanel.

foreach (Image img in GetImageListForClassification(entityClass))
{
    PictureBox box = new PictureBox();
    box.Image = img;
    box.Size = box.PreferredSize;
    box.Anchor = AnchorStyles.Top | AnchorStyles.Left;
    uxIconPanel.Controls.Add(box);
}


Start with an array to hold your image classes which will need

- image
- left
- top
- height
- width

Override the OnPaint() and draw each image in the array at its location.

Handle the MouseDown event.

- track the mouse down location
- determine whether the location hits one of the images 
  and track which one was hit
- track the original location of the hit image

Handle the MouseMove event.

- if not mouse button pressed then return
- if not tracking a hit from mouse down then return
- determine the change in position of the mouse from 
  what it was in the mouse down event
- apply the change in position to the original image position
  to set the new position of the tracked image
- Invalidate() to cause a repaint in the new position

This pseudo-code will give basic object dragging.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜