Let's say that my program have max 8 buttons and I should be able to switch them on/off (visible/hidden) according to my settings in configuration file.
My chosen buttons should be then visible in two rows (if more than 4) or one row (cnt<=4) on the form leaving no gap between them.(I mean when 1,2,3,4 are active they would be in the first row same 1,2,5,8 is active so the 5 will take place of the button 3 and 8 will take the 4th place)
<add key="butactive" value="1;3;4;8"/>
<add key="but1" value="START"/>
<add key="but2" value="END"/>
<add key="but3" value="PAUSE"/>
<add key="but4" value="RESET"/>
...
The program should be able to determine from "butactive" key which of those buttons will be visible and then change their Text property and add specific action which is also taken from config file.
private void Form2_Load(object sender, EventArgs e)
{
radButton1.Text = ConfigurationManager.AppSettings["but1"];
radButton2.Text = ConfigurationManager.AppSettings["but2"];
radButton3.Text = ConfigurationManager.AppSettings["but3"];
radButton4.Text = ConfigurationManager.AppSettings["but4"];
...
radButton1.Click += getAction(ConfigurationManager.AppSettings["but1a"]);
radButton2.Click += getAction(ConfigurationManager.AppSettings["but2a"]);
...
}
Any idea how to select needed buttons when the form loads and place them in desired positions?
You can use a FlowLayoutPanel as a container for your buttons. If you size it correctly everything you need to do is set the visibility of the buttons and they will arrange themselves as you need:
radButton1.Visible = (bool)ConfigurationManager.AppSettings["butactive1"];
radButton2.Visible = (boll)ConfigurationManager.AppSettings["butactive2"];
...
As for the action, you can use the same event handler for all the buttons and execute whatever you need based on the button that was pressed and what action you have defined:
radButton1.Click += buttonClickHandler;
radButton2.Click += buttonClickHandler;
private void buttonClickHandler(object sender, EventArgs e)
{
var button = sender as Button;
var action = getAction(ConfigurationManager.AppSettings[button.Text + "a"];
//Execute whatever you want here based on action
}
That piece of code you have there is not good, you are creating dependencies on config names with hard codded strings, that is not a good practice at all, if you can access Properties.Default I suggest you to go that way.
For the buttons position you can mess around with positions, for instance you can switch button 3 and 5 positions like this in windows forms:
radButton5.Position = radButton3.Position
But that is not a good approach, what I advice you is to cut that dependency in button numbers out, you can for instance create all buttons hidden and always show the first buttons, the method "getAction" will be a great help in that approach and its not to hard to implement the behavior.
A Scratch (pseudo code) of what you should do would be something like this:
// Get the active buttons from configuration (eg var will be a list of the active buttons names)
var activeButtons = getActiveButtons();
var textBoxNumber = 0
foreach(var button in activeButtons)
{
textBoxNumber++;
TextBox tbx = this.Controls.Find("radButton" + textBoxNumber.ToString(), true).FirstOrDefault() as TextBox;
if(tbx != null)
tbx.Text = ConfigurationManager.AppSettings[button];
}
Related
While working on a small app that pulls test cases, runs, and results from an SQL Server Database, I encountered a dilemma in my methodology for attempting to create dynamic controller names in a TableLayoutPanel in WinForms. I am creating the rows dynamically when the user chooses the particular test case, and from there the TableLayoutPanel will open another window with the test steps preloaded and two radio buttons to indicate whether or not the test passed. My issue is that when I select one of the radio buttons on the right of the step, I get the same console read every single time. I need to be able to determine which exact radio button the user has pressed so I can therefore determine what row it's in and subsequently what test either passed or failed. My main code is as follows:
FormManualTest.cs (section when adding to the TableLayoutPanel)
private void addRowToolStripMenuItem_Click(object sender, EventArgs anotherEvent)
{
tableLayoutTest.RowStyles.Clear(); // Clear row styles to ensure a clean start when adding to the TableLayoutPanel
List<RadioButton> listOfRadioControls = new List<RadioButton>(); // Create array of radio buttons
List<UserCustomStep> listOfStepControls = new List<UserCustomStep>(); // Create array of custom controls
for (int i = 0; i < 5; i++)
{
UserCustomStep step = new UserCustomStep(Counter, "Step: " + i + " Push the button to elicit a response."); // Creates new user custom step control instance
RadioButton pass = new RadioButton();
pass.Text = "Pass";
pass.AutoSize = true;
RadioButton fail = new RadioButton();
fail.Text = "Fail";
fail.AutoSize = true;
fail.Margin = new Padding(3,3,20,3); // Needed to see the fail button without having to scroll over
listOfStepControls.Add(step); // Add step to UserCustomStep array
listOfRadioControls.Add(pass); // Add radio buttons to the RadioButton array
listOfRadioControls.Add(fail);
listOfRadioControls[i * 2].CheckedChanged += (s, e) => // Subscribes the pass radio button to listen for when a user has clicked on it
{
Console.WriteLine("Pass " + i + " was clicked");
};
listOfRadioControls[(i * 2) + 1].CheckedChanged += (s, e) => // Subscribes the fail radio button to listen for when a user has clicked on it
{
Console.WriteLine("Fail " + i + " was clicked");
};
tableLayoutTest.Controls.Add(listOfStepControls[i], 0, i); // Adds CustomStep to first column
tableLayoutTest.Controls.Add(listOfRadioControls[i*2], 1, i); // Adds Pass Radio Button to second column
tableLayoutTest.Controls.Add(listOfRadioControls[(i * 2) + 1], 2, i); // Add Fail Raido Button to third column
Counter++; // Increment couter to add subsequent steps underneath the previous ones.
}
}
Screenshots of App with Console Readout:
After Test Case Has Been Clicked and Radio Button Has Been Pressed
(From clicking this I would expect the console to read "Pass 1 was clicked")
Console Read:
Click Fail Button:
(I know from this image below that since the Pass button doesn't remain clicked I'm somehow using the same controller for all 5 of them)
Console Read
So from all of these issues that I've been presented with, I know that I'm somehow using the same controller for all 5 instances regardless of the fact that I'm storing everything in a controller array and grabbing from there. The for loop will have to be converted to a for each loop later, but that still doesn't solve my issue. I believe that if I could say something like:
RadioButton (pass+id) = new RadioButton();
or something similar while looping through to dynamically create the name for the controls, then each one would be a completely separate control and I could go from there. Any help would be greatly appreciated! I come from a heavy web background so my normal skills to remedy this in JS land aren't coming in handy as of right now. Thanks again for the assistance.
The Name property is optional, you don't need to specify it and it doesn't need to be unique. You can use property Tag for your own purpose (you can assign there ID or event instance of some object).
However you can also create your own control/usercontrol which encapsulate the whole row, and you can declare your own properties exactly for your purpose.
Building a program in which when a button is pushed, it saves that pushed event as a "1" in an array. I want to have 3 buttons, so the array would have 3 fields full of 0s or 1s (Pushed or not pushed) during a dictated length of time. Not too familiar with C#, so I have no idea where to start. Any ideas?
First, you should get familiar with the C# language. A good way to start: https://www.tutorialspoint.com/csharp/
I don't know what your final aim is, but if you really want to go with arrays then try something like this:
// declare it as class member
int[] buttonStates = new int[3] {0, 0, 0}; // immediate initialization
There are several ways to handle button presses. It's depending on the platform you're developing on: WinForms, ASP.NET, WPF, etc.?
But in general:
your button event handlers should contain the code that places the 1s in the array.
// in the first button handler:
buttonStates[0] = 1;
// in the second button handler:
buttonStates[1] = 1;
// in the third button handler:
buttonStates[2] = 1;
Adding to the provided answers and as your title suggests to get Button ID. To identify each button uniquely, you can use 'Name'(unique name for the element to be identified) or 'Tag' (can be used to store custom information about the element) properties of a button.
You can set the button ID in the tag property of button:
Button btn = new Button();
btn.Tag = 1;
Then in your event handler
private void Btn_Click(Object sender, RoutedEventArgs e)
{
Button b = (Button)sender;
int btnTag = (int)b.Tag; //check the Id
}
and can refer to the button using this 'ID'
In my WinForms Visual Studio application i have a checkbox styled as a Flat Button with this C# code:
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (TestBox.Checked == true)
{
TestBox.Image = Image.FromFile("M:\\CheckBox_52x.png");
TestBox.ImageAlign = ContentAlignment.MiddleCenter;
TestBox.FlatAppearance.BorderSize = 0;
// make all four (!) BackColors transparent!
TestBox.BackColor = System.Drawing.Color.Transparent;
TestBox.FlatAppearance.CheckedBackColor = Color.Transparent;
TestBox.FlatAppearance.MouseDownBackColor = Color.Transparent;
}
else
{
TestBox.Image = Image.FromFile("M:\\CheckBoxUncheck_52x.png");
TestBox.ImageAlign = ContentAlignment.MiddleCenter;
TestBox.FlatAppearance.BorderSize = 0;
// make all four (!) BackColors transparent!
TestBox.BackColor = System.Drawing.Color.Cyan;
TestBox.FlatAppearance.CheckedBackColor = Color.Cyan;
TestBox.FlatAppearance.MouseDownBackColor = Color.Cyan;
}
}
I was wondering, if instead of doing this to every single checkbox in my application, can i make the "UNCHECKED" version i have coded the default checkbox style for this applicatiom - eg - every time i create a new one it appears with these properties.
Please keep in mind that i am brand new to coding in C#.
If you want multiple controls to use the same eventhandler, that's easy - just use the same event handler. Change your code to something like:
private void HandleCheckBoxCheckedChanged(object sender, EventArgs e)
{
CheckBox checkBox = (CheckBox) sender;
string imageFile;
Color color;
if (checkBox.Checked == true)
{
// TODO: Use resources embedded within your app
imageFile = "M:\\CheckBox_52x.png";
color = Color.Transparent;
}
else
{
imageFile = "M:\\CheckBoxUncheck_52x.png";
color = Color.Cyan;
}
// TODO: Load each file once and reuse the bitmap, I suspect.
checkBox.Image = Image.FromFile(imageFile);
checkBox.ImageAlign = ContentAlignment.MiddleCenter;
checkBox.FlatAppearance.BorderSize = 0;
checkBox.BackColor = color;
checkBox.FlatAppearance.CheckedBackColor = color;
checkBox.FlatAppearance.MouseDownBackColor = color;
}
You can then attach the same handler to all your checkboxes.
If you have multiple classes, you could make that a public static method. At that point you may need to add the event handler in code rather than getting the designer to do it - I don't know whether the designer knows how to use static methods for event handlers. But it would just be something like:
TestBox.CheckedChanged += CheckBoxUtilities.HandleCheckBoxCheckedChanged;
That's if you really just want the same code to be used for event handlers. Other things to consider are:
Constructing a subclass of CheckBox as suggested by rakatherock. My own experience with creating custom controls in Windows Forms has not been great, but from an OO perspective it feels fine. An initial implementation could just derive from CheckBox and implicitly add an event handler which does exactly what your current code does.
If you want to find all the CheckBox controls in a form at some point, you can use the Controls property and then recurse through any control which itself a container. I won't go into the details of that now though, as it sounds like you don't really want this - unless you did it just to find all the CheckBox controls and add the same event handler to all of them.
This can be done by creating custom checkbox control.
From the question what you are trying to do is to manipulate all check boxes in the application from a single check box so here the logic goes like:
Save the All checkbox value(Checked or unchecked) in the settings
To create a settings value
Right Click Project>Properties>Settings>Give Name as someBoolValue,type as bool,Scope as User
So on CheckChanged event of this check box save the value in Settings variable that we have created just now.In CheckChangedEvent of main All Check box code goes like:
Properties.Settings.Default.someBoolValue=cbCheckBox.Checked;
Properties.Settings.Default.Save();
Now on every form load are where ever the event is suitable do like
foreach(Control c in this.Controls)
{
if(c is CheckBox)
{
c.Checked=Properties.Settings.Default.someBoolValue
}
}
Note:You can replace all the check box that i implemented with your custom controls.I have given you just an idea how to do it.
I'm a newbie in c# and probably going to ask a very easy question, but I've not been able to find anything on the web to help.
I have a tabControl with a TabPage which is containing a TextBox object; this object, when the event "Text changed" is invoked, will perform the change of the parent tabPage's name.
The textbox where I typed "text changed by me" has a method which is managing changing the name of the tabPage:
private void textBox1_TextChanged(object sender, EventArgs e)
{
if (this.textBox1.Text != "")
this.tabControl2.SelectedTab.Text = this.textBox1.Text;
else
this.tabControl2.SelectedTab.Text = "(no name)";
}
Into the current page menu is contained a control to add a new page, which runs this method when the user click on it:
private void addNewPageToolStripMenuItem_Click(object sender, EventArgs e)
{
int numPagine;
string strPagine;
numPagine = this.tabControl2.TabCount;
strPagine = numPagine.ToString();
this.tabControl2.TabPages.Add("new page" + strPagine);
}
...and here is the output, which is expected since I'm just asking to add a new empty tabPage:
So, my question is: how can I make possible that when the user is clicking on "Add new page", rather than creating an empty new tabPage the program is rather creating a page like the first one (i.e. containing a textbox into the same position which has a method to change the text of the parent tabPage that I have just created?
Here is an example.
//..
// create the new page
TabPage tpNew = new TabPage("new page..");
// add it to the tab
this.tabControl2.TabPages.Add(tpNew);
// create one labe with text and location like label1
Label lbl = new Label();
lbl.Text = label1.Text;
lbl.Location = label1.Location;
// create a new textbox..
TextBox tbx = new TextBox();
tbx.Location = textBox1.Location;
tpNew.Controls.Add(lbl);
tpNew.Controls.Add(tbx);
// add code to the new textbox via lambda code:
tbx.TextChanged += ( (sender2, evArgs) =>
{
if (tbx.Text != "")
this.tabControl2.SelectedTab.Text = tbx.Text;
else
this.tabControl2.SelectedTab.Text = "(no name)";
} );
For more complicated layout you may want to consider creating a user control..
You also may want to create the first page with this code; the, of course with real values for text and positions!
For creating a UserControl you go to the project tag and right click Add-UserControl-UserControl and name it, maybe myTagPageUC. Then you can do layout on it like on a form. A rather good example is right here on MSDN
The problem is that is has no connection to the form, meaning you'll have to code all sorts of references to make it work..
I'm not really sure if you may not be better off writing a complete clonePage method instead. It could work like the code above, but would loop over the Controls of the template page and check on the various types to add the right controls..
It really depends on what is more complicated: the Layout or the ties between the pages and the form and its other controls..
For my C# Windows Form Application, I have created a flowlayoutpanel that contains several panels. Inside the panel, I have a button "Clear" for each and every single panel.
How do I write the event handler for the code for the button "Clear" such that once I have click the button, the panel would sort of be "Removed" from the flowlayoutpanel.
This is a short part of the code of the adding of panels to the flowlayoutpanel.
nFlowPanel.Controls.Add(createNotificationPanel());
nFlowPanel.Controls.Add(createNotificationPanel());
nFlowPanel.Controls.Add(createNotificationPanel());
nFlowPanel.Controls.Add(createNotificationPanelImpt());
nFlowPanel.Controls.Add(createNotificationPanelImpt());
and this is the code for the button "Clear"
Button btnClear = new Button
{
Text = "Clear",
Name = "btnClear",
Location = new Point(416, 17)
};
p.Controls.Add(btnClear);
btnClear.Click += new EventHandler(buttonClear_Click);
So what should i write in the following method to have the effect of removing e.g. the second panel that was added in the first part of code I have written?
void buttonClear_Click(object sender, EventArgs e)
{
throw new NotImplementedException();
}
EDIT
the code for creating my panel is
var p = new Panel
{
BorderStyle = BorderStyle.FixedSingle ,
Size = new Size(506,100),
Name = "notifyPanel"
};
and the code for creating my FlowLayoutPanel is
var nFlowPanel = new FlowLayoutPanel
{
FlowDirection = FlowDirection.TopDown,
WrapContents = false,
AutoScroll = true,
Size = new Size(530, 377),
Location = new Point(13, 145)
};
and the code for my button clear is
void buttonClear_Click(object sender, EventArgs e)
{
var button = (Control)sender;
var panel = button.Parent.Controls["notifyPanel"];
panel.Dispose();
}
however it gives the error
Object reference not set to an instance of an object.
on the panel.Dispose() line.
anyone can help?
The Controls.Remove() method is very dangerous, it doesn't dispose the control. Which will live on, moved to the so-called parking window, using up both Windows and managed resources. After a bit less than 10,000 times doing this it crashes your program when Windows is no longer willing to let you create any more windows.
Call the control's Dispose() method instead. That also automatically removes the control from its container.
void buttonClear_Click(object sender, EventArgs e)
{
var panel = nFlowPanel.Controls["notifyPanel"];
panel.Dispose();
}
You can do it like this:
nFlowPanel.Controls.Remove((sender as Button).Parent);
I will suggest you to use List for this. Before adding Panels in the FlowLayoutpanel, add them in the List. Then just remove the indexed panel from the flowlayoutpanel.
Panel pnlTemp = (panel)list[index];
nFlowPanel.Controls.Remove(pnlTemp);
To get the index of the button you have to add your buttons also to your list and after clicking any button, search the button in the list and get the index of the button where it is saved in the list. If my code is unclear, let me know. but I feel your task is that complex. I am not sure but this link may be of some help.
Hope it helps.