【问题标题】:c# unsubscribe anonym method with extra parametersc# 取消订阅带有额外参数的匿名方法
【发布时间】:2018-08-17 21:14:31
【问题描述】:

我将PictureBox 中的图像添加到TableLayoutPanel 并使用匿名方法在图像上写入文本,如下所示:

    private void AddPictureWithText(string text, int textX, int textY, int col, int row)
    {
        var picBox = new PictureBox()
        {
            Image = Properties.Resources.ProgressStage_LT_GRAY,
            SizeMode = PictureBoxSizeMode.Zoom,
            Dock = DockStyle.Fill,
        };
        picBox.Paint += (sender, e) => { picPaint(sender, e, text, textX, textY); };
        tableLayoutPanel1.Controls.Add(picBox, col, row);
    }

    private void picPaint(object sender, PaintEventArgs e, string text, int textPosX, int textPosY)
    {
        using (Font myFont = new Font("Arial", 12))
        {
            e.Graphics.DrawString(text, myFont, Brushes.White, new Point(textPosX, textPosY));
        }
    }

我需要匿名方法,因为我必须向Paint event 添加额外的参数。

如何退订此活动?

【问题讨论】:

  • 您必须将传递给绘制事件的委托保存起来,以便以后使用私有字段之类的东西。然后,您可以从班级中的其他位置删除委托。
  • 让代码说话。 :)
  • this.whatever = new Func<object, Eventargs>((sender, e) => { .... }); picBox.Paint += this.whatever;

标签: c# .net winforms events anonymous-methods


【解决方案1】:

您订阅了一个匿名方法,然后要取消订阅,您需要对该方法的引用(您没有)。为了使它更复杂,它也是一个闭包,那么你不能简单地将它移动到一个普通的类方法中。

第一个也是最简单的解决方法(不要这样做)是将该委托存储在Control.Tag 属性中。你可以有一个局部变量,或者在 C# 7 中,一个局部函数:

private void AddPictureWithText(string text, int textX, int textY, int col, int row)
{
    var picBox = new PictureBox
    {
        Image = Properties.Resources.ProgressStage_LT_GRAY,
        SizeMode = PictureBoxSizeMode.Zoom,
        Dock = DockStyle.Fill,
        Tag = PaintPictureBox
    };

    picBox.Paint += PaintPictureBox;
    tableLayoutPanel1.Controls.Add(picBox, col, row);

    void PaintPictureBox(object sender, PaintEventArgs e)
        => picPaint(sender, e, text, textX, textY);
} 

要删除它,您只需选择 PictureBox.Tag,将其转换为 PaintEventHandler 并删除它:

pbox.Paint -= (PaintEventHandler)pbox.Tag;

不要这样做。这只是解决问题的错误方法的解决方法。

让我们分步进行:首先声明一个plain PaintEventHandler:

private void PaintPictureBox(object sender, PaintEventArgs e)
{
}

然后您需要一些参数,最好的办法可能是跟踪您必须使用 模型 绘制的内容,但我们稍后再讨论。现在你可以添加你需要的参数到Control.Tag:

    var picBox = new PictureBox
    {
        Image = Properties.Resources.ProgressStage_LT_GRAY,
        SizeMode = PictureBoxSizeMode.Zoom,
        Dock = DockStyle.Fill,
        Tag = new PaintModel { Text = text, Locaiton = new Point(textx, texty) }
    };

内部PaintPictureBox

var data = (PaintModel)((Control)sender).Tag;

您可以访问data.Textdata.Location。不要忘记使用相关属性(或使用命名元组)声明所需的 PaintModel 类/结构。如果您不需要任何其他属性,您可以直接将Point 放在Tag 属性中并使用hidden PictureBox.Text 属性作为标题(在这种情况下,您显然不需要需要PaintModel 类/结构。)

不要这样做。这只是一个稍微好一点的方法,因为我们仍在处理职责(其他人负责在PictureBox 中绘制文本而不是自己。)下一步是什么?我们来介绍一个自定义控件:

sealed class PictureBoxWithText : PictureBox
{
    public Point TextLocation { get; set; }

    public override void OnPaint(PaintEventArgs pe)
    {
        base.OnPaint(pe);

        using (Font myFont = new Font("Arial", 12))
            pe.Graphics.DrawString(Text, myFont, Brushes.White, TextLocation);
    }
}

您的AddPictureWithText() 将是:

var picBox = new PictureBoxWithText
{
    Text = text,
    TextLocation = new Point(textx, texty),
    Image = Properties.Resources.ProgressStage_LT_GRAY,
    SizeMode = PictureBoxSizeMode.Zoom,
    Dock = DockStyle.Fill,
};

tableLayoutPanel1.Controls.Add(picBox, col, row);

稍微好一点,我们还有一些地方需要改进:我们正在为每个绘制操作创建(并正确处理)Font 对象。它很慢并且消耗资源。将其设为私有字段:

sealed class PictureBoxWithText : PictureBox
{
    private readonly Font _textFont = new Font("Arial", 12);

    protected override Dispose(bool disposing)
    {
        try
        {
            if (disposing)
                _textFont?.Dispose();
        }
        finally
        {
            base.Dispose(disposing);
        }            
    }

    // Existing implementation
}

如果字体是固定的,那么您可以将其移动到 static readonly 字段(无需覆盖 Dispose(bool))。

请记住,这不是 完美always-use-this 解决方案,如果您这样做是为了显示大量图片+文字然后添加 100 + 控件会严重影响性能,在这种情况下,您最好保留正在显示的对象列表并相应地执行绘制(将TableLayoutPanelPictureBox 放在一起。)

【讨论】:

  • 我只需要 5 张图片,不会更多。谢谢。它解决了我的问题。我曾经派生出 PictureBox 类。
猜你喜欢
  • 2010-09-16
  • 2011-09-12
  • 2012-04-02
  • 2021-07-24
  • 1970-01-01
  • 1970-01-01
  • 2021-06-14
  • 1970-01-01
  • 2020-06-03
相关资源
最近更新 更多