C#。在 Windows 窗体上显示和移动图标

发布于 2024-08-22 11:20:24 字数 705 浏览 5 评论 0原文

我需要在 Windows 窗体上显示一组图标并在运行时移动它们。我已经使用 PictureBox 控件来显示图标,但我必须要么不将 picturebox.SizeMode 更改为 Stretched,这会导致图标非常小,或者它们在运行时看起来模糊且可怕,即使它们是高质量的图标。

尽管很多人遇到了同样的问题,但我在谷歌上大量搜索了解决方案,但没有成功。你有解决办法吗?

如果没有,将如何在 Windows 窗体上显示图标,以便它们可以在运行时轻松移动?

感谢您分享您的时间和知识。

编辑 以下是一些屏幕截图: 上一张是设计时的 PictureBox,下一张是运行时的

alt文本 http://www.imagechicken.com/uploads/1266687694012129500.png

替代文本http://www.imagechicken.com/uploads/1266687487098617800.png

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 display 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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

梦与时光遇 2024-08-29 11:20:24

正如其他人所说,我会使用位图而不是图标。我将这样做:

首先,创建一个包含位图的类和一个委托,该委托将指向一个方法,该方法将获取位图作为时间函数的位置。

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; }
    }
}

对于位置函数,您需要决定位图如何移动。 (注意:时间以毫秒表示。)它可以很简单:

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));
}

或者它可以是由多个方程组成的分段函数,例如:

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

这一切都取决于您希望它如何移动。希望你喜欢数学。 :)

然后,在保存位图的控件中,添加一个 List 字段。

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

然后,您需要一个父控件大小的位图来用作缓冲区,这样用户体验将不会闪烁。您将在缓冲区上绘制所有移动位图,然后在 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);
}

为了实现动画效果,你需要一个定时器。当计时器到时,刷新缓冲区,然后刷新控件。

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 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.

凯凯我们等你回来 2024-08-29 11:20:24

重写 OnPaint() 并使用 e.Graphics.DrawImageUnscaled() 在您想要的位置绘制图标。

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

遇见了你 2024-08-29 11:20:24

我使用一组 32x32 .png 图标来完成此操作。我使用 FlowLayoutPanel 作为容器并向其中添加 PictureBox 控件。在此代码片段中,uxIconPanel 是一个 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);
}

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);
}
熊抱啵儿 2024-08-29 11:20:24

从一个数组开始保存图像类,该类需要

- image
- left
- top
- height
- width

重写 OnPaint() 并在数组中的位置绘制每个图像。

处理 MouseDown 事件。

- 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

处理 MouseMove 事件。

- 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

该伪代码将提供基本的对象拖动。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文