I am currently using a loop to create a new User Control on my windows form. I want it to add a single instance of this User Control in a new position where Y is incremented by 125 each time.
I'm pretty new with C# and Visual Studio, so with the below code, the first instance is being replicated each time I press the 'add' event. I was just wondering if someone can give some assistance on the best way to store the value of 'y' from this first instance to be passed into the loop the second time? Or if there is any better way to do this.
Thanks in advance!
private void btnAddRow_Click(object sender, EventArgs e)
{
int y = 175;
for (int i = 0; i <= 0; i++)
{
NewSkillRow nSkill = new NewSkillRow();
nSkill.Location = new Point(75, y += 125);
Controls.Add(nSkill);
btnAddRow.Click += new EventHandler(btnAddRow_Click);
}
}
Make your y variable local to the class (you can also initialize it with its default):
private int y = 175;
The event handler is called every time you click the button. So remove the initialization of y from there.
private void btnAddRow_Click(object sender, EventArgs e)
{
var nSkill = new NewSkillRow();
nSkill.Location = new Point(75, y += 125);
Controls.Add(nSkill);
}
Note that the event handler attachment was removed. Reattaching an event handler from within the handler would lead to an increasing number of invokations every time the button is clicked.
The loop is fine, but not necessary: For just one iteration, you can as well just omit it.
The use of the y += 125 is also ok, it relies on the specification that the return value of an assignment operator is the value that has been assigned.
Related
Im trying to make a cookie clicker style thing where every time you click something it adds 1 to a variable. Im trying to do int clicks = clicks + 1 but it says that it is use of an uninitialized variable. I tried to set int clicks = 0 but then it says that clicks is already defined in the scope. I tried to see if i could do something like if (clicks == null) but obviously it cant check it because it is not a variable. I have only used c# for like a day, can someone please help?
private void cookie_Click(object sender, EventArgs e)
{
int clicks = 0;
clicks = clicks + 1;
numClicks.Text = "" + clicks;
}
^ this is the code. i also realized when i click it, it resets itself to 0 and goes back to 1, so it cant go 1, 2, 3 etc. is there a way to set the variable when the form starts and then start to add on clicks? im so dumb
int clicks = clicks + 1;
Is indeed nonsensical. This is the code that is declaring and initializing clicks, so it makes perfect sense that we can't ask "what is the value of clicks?" (in order so we can add one to it); until we have definitely assigned a value to clicks, the value is undefined.
Instead:
int clicks = 0;
And then when you want to increment it:
clicks++;
You'll want to do something like this:
int clicks = 0;//Define an integer 'clicks' and set it to 0
//and in your click handler:
clicks = clicks + 1;//Increment your count.
Note:
Clicks is an integer, it can never be null. (look up c# primitives for more info)
Using int clicks = clicks + 1 doesn't make sense. At the right side of the equation, what's the value of clicks? It's not defined yet.
The problem with your implementation is that every time the function gets called the first thing it does is sets clicks to 0. You need to initialize it outside of the function to avoid this. If you initialize it in the class it will be set to 0 initially when the class is created then you can increase the value everytime the functions is called. Not sure exactly your use case but without more information I would recommend doing something like this.
class YourClass {
// initialize outside of the function that increases the counter
int clicks = 0;
// rest of the code in class
private void cookie_Click(object sender, EventArgs e)
{
clicks++;
// this would also work: clicks = clicks + 1;
numClicks.Text = "" + clicks;
}
}
I am just playing around with arrays of rectangles in c# everything is working fine until I want to cycle between "layers" of rectangles, after cycling a few times to application (Silverlight) becomes very slow.
My code is as follows, would it be possible to offer any advice on what could be causing this degradation?
private int worldHeight = 20;
private int currentLayerNo = 0;
private int keypressCounter = 0;
public MainPage()
{
InitializeComponent();
createGrid();
}
private void createGrid()
{
blockCanvas.Children.Clear();
int x = 0, y = 0;
Layer generateWorld = new Layer();
generateWorld.z = worldHeight;
generateWorld.x = 50;
generateWorld.y = 50;
generateWorld.Gen();
Rectangle[,] currentLayer = generateWorld.createLayer(currentLayerNo);
for (int a = 0; a < currentLayer.GetLength(0); a++)
{
for (int b = 0; b < currentLayer.GetLength(1); b++)
{
Rectangle currentBlock = new Rectangle();
currentBlock = currentLayer[a, b];
blockCanvas.Children.Add(currentBlock);
Canvas.SetTop(currentBlock, x);
Canvas.SetLeft(currentBlock, y);
y = y + 32;
}
x = x + 32;
y = 0;
}
currentLayer = null;
this.KeyDown += new KeyEventHandler(onKeyDown);
}
private void onKeyDown(object sender, KeyEventArgs e)
{
//Code here cycles between layers, each time calling the createGrid() method after altering currentLayerNo
}
}
Each time you call createGrid() you are also calling this.KeyDown += new KeyEventHandler(onKeyDown); which adds a new event listener.
On the first call to createGrid() this means that on key down createGrid() will be called once, which will then register again for KeyDown, meaning will now be called twice. On the next KeyDown event createGrid() is called twice and each time it adds itself to the KeyDown event.
Repeat until slow down.
You can put the registration in the MainPage constructor.
Clearing and adding children on Canvas is performance intensive operation. If you want to improve performance, use caching. By this I mean instead of recreating all those rectangles, simply reuse them.
Reposition the ones you need, add extras if you need some or hide those that were created in the previous step, but will not be needed now.
Another reason for performance problem is that you have added the same event handler over and over again whenever you call creategrid. This means that when you press the key for the second time, you will call createGrid 2 times. On the each subsequent key press, you double the number of calls to createGrid.
Try to remove that line first and see if you need to use caching. I needed it in visually heavy WPF application and it worked like a charm.
I need to read the value from PLC and display it in a form whenever the PLC Tag value changes.
There will list of tags which I need to monitor. Whenever a TAG value changes i need to call a function(different functions for each tag).
This is what I have done so far for capturing the Tag value change..
After connecting to PLC, i ll create the LIST of tags.
Read TAG values in timer.
While reading i ll check with OLDVALUES tag, if there is any change in value I ll raise an event.
This is working fine with 4 or 5 tags. When the Tag count is more, say 100, some of the Tag change events are not firing..
This is what so far I have done..
public delegate void DataChangedEventHandler(string TagName, string NewValue);
private Timer tmr = new Timer();
public event DataChangedEventHandler OnDataChanged;
private void StartTimer(DataTable dt)
{
AddTagList(dt);
SetInitialVales();
tmr.Tick += timerticks;
tmr.Interval = 250;
tmr.Enabled = true;
tmr.Start();
}
private void StopTimer()
{
tmr.Enabled = false;
tmr.Stop();
}
I ll add the list of tags..
private List<string> TagValues = new List<string>();
private List<string> oldValues = new List<string>();
private List<string> newValues = new List<string>();
private void AddTagList(DataTable dt)
{
int ILoop = 0;
foreach (DataRow row in dt.Rows)
{
TagValues.Add((string)row["Path"]);
ILoop = ILoop + 1;
}
}
To set the initial values of the Tags.
private void SetInitialVales()
{
int iLoop = 0;
foreach (string vals in TagValues)
{
var rd = ReadTag(vals);
oldValues.Add(rd.ToString());
newValues.Add(rd.ToString());
iLoop = iLoop + 1;
}
//newValues = oldValues
}
and the main datachange part.
private void timerticks(object sender, EventArgs eventArgs)
{
int iLoop = 0;
foreach (string vals in TagValues)
{
oldValues[iLoop] = ReadTag(vals).ToString();
if (oldValues[iLoop] != newValues[iLoop])
{
newValues[iLoop] = oldValues[iLoop];
if (OnDataChanged != null)
{
OnDataChanged(vals, newValues[iLoop]);
}
}
iLoop = iLoop + 1;
}
}
My Queries:
1.What will happen if a event is raised while already raised event is still in progress(sub procedure is not completed)?? Is because of this reason I am missing some datachange events??
2.How to raise a raise a event automatically whenever the member of LIST value changes??
3.Any other better method to handle the timer-read-raiseevent?
What will happen if a event is raised while already raised event is still in progress
The event won't be raised, not until your code is done executing the previous one. Clearly you'll run into trouble when the events you fire take too long, longer than 1 second. The more tags you have, or the more of them can change within one scan, the greater the odds that these events take more than 1 second and thus miss a tag change.
You'll need to de-couple the scanning from the event processing. You can do so with a worker thread that does nothing but check for tag changes in a loop. And if it sees any, put a update notification in a thread-safe queue. Another thread, like your UI thread, can empty the queue and process the notifications. The queue now acts as a buffer, providing enough storage to be able to keep up with a sudden burst of tag changes.
Wouldn't it be better to create a class with old-new value in it and then a map with the tag as key to access the old-new instance?
It seems otherwise you have a lot of things floating around that need to be kept synched.
I am doing a project which includes dynamic controls creation and removal from the WinForm,
So I decided to that part on a small test project.
Test project has two files Form1.cs and NewControls.cs. This program creates additional buttons whenever user clicks an Add button already on the form.And removes the newly created button when it is clicked (self removal button). Also after removal of button other button's Name, Text and their position are changed according to a local variable (controlIndex).
Form1.cs
public partial class Form1 : Form
{
static List<NewControl> newControlsList = new List<NewControl>();
public Form1()
{
InitializeComponent();
}
private void Add_Click(object sender, EventArgs e)
{
newControlsList.Add(new NewControl(newControlsList.Count));
}
public static void RemoveButton(object sender, EventArgs e)
{
NewControl tempNewControl = (NewControl)(sender as Button).Tag;
tempNewControl.RemoveControl();
newControlsList.Remove(tempNewControl);
MessageBox.Show("Removed!");
foreach (NewControl tempcontrol in newControlsList)
{
tempcontrol.controlIndex = newControlsList.IndexOf(tempcontrol);
tempcontrol.PlaceControl();
}
}
}
NewControl.cs
class NewControl
{
public int controlIndex = 0;
Button newButton = new Button();
public NewControl(int index)
{
controlIndex = index;
PlaceControl();
}
public void RemoveControl()
{
newButton.Dispose();
Form1.ActiveForm.Controls.Remove(newButton);
}
public void PlaceControl()
{
newButton.Tag = this;
newButton.Name = "btn" + controlIndex.ToString("D2");
newButton.Text = "btn" + controlIndex.ToString("D2");
newButton.Size = new Size(100, 20);
newButton.Left = controlIndex * 100;
Form1.ActiveForm.Controls.Add(newButton);
newButton.Click += new EventHandler(Form1.RemoveButton);
}
}
Program works nearly as expected. Problem is the MessageBox which I used in form1.cs in RemoveButton() fires many time (as opposed to just one time), which implies whole method being executed several times. Actually I pasted that MessageBox for debugging (sort of).
Since I cannot debug the application as when "Form1.ActiveForm.Controls.Add(newButton);" statement is executed, debugger Throws NullReferenceException, as there is not an active form while debugging.
I know that's like a bonus question but I thought to just put it there. I am a beginner and can't see the way through both the problems. 1st problem is really important for my original project as it will cause problem when many controls are added.
I think it is because you call PlaceControl from Form1.cs AND in the constructor of the NewControl class, Because you say newButton.Click += new EventHandler(Form1.RemoveButton);.
You are adding EventHandlers, so there can be more of them.
So when you call placeControl multiple times, you've got multiple event handlers, i think.
Probably the EventHandler hasn't been removed by RemoveButton. (I've been working in java most recently so my terms might be a little off for C#.) Suggestion: set control visibility to true when you want it and false otherwise rather than adding and removing.
Everytime a button is removed you go over your existing list of controls, and you call "PlaceControl", which attaches yet another handler.
foreach (NewControl tempcontrol in newControlsList)
{
tempcontrol.controlIndex = newControlsList.IndexOf(tempcontrol);
tempcontrol.PlaceControl();
}
Remove the above code block from RemoveButton, and you will see that your dynamically added buttons will each only trigger the event once.
In your RemoveButton event you loop on each button and call again PlaceControl.
The only reason is to reposition the remainder controls.
I think it's better a call to a separate method that do only this work.
public void RepositionControl()
{
newButton.Left = controlIndex * 100;
}
this will prevent to mess with event handlers added more than one time
I have a case where I want a given event to execute once, and only once. I'm trying to do it this way, but I'm having prolems (else I wouldn't be asking). Note that the following code is inside the function that decides that the event needs to be fired.
EventHandler h = delegate(Object sender, EventArgs e) {
FiringControl.TheEvent -= h; // <-- Error, use of unassigned local variable "h"
// Do stuff
}
FiringControl.TheEvent += h;
In general this should work because of the way scope is preserved for delegates until after they're done running, but, since h is in the process of being built when I try to use it it is still considered to be "uninitialized", apparently.
You could set h = null first and then set it equal to something else? Like this?
EventHandler h = null;
h = delegate(Object sender, EventArgs e) {
FiringControl.TheEvent -= h; // <-- Error, use of unassigned local variable "h"
// Do stuff
}
FiringControl.TheEvent += h;
But are you sure this is the right approach? What are you trying to do overall?