繁体   English   中英

C#退订带有额外参数的匿名方法

[英]c# unsubscribe anonym method with extra parameters

我将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添加额外的参数。

我如何退订该活动?

您订阅了一个匿名方法,然后要取消订阅,则需要对该方法的引用(您尚未引用)。 为了使其更加复杂,它也是一个闭包,那么您不能简单地将其移至普通类方法。

首先也是最简单的解决方法(不要这样做)是将该委托存储在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;

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

让我们分步骤进行:首先声明一个简单的 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属性中,并使用隐藏的 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) )。

请记住,这不是完美的解决方案,也不是始终使用的解决方案,如果这样做是为了显示大量图片和文本,则添加100多个控件会严重影响性能,在这种情况下,最好保留一个列表显示的对象并进行相应的绘制(将TableLayoutPanelPictureBox放在一起)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM