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.