簡體   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