【问题标题】:C#. Displaying and moving icons on a windows formC#。在 Windows 窗体上显示和移动图标
【发布时间】:2011-01-19 03:27:22
【问题描述】:

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

尽管很多人有同样的问题,但我已经在谷歌上搜索了很多解决方案,但无济于事。你有解决办法吗?

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

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

编辑 下面是一些截图:上一个是设计时的PictureBox,下一个是运行时

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

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

【问题讨论】:

  • 被用户或代码移动了?图标是什么格式的?
  • 以编程方式。图标为 512x512。
  • 您是否在 WinForms 应用程序中创建“抓猴子”广告横幅?如果是这样,请将结果发送给我,这是有史以来最好的广告。

标签: c# winforms transparency icons picturebox


【解决方案1】:

正如其他人所说,我会使用位图而不是图标。以下是我的做法:

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

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&lt;MovingBitmap&gt; 字段。

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

我认为这基本上就是您需要做的所有事情。它没有经过全面测试和调试,但它应该为您指明正确的方向。

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 我该如何移动它们?
    • 把它们画在不同的位置?您可以通过计时器触发表单失效。
    【解决方案3】:

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

    【讨论】:

    • 同样的问题。图标太小了。如果我将 box.SizeMode 更改为拉伸图标会看起来很糟糕。
    【解决方案4】:

    从一个数组开始来保存你需要的图像类

    - 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
    

    这个伪代码将给出基本的对象拖动。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-15
      • 1970-01-01
      • 2010-11-22
      • 2023-03-30
      • 2012-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多