简体   繁体   中英

EventHandler inside loop for several Windows Forms

My code is creating multiple instances of a form with different controls and events. This is done while incrementing i to the total amount of forms. The aim is to add an event to a button control in the form. My forms are instances of Ticker , the subclass of Form that I have created.

The issue I have is that when I click on the button at run-time, the event handler kicks in and i is out of bounds as the loop has already finished.

A snippet of the code is below, the event handler is integrated into a loop incrementing i which also houses the switches. Any way for thisObject to be equal to the current active ticker while still keeping the event handler in the loop? EDIT: I've provided the full class for better clarity. The section in question follows through the action_set>>email>>trigger>>click cases.

   public partial class DesktopTickers : Form
    {
        public DesktopTickers(List<string> tickerArgs)
        {
            InitializeComponent();
            this.Opacity = 0;
            this.ShowInTaskbar = false;
            int totalTickers = tickerArgs.Count();

            Rectangle screenBounds = GetDpiSafeResolution();
            Size iconSizeDefault = this.Size;                                                         
            string iconDirectory = @"\\server\Tickers\";
            string iconSuffix = "_icon.png";
            string configDirectory = @"\\server\Tickers\";
            string configSuffix = ".txt";
            char paramDelimiter = ':';
            char controlDelimiter = '=';
            char[] controlSeparatorL = {'('};
            char controlSeparatorR = ')';
            char actionDelimiter = '=';
            char[] actionSeparatorL = {'('};
            char actionSeparatorR = ')';
            char propertyDelimiter = '-';
            int maxWidthDefault = iconSizeDefault.Width;                                                                             
            int maxHeightDefault = iconSizeDefault.Height;                                                                             
            Ticker[] tickers = new Ticker[tickerArgs.Count()];
            List<Control> controls = new List<Control>();

            for (int i = 0; i < tickerArgs.Count(); i++)                                                       
            {
                string tickerArg = tickerArgs[i];                                                               
                string tickerConfigPath = configDirectory + tickerArg + configSuffix;                           
                string tickerResourcePath = iconDirectory + @"\" + tickerArg + @"\";
                string tickerIconPath = tickerResourcePath + tickerArg + iconSuffix;
                tickers[i] = new Ticker(screenBounds, tickerArg, i+1, tickerArgs.Count(), iconSizeDefault, 
                                        maxHeightDefault, maxWidthDefault, tickerIconPath);
                string[] tickerConfigContents = File.ReadAllLines(tickerConfigPath);
                for (int j = 0; j < tickerConfigContents.Length; j++)               
                {
                    string thisConfigLine = tickerConfigContents[j];                                                    
                    int configParamEnd = thisConfigLine.IndexOf(paramDelimiter);                                        
                    if (configParamEnd < 0) { configParamEnd = 0; }
                    string tickerConfigParam = thisConfigLine.Substring(0, configParamEnd);                           
                    string tickerConfigValue = thisConfigLine.Substring(configParamEnd + 1);
                    switch (tickerConfigParam.ToLower())                                    
                    {   //TICKER LEVEL PARAMETERS
                        case "icon_width":
                            tickers[i].iconWidth = Convert.ToInt32(tickerConfigValue);
                            break;
                        case "icon_height":
                            tickers[i].iconHeight = Convert.ToInt32(tickerConfigValue);
                            break;
                        case "max_width":                                                    
                            tickers[i].maxWidth = Convert.ToInt32(tickerConfigValue);        
                            break;
                        case "max_height":                                                   
                            tickers[i].maxHeight = Convert.ToInt32(tickerConfigValue);  
                            break;
                        case "control_set":   
                            for (int k = j + 1; k < tickerConfigContents.Length; k++)        
                            {   //CONTROL LEVEL PARAMETERS
                                string thisControlLine = tickerConfigContents[k]; 
                                if(thisControlLine == "end") { break; }
                                int controlParamEnd = thisControlLine.IndexOf(controlDelimiter); 
                                string thisControlType = thisControlLine.Substring(0, controlParamEnd);
                                string thisControlDetails = thisControlLine.Substring(controlParamEnd+ 1);
                                thisControlDetails = thisControlDetails.Replace(controlSeparatorR.ToString(), "");
                                string[] controlProperties = thisControlDetails.Split(controlSeparatorL, StringSplitOptions.RemoveEmptyEntries); 
                                switch (thisControlType.ToLower()) 
                                {   //CONTROL TYPE LEVEL PARAMETERS
                                    case "image":                  
                                        PictureBox thisImage = new PictureBox(); 
                                        for (int l = 0; l < controlProperties.Length; l++)
                                        {
                                            string thisProperty = controlProperties[l];
                                            int propertyParamEnd = thisProperty.IndexOf(propertyDelimiter);
                                            string propertyType = thisProperty.Substring(0, propertyParamEnd - 1);
                                            string propertyValue = thisProperty.Substring(propertyParamEnd + 2);
                                            switch (propertyType.ToLower())
                                            {   //PROPERTY LEVEL PARAMETERS
                                                case "file":
                                                    try { thisImage.BackgroundImage = Image.FromFile(tickerResourcePath + propertyValue); }
                                                    catch { thisImage.BackgroundImage = thisImage.ErrorImage; }
                                                    break;
                                                case "bounds":
                                                    char[] pointDelimiter = { ',' };
                                                    string[] boundsValues = propertyValue.Split(pointDelimiter);
                                                    Rectangle bounds = new Rectangle(
                                                                            new Point(Convert.ToInt32(boundsValues[0]), Convert.ToInt32(boundsValues[1])),
                                                                            new Size(Convert.ToInt32(boundsValues[2]), Convert.ToInt32(boundsValues[3])));
                                                    thisImage.Bounds = bounds;
                                                    break;
                                                case "layout":
                                                    switch(propertyValue.ToLower())
                                                    {
                                                        case "stretch":
                                                            thisImage.BackgroundImageLayout = ImageLayout.Stretch;
                                                            break;
                                                        case "zoom":
                                                            thisImage.BackgroundImageLayout = ImageLayout.Zoom;
                                                            break;
                                                        case "tile":
                                                            thisImage.BackgroundImageLayout = ImageLayout.Tile;
                                                            break;
                                                        case "center":
                                                            thisImage.BackgroundImageLayout = ImageLayout.Center;
                                                            break;
                                                        default:
                                                            thisImage.BackgroundImageLayout = ImageLayout.None;
                                                            break;
                                                    }
                                                    break;
                                                case "id":
                                                    thisImage.Name = propertyValue;
                                                    break;
                                            }
                                            tickers[i].Controls.Add(thisImage);
                                            thisImage.Show();
                                        }
                                        break;
                                    case "label":
                                        Label thisLabel = new Label();
                                        for (int l = 0; l < controlProperties.Length; l++)
                                        {
                                            string thisProperty = controlProperties[l];
                                            int propertyParamEnd = thisProperty.IndexOf(propertyDelimiter);
                                            string propertyType = thisProperty.Substring(0, propertyParamEnd - 1);
                                            string propertyValue = thisProperty.Substring(propertyParamEnd + 2);
                                            thisLabel.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
                                            switch (propertyType.ToLower())
                                            {   //PROPERTY LEVEL PARAMETERS
                                                case "text":
                                                    thisLabel.Text = propertyValue;
                                                    break;
                                                case "font":
                                                    char fontDelimiter = ',';
                                                    int fontSplitIndex = propertyValue.IndexOf(fontDelimiter);
                                                    string fontName = propertyValue.Substring(0, fontSplitIndex);
                                                    string fontSize = propertyValue.Substring(fontSplitIndex + 1);
                                                    int fontSizeNum = int.Parse(fontSize);
                                                    thisLabel.Font = new Font(propertyValue, fontSizeNum);
                                                    break;
                                                case "bounds":
                                                    char[] pointDelimiter = {','};
                                                    string[] boundsValues = propertyValue.Split(pointDelimiter);
                                                    Rectangle bounds = new Rectangle(
                                                                            new Point(Convert.ToInt32(boundsValues[0]), Convert.ToInt32(boundsValues[1])),
                                                                            new Size(Convert.ToInt32(boundsValues[2]), Convert.ToInt32(boundsValues[3])));
                                                    thisLabel.Bounds = bounds;
                                                    break;
                                                case "id":
                                                    thisLabel.Name = propertyValue;
                                                    break;
                                            }
                                            thisLabel.Show();
                                            tickers[i].Controls.Add(thisLabel);
                                        }
                                        break;
                                    case "button":
                                        Button thisButton = new Button();
                                        for (int l = 0; l < controlProperties.Length; l++)
                                        {
                                            string thisProperty = controlProperties[l];
                                            int propertyParamEnd = thisProperty.IndexOf(propertyDelimiter);
                                            string propertyType = thisProperty.Substring(0, propertyParamEnd - 1);
                                            string propertyValue = thisProperty.Substring(propertyParamEnd + 2);
                                            switch (propertyType.ToLower())
                                            {
                                                case "text":
                                                    thisButton.Text = propertyValue;
                                                    break;
                                                case "font":
                                                    char fontDelimiter = ',';
                                                    int fontSplitIndex = propertyValue.IndexOf(fontDelimiter);
                                                    string fontName = propertyValue.Substring(0, fontSplitIndex);
                                                    string fontSize = propertyValue.Substring(fontSplitIndex + 1);
                                                    int fontSizeNum = int.Parse(fontSize);
                                                    thisButton.Font = new Font(propertyValue, fontSizeNum);
                                                    break;
                                                case "bounds":
                                                    char[] pointDelimiter = { ',' };
                                                    string[] boundsValues = propertyValue.Split(pointDelimiter);
                                                    Rectangle bounds = new Rectangle(
                                                                            new Point(Convert.ToInt32(boundsValues[0]), Convert.ToInt32(boundsValues[1])),
                                                                            new Size(Convert.ToInt32(boundsValues[2]), Convert.ToInt32(boundsValues[3])));
                                                    thisButton.Bounds = bounds;
                                                    break;
                                                case "id":
                                                    thisButton.Name = propertyValue;
                                                    break;
                                            }
                                            thisButton.Show();
                                            tickers[i].Controls.Add(thisButton);
                                        }
                                        break;
                                    case "textbox":
                                        TextBox thisTextBox = new TextBox();
                                        for (int l = 0; l < controlProperties.Length; l++)
                                        {
                                            string thisProperty = controlProperties[l];
                                            int propertyParamEnd = thisProperty.IndexOf(propertyDelimiter);
                                            string propertyType = thisProperty.Substring(0, propertyParamEnd - 1);
                                            string propertyValue = thisProperty.Substring(propertyParamEnd + 2);
                                            thisTextBox.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
                                            switch (propertyType.ToLower())
                                            {   //PROPERTY LEVEL PARAMETERS
                                                case "text":
                                                    thisTextBox.Text = propertyValue;
                                                    break;
                                                case "font":
                                                    char fontDelimiter = ',';
                                                    int fontSplitIndex = propertyValue.IndexOf(fontDelimiter);
                                                    string fontName = propertyValue.Substring(0, fontSplitIndex);
                                                    string fontSize = propertyValue.Substring(fontSplitIndex + 1);
                                                    int fontSizeNum = int.Parse(fontSize);
                                                    thisTextBox.Font = new Font(propertyValue, fontSizeNum);
                                                    break;
                                                case "bounds":
                                                    char[] pointDelimiter = { ',' };
                                                    string[] boundsValues = propertyValue.Split(pointDelimiter);
                                                    Rectangle bounds = new Rectangle(
                                                                            new Point(Convert.ToInt32(boundsValues[0]), Convert.ToInt32(boundsValues[1])),
                                                                            new Size(Convert.ToInt32(boundsValues[2]), Convert.ToInt32(boundsValues[3])));
                                                    thisTextBox.Bounds = bounds;
                                                    break;
                                                case "id":
                                                    thisTextBox.Name = propertyValue;
                                                    break;
                                            }
                                            thisTextBox.Show();
                                            tickers[i].Controls.Add(thisTextBox);
                                        }
                                        break;
                                }
                            }
                            break;
                        case "action_set":
                            for (int k = j + 1; k < tickerConfigContents.Length; k++)                                        
                            {   //ACTION LEVEL PARAMETERS
                                string thisActionLine = tickerConfigContents[k];                                             
                                if (thisActionLine == "end") { break; }
                                int actionParamEnd = thisActionLine.IndexOf(actionDelimiter);       
                                string thisActionType = thisActionLine.Substring(0, actionParamEnd);
                                string thisActionDetails = thisActionLine.Substring(actionParamEnd + 1);
                                thisActionDetails = thisActionDetails.Replace(actionSeparatorR.ToString(), ""); 
                                string[] actionProperties = thisActionDetails.Split(actionSeparatorL, StringSplitOptions.RemoveEmptyEntries);
                                Control thisObject = new Control();
                                switch (thisActionType.ToLower())                                                          
                                {   //ACTION TYPE LEVEL PARAMETERS
                                    case "email":
                                        //email requires trigger, objectid, action to send email, email action params
                                        for (int l = 0; l < actionProperties.Length; l++)
                                        {
                                            string thisProperty = actionProperties[l];
                                            int propertyParamEnd = thisProperty.IndexOf(propertyDelimiter);
                                            string propertyType = thisProperty.Substring(0, propertyParamEnd - 1);
                                            string propertyValue = thisProperty.Substring(propertyParamEnd + 2);
                                            string emailDomain = "";
                                            string emailServer = "";
                                            int emailPort = 0;
                                            string emailTemplate = "";
                                            string emailRecipient = "";
                                            switch (propertyType.ToLower())
                                            {
                                                case "domain":
                                                    emailDomain = propertyValue;
                                                    break;
                                                case "server":
                                                    emailServer = propertyValue;
                                                    break;
                                                case "port":
                                                    emailPort = Convert.ToInt32(propertyValue);
                                                    break;
                                                case "file":
                                                    emailTemplate = tickerResourcePath + propertyValue;
                                                    break;
                                                case "recipient":
                                                    emailRecipient = propertyValue;
                                                    break;
                                                case "object":
                                                    thisObject = tickers[i].Controls.Find(propertyValue, false).FirstOrDefault() as Control;
                                                    //thisObject = objects[0];
                                                    break;
                                                case "trigger":
                                                    tickers[i].SetEmailProperties(emailDomain, emailServer, emailPort, emailTemplate, emailRecipient);
                                                    switch(propertyValue.ToLower())
                                                    {
                                                        case "click":
                                                            thisObject.MouseDown += new MouseEventHandler((sender, e)
                                                                => tickers[i].SendEmail_Event(sender, e));
                                                            break;
                                                    }
                                                    break;
                                            }
                                        }
                                        break;
                                }
                            }
                            break;
                    }
                }
                tickers[i].Show();
            }
        }


        private Rectangle GetDpiSafeResolution()
        {
            using (Graphics graphics = this.CreateGraphics())
            {
                return new Rectangle(new Point(0, 0), new Size((Screen.PrimaryScreen.Bounds.Width * (int)graphics.DpiX) / 96
                , (Screen.PrimaryScreen.Bounds.Height * (int)graphics.DpiY) / 96));
            }
        }

    }

This seems to be a capture issue. You need to capture the value of i when the form is created and keep this value when you create the event handler. This can be done simply by creating a new variable iValue or tickerInd while creating the form and using this variable rather than i in the event handler code.

I am writing this answer a little bit speculatively. Please provide a larger code snippet which will allow us to see the creation code for the form and the handler. But I believe that your current code is as follows. The part that I marked as "code based on i" is the code snippet that you have currently provided.

for (var i = 0; i < formCount; ++i)
{
    var form = new Ticker();
    form.Button.OnClicked += () =>
        {
            //code based on i
            doSomething(i);
        };
}

It needs to be as follows:

for (var i = 0; i < formCount; ++i)
{
    var formInd = i;
    var form = new Ticker();
    form.Button.OnClicked += () =>
        {
            //code based on i
            doSomething(formInd);
        };
}

Edit: You need to change the following code:

case "click":
    //replace i with tickerInd, this will capture the current value of i
    var tickerInd = i;
    thisObject.MouseDown += new MouseEventHandler((sender, e)
      => tickers[tickerInd].SendEmail_Event(sender, e));
break;

Note: Your code seems to need some refactoring. First of all, you don't really need Ticker[] tickers , you can simply do the following:

public DesktopTickers(List<string> tickerArgs)
{
    for (var i = 0; i < tickerArgs.Count; ++i)
    {
        var tickerArg = tickerArgs[i];
        var ticker = new Ticker(...);
        //your whole initialization code goes here
        //replace all "tickers[i]" with "ticker"
        ticker.Show();
    }
}

Afterwards, you can do further refactoring by moving this initialization into Ticker constructor and then further divide it into initializtion methods.

I don't think I entirely understand the issue, but it seems you have a problem with scoping more than loops.

In C# 4 there was a bug in the compiler which scoped foreach and for variables such that when they were closed it would always be the last value assigned. This was fixed in C# 5.

If ticker[i] in (sender, e) => tickers[i].SendEmail_Event(sender, e) always ends up being out of scope, it could be because your iterator variable is declared outside the scope of the loop.

int i = 0;
var funcs = new Func<int>[100];
do 
{
    funcs[i] = () => i;
}while(++i < 100);

Assert.That(funcs[0](), Is.EqualTo(0));

This test will fail, because they all close over i, which in effect will be 100 when the loop ends. To fix this you need a variable that is in the inner scope (and therefore in this case unique to each iteration):

int i = 0;
var funcs = new Func<int>[100];
do 
{
  int closedInt = i;
  funcs[i] = () => closedInt;
}while(++i < 100);

Assert.That(funcs[0](), Is.EqualTo(0));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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