TLDR; Looking for a method to retrieve all radiobuttons by means of something like this... (psudo)
List<RadioButton> btn = new List<RadioButton>;
btn = stackPanel.getAllRadioButtons()
I am currently building a little quiz application in C#. I have functions that add the required GUI elements to a groupbox. I would like to know if there is any way that I can loop through the created elements (for instance radio buttons) to see which are checked.
Here is one of the functions along with how they are added to the window.
private void tfQ(string questionBody)
{
StackPanel questionPanel = new StackPanel{Orientation = Orientation.Vertical};
questionPanel.Children.Add(new Label { Content = questionBody });
GroupBox group = new GroupBox();
RadioButton trueRadio = new RadioButton();
trueRadio.Content = "True";
RadioButton falseRadio = new RadioButton();
falseRadio.Content = "False";
questionPanel.Children.Add(trueRadio);
questionPanel.Children.Add(falseRadio);
group.Content = questionPanel;
mainStack.Children.Add(group);
}
Constructor:
public quiz()
{
tfQ("This is a true/false question");
Window w = new Window();
w.Content = mainStack;
w.Show();
}
I have found many ways to do it in the C# scripting format
(using the Control function ...)
var checkedButton = container.Controls.OfType<RadioButton>()
.FirstOrDefault(r => r.Checked);
but I have not yet found a "programmatic" way of doing it. I have considered changing the type of void tfQ to a StackPanel, but that only helps me to easier loop through the stack panels, althought that only partly solves me problem - allows me to easier loop through the StackPanels but I still don't know how to get the RadioButtons on a panel.
P.S I am very new to C# - with experience in Java/C/C++/Python
I made use of the following code to cast the content into a new groupbox and stackpanel, respectively.
if (child is GroupBox)
{
if ((child as GroupBox).Content is StackPanel)
{
StackPanel d = (StackPanel)((GroupBox)child).Content;
}
}
This allowed me to cast the content into a local copy of a control. It may not be the best way - I'm sure it is very inefficient to be honest. Solved the problem.
Related
First, a little overview of how my current UI looks like:
Note that other than the stuff in the ToolStripControlHost, everything else is standard WinForms.
In short, I want to have something similar to the ToolStripControlHost but I need it "pinned" to the bottom of the menu, mostly so that when there is a lot of items, it is not scrolled like the rest of the menu items.
After some searching around I came to the conclusion that maybe customizing painting might be the solution, don't know if this is the case though.
Here's some sample code, but I'm not sure how useful it is:
public ToolStripDropDownButtonContainer(ToolStripDropDownButton button)
{
this.UIControl = button.GetCurrentParent();
this.Button = button;
if (this.Button.Tag == null)
{
this.Button.Tag = true;
this.Button.DropDownDirection = ToolStripDropDownDirection.AboveLeft;
ToolStripDropDownMenu menu = (ToolStripDropDownMenu)this.Button.DropDown;
menu.SuspendLayout();
try
{
menu.BackColor = Color.White;
menu.ShowImageMargin = false;
menu.ShowCheckMargin = false;
menu.AutoSize = true;
menu.Margin = Padding.Empty;
menu.Padding = Padding.Empty;
menu.GripMargin = Padding.Empty;
menu.GripStyle = ToolStripGripStyle.Hidden;
menu.MinimumSize = new Size(310, 0);
menu.MaximumSize = menu.MinimumSize;
// TODO pin panel (or some control) to the bottom-side of the menu
}
finally
{
menu.ResumeLayout();
}
}
}
My solution to this problem is to completely avoid using the normal menu control containment system and instead have the menu show one FlowLayoutPanel which instead contains my menu items.
This involved having to add some various trickeries to get the panel to behave nicely with the UI. The added advantage of this approach is more flexibility and control over the system.
On the downside, I noticed a degrade in performance when I have a tonne of sub-items, but I'll investigate this separately.
I am creating a winforms app which generates a number of panels at runtime. I want each panel, when clicked, to open a web link.
The panels are generated at runtime:
for (int i = 0; i < meetings.Count(); i++) {
Panel door = new Panel();
door.Location = new System.Drawing.Point((i * 146) + (i * 10) + 10, 10);
door.Size = new System.Drawing.Size(146, 300);
door.BackgroundImage = ConferenceToolkit.Properties.Resources.Door;
door.Click += new EventHandler(door_Click);
Controls.Add(door);
}
and I want the event handler to point to a URL that is stored somehow in the Panel attributes. (On a web form I could use Attributes["myAttribute"] but this doesn't seem to work with WinForms):
private void door_Click(object sender, EventArgs e)
{
Panel p = sender as Panel;
Process.Start(p.Attributes["url"]);
}
There are many options for this, you may store URL in the (unused in Panel) Text property:
door.Text = FindUrl(meetings[i]);
Used like:
Process.Start(p.Text);
As alternative you may use general purpose Tag property:
door.Tag = FindUrl(meetings[i]);
With:
Process.Start(p.Tag.ToString());
Tag property is usually right place for these things and, becauase it's of type object, you can even use it to store complex types (in case you need more than a simple string).
See also similar posts for slightly more complex cases: this, this and this.
You can store the URL that you want in the Panel's Tag propertie
for example
p.Tag = "www.google.com";
and then you can use it when use cast the Panel in the on click method
reference for the .Tag property
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.tag(v=vs.110).aspx
I am semi new to C# and have made multiple instances of a User Control as such
StackPanel StkPnl = new StackPanel();
StkPnl = SP; //SP being the static objRef of the StackPanel in the MainWindow
for (int i = 0; i < 5; i++)
{
UserControl UsrCtrl = new UserControl();
UsrCtrl = new UserControl1();
UserControl1.TB.Text = "Text:"+i; //TB being the static objRef of the textblock that is in the user control
UsrCtrl.Name = "UsrCtrl" + i;
StkPnl.Children.Add(UsrCtrl);
}
What I am wanting to do is be able to call each user control i made independently and edit that specific textblock.text. eg: edit UsrCtrl3's textblock without altering the other 4 user controls created.
I found it kinda hard to explain myself. Let me know if you need any clarification and ill try to do my best to explain.
You can access a specific element in a collection using the index like this:
StkPnl.Children[3].TB.Text = "Some Text"; // edit fourth user control (UsrCtrl3)
Or you can use LINQ to search on the name.
The method SingleOrDefault() expects at most one match. If no match is found, it returns null.
var thirdUserControl = StkPnl.Children.SingleOrDefault(x => x.Name == "UsrCtrl3");
if (thirdUserControl != null)
thirdUserControl.TB.Text = "Some Text";
I've found similar answers to my question before, but not quite to what I'm trying to do...
In Visual Basic (last I used it, in 06/07) there was an "Index" property you could assign to multiple controls with the same name. I used this primarily to loop through controls, i.e.:
For i = 1 to 500
picSeat(i).Print "Hello"
Next i
Is there a way to do this in C#? I know there is a .IndexOf(), but would that really help for what I'm doing? I want to have multiple controls with the same name, just different index.
This is a Windows Form Application, and I'm using Visual Studio 2012. I am talking about controls, not arrays/lists; this was possible in VB and I was wondering if it was possible at all in C#. So I want to have, say, 30 seats in a theatre. I want to have each seat represented by a picturebox named "picSeat". VB would let me name several objects the exact same, and would assign a value to a control property "Index". That way, I could use the above loop to print "Hello" in every picture box with only 3 lines of code.
No, this feature does not exist in C#, and was never implemented in the transition from classic VB to VB.Net.
What I normally do instead is put each of the controls in question in a common parent container. The Form itself can work, but if you need to distinguish these from others of the same type a GroupBox or Panel control will work, too. Then, you access the controls like this:
foreach (var picBox in parentControl.Controls.OfType<PictureBox>())
{
// do something with each picturebox
}
If you want to use a specific control, just write by name:
pictureBox6.SomeProperty = someValue;
If you need to change a specific control determined at run-time, normally this is in response to a user event:
void PictureBox_Click(object sender, EventArgs e)
{
var picBox = sender As PictureBox;
if (picBox == null) return;
//picBox is now whichever box was clicked
// (assuming you set all your pictureboxes to use this handler)
}
If you really really want the Control Arrays feature, you can do it by adding code to create the array to your form's Load event:
PictureBox[] pictureBoxes = Me.Controls.OfType<PictureBox>().ToArray();
Are we talking WinForms here? I'm not sure, but I don't think you can have multiple controls in winforms with same name. But I vaguely recall doing something similar and the solution was to name them Button_1, Button_2 etc. Then you can iterate through all controls and get your own index.
Beware though that if you want to instanciate a separate control for each seat in a theatre, you might run into some serious performance issues :) I've done something similar to that as well and ended up drawing the whole thing on a canvas and using mouse coordinates to handle the events correctly.
You may want to check out the Uid property of controls.
(http://msdn.microsoft.com/en-us/library/system.windows.uielement.uid(v=vs.110).aspx)
You can access Control through Uid property with the following
private static UIElement FindUid(this DependencyObject parent, string uid)
{
var count = VisualTreeHelper.GetChildrenCount(parent);
if (count == 0) return null;
for (int i = 0; i < count; i++)
{
var el = VisualTreeHelper.GetChild(parent, i) as UIElement;
if (el == null) continue;
if (el.Uid == uid) return el;
el = el.FindUid(uid);
if (el != null) return el;
}
return null;
}
And simply use
var control = FindUid("someUid");
I copied code from this post
If you create an indexed dictionary of your user control, it will behave pretty much the same as in VB6, though you'll not see it on the VS C# GUI. You'll have to get around the placement issues manually. Still - and most importantly -, you'll be able to refer to any instance by the index.
The following example is for 3 pieces for clarity, but of course you could automate every step of the process with appropriate loops.
public partial class Form1 : Form
{
...
Dictionary<int, UserControl1> NameOfUserControlInstance = new Dictionary<int, UserControl1>()
{
{ 1, new UserControl1 {}},
{ 2, new UserControl1 {}},
{ 3, new UserControl1 {}}
};
private void Form1_Load(object sender, EventArgs e)
{
NameOfUserControlInstance[1].Location = new System.Drawing.Point(0, 0);
NameOfUserControlInstance[2].Location = new System.Drawing.Point(200, 0);
NameOfUserControlInstance[3].Location = new System.Drawing.Point(400, 0);
Controls.Add(NameOfUserControlInstance[1]);
Controls.Add(NameOfUserControlInstance[2]);
Controls.Add(NameOfUserControlInstance[3]);
}
...
}
I like using Tags to apply any type of meta data about the controls
for (int i = 0; i< 10; ++i)
{
Button button = new Button();
button.Tag = i;
}
I have two panels occupying the same space in one side of a splitviewcontainer.
I want to dynamically show or hide one panel or the other.
Approaches I've tried so far are:
if (treeView1.SelectedNode.Name.Contains("cat")) {
menuItemPanel.SendToBack();
foreach (Control control in menuItemPanel.Controls)
control.SendToBack();
menuItemPanel.Visible = false;
categoryPanel.Hide();
categoryPanel.Visible = true;
categoryPanel.BringToFront();
foreach (Control control in categoryPanel.Controls)
control.BringToFront();
categoryPanel.Show();
// ...
}
and the converse for the else case.
None of these seem to work. That is the categoryPanel is shown when a category node is selected, but the screen shows blank space when a menuItem node is selected.
What am I doing wrong?
The code doesn't make much sense, re-ordering the controls in the panels is not what you want to do. Also sounds that the code in the else clause is failing, code you didn't post. Do it like this instead (a guess):
bool categorySelected = treeView1.SelectedNode.Name.Contains("cat");
menuItemPanel.Visible = !categorySelected;
categoryPanel.Visible = categorySelected;
try this
if (treeView1.SelectedNode.Name.Contains("cat"))
{
menuItemPanel.Visible = false;
categoryPanel.Visible = true;
}