a way to access "code generated" labels in C# winforms [duplicate] - c#

I have a ToolStripMenuItem called myMenu. How can I access this like so:
/* Normally, I would do: */
this.myMenu... etc.
/* But how do I access it like this: */
String name = myMenu;
this.name...
This is because I am dynamically generating ToolStripMenuItems from an XML file and need to reference MenuItems by their dynamically generated names.

Use the Control.ControlCollection.Find method.
Try this:
this.Controls.Find()

string name = "the_name_you_know";
Control ctn = this.Controls[name];
ctn.Text = "Example...";

Assuming you have the menuStrip object and the menu is only one level deep, use:
ToolStripMenuItem item = menuStrip.Items
.OfType<ToolStripMenuItem>()
.SelectMany(it => it.DropDownItems.OfType<ToolStripMenuItem>())
.SingleOrDefault(n => n.Name == "MyMenu");
For deeper menu levels add more SelectMany operators in the statement.
if you want to search all menu items in the strip then use
ToolStripMenuItem item = menuStrip.Items
.Find("MyMenu",true)
.OfType<ToolStripMenuItem>()
.Single();
However, make sure each menu has a different name to avoid exception thrown by key duplicates.
To avoid exceptions you could use FirstOrDefault instead of SingleOrDefault / Single, or just return a sequence if you might have Name duplicates.

Control GetControlByName(string Name)
{
foreach(Control c in this.Controls)
if(c.Name == Name)
return c;
return null;
}
Disregard this, I reinvent wheels.

Using the same approach of Philip Wallace, we can do like this:
public Control GetControlByName(Control ParentCntl, string NameToSearch)
{
if (ParentCntl.Name == NameToSearch)
return ParentCntl;
foreach (Control ChildCntl in ParentCntl.Controls)
{
Control ResultCntl = GetControlByName(ChildCntl, NameToSearch);
if (ResultCntl != null)
return ResultCntl;
}
return null;
}
Example:
public void doSomething()
{
TextBox myTextBox = (TextBox) this.GetControlByName(this, "mytextboxname");
myTextBox.Text = "Hello!";
}
I hope it help! :)

this.Controls.Find(name, searchAllChildren) doesn't find ToolStripItem because ToolStripItem is not a Control
using SWF = System.Windows.Forms;
using NUF = NUnit.Framework;
namespace workshop.findControlTest {
[NUF.TestFixture]
public class FormTest {
[NUF.Test]public void Find_menu() {
// == prepare ==
var fileTool = new SWF.ToolStripMenuItem();
fileTool.Name = "fileTool";
fileTool.Text = "File";
var menuStrip = new SWF.MenuStrip();
menuStrip.Items.Add(fileTool);
var form = new SWF.Form();
form.Controls.Add(menuStrip);
// == execute ==
var ctrl = form.Controls.Find("fileTool", true);
// == not found! ==
NUF.Assert.That(ctrl.Length, NUF.Is.EqualTo(0));
}
}
}

One of the best way is a single row of code like this:
In this example we search all PictureBox by name in a form
PictureBox[] picSample =
(PictureBox)this.Controls.Find(PIC_SAMPLE_NAME, true);
Most important is the second paramenter of find.
if you are certain that the control name exists you can directly use it:
PictureBox picSample =
(PictureBox)this.Controls.Find(PIC_SAMPLE_NAME, true)[0];

You can use find function in your Form class. If you want to cast (Label) ,(TextView) ... etc, in this way you can use special features of objects. It will be return Label object.
(Label)this.Controls.Find(name,true)[0];
name: item name of searched item in the form
true: Search all Children boolean value

this.Controls["name"];
This is the actual code that is ran:
public virtual Control this[string key]
{
get
{
if (!string.IsNullOrEmpty(key))
{
int index = this.IndexOfKey(key);
if (this.IsValidIndex(index))
{
return this[index];
}
}
return null;
}
}
vs:
public Control[] Find(string key, bool searchAllChildren)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentNullException("key", SR.GetString("FindKeyMayNotBeEmptyOrNull"));
}
ArrayList list = this.FindInternal(key, searchAllChildren, this, new ArrayList());
Control[] array = new Control[list.Count];
list.CopyTo(array, 0);
return array;
}
private ArrayList FindInternal(string key, bool searchAllChildren, Control.ControlCollection controlsToLookIn, ArrayList foundControls)
{
if ((controlsToLookIn == null) || (foundControls == null))
{
return null;
}
try
{
for (int i = 0; i < controlsToLookIn.Count; i++)
{
if ((controlsToLookIn[i] != null) && WindowsFormsUtils.SafeCompareStrings(controlsToLookIn[i].Name, key, true))
{
foundControls.Add(controlsToLookIn[i]);
}
}
if (!searchAllChildren)
{
return foundControls;
}
for (int j = 0; j < controlsToLookIn.Count; j++)
{
if (((controlsToLookIn[j] != null) && (controlsToLookIn[j].Controls != null)) && (controlsToLookIn[j].Controls.Count > 0))
{
foundControls = this.FindInternal(key, searchAllChildren, controlsToLookIn[j].Controls, foundControls);
}
}
}
catch (Exception exception)
{
if (ClientUtils.IsSecurityOrCriticalException(exception))
{
throw;
}
}
return foundControls;
}

Assuming you have Windows.Form Form1 as the parent form which owns the menu you've created. One of the form's attributes is named .Menu. If the menu was created programmatically, it should be the same, and it would be recognized as a menu and placed in the Menu attribute of the Form.
In this case, I had a main menu called File. A sub menu, called a MenuItem under File contained the tag Open and was named menu_File_Open. The following worked. Assuming you
// So you don't have to fully reference the objects.
using System.Windows.Forms;
// More stuff before the real code line, but irrelevant to this discussion.
MenuItem my_menuItem = (MenuItem)Form1.Menu.MenuItems["menu_File_Open"];
// Now you can do what you like with my_menuItem;

Since you're generating them dynamically, keep a map between a string and the menu item, that will allow fast retrieval.
// in class scope
private readonly Dictionary<string, ToolStripMenuItem> _menuItemsByName = new Dictionary<string, ToolStripMenuItem>();
// in your method creating items
ToolStripMenuItem createdItem = ...
_menuItemsByName.Add("<name here>", createdItem);
// to access it
ToolStripMenuItem menuItem = _menuItemsByName["<name here>"];

Have a look at the ToolStrip.Items collection. It even has a find method available.

You can do the following:
private ToolStripMenuItem getToolStripMenuItemByName(string nameParam)
{
foreach (Control ctn in this.Controls)
{
if (ctn is ToolStripMenuItem)
{
if (ctn.Name = nameParam)
{
return ctn;
}
}
}
return null;
}

A simple solution would be to iterate through the Controls list in a foreach loop. Something like this:
foreach (Control child in Controls)
{
// Code that executes for each control.
}
So now you have your iterator, child, which is of type Control. Now do what you will with that, personally I found this in a project I did a while ago in which it added an event for this control, like this:
child.MouseDown += new MouseEventHandler(dragDown);

Related

Creating a class that loops through textboxes and labels making them visible in winform

I am very new to c# and visual studio.
I am using c# with Visual studio. I want to create a method that lops through a number of textboxes and labels and set their visible control to "True."
This is the code I have come up with so far, but it does not work.
public static void showFields(params string[] values)
{
foreach (var value in values)
{
value.Visible = true;
}
}
Any help would be greatly appreciated.
You are on the right path, just need to replace string with Control, by the way, string does not have the Visible property.
public static void showFields(params Control[] values)
{
foreach (var value in values)
{
value.Visible = true;
}
}
Code should be similar to this. You may have nested controls. In this case, you create a recursive method
private void MakeThemVisible(Control topControl)
{
foreach (var c in topControl.Controls)
{
if (c is TextBox txt && <your second criteria>) // <-- pattern matching
{
// <---- txt specific code here -------
txt.Visible = true;
continue;
}
else if (c is Label lbl && <your second criteria>) // <-- pattern matching
{
// <---- lbl specific code here -------
lbl.Visible = true;
continue;
}
MakeThemVisible(c);
}
}
Your form is also control
If you already have a list of needed controls in the form of array - Control[] values, you can use LINQ
values.ToList().ForEach(c => {c.Visible = true;});

Capture RichTextBlock Hyperlink events

I'm trying to capture the click event of Hyperlinks inside a dynamically generated RichTextBlock.
I'm dynamically generating the contents of a richtextblock and then applying them with XamlReader. The content can vary quite a bit, so I can't manually parse the xaml and hook up events at that point.
My basic idea is to, once the richtextblock is loaded, find all Hyperlinks in it and hook up their click event there. This is my current code:
public class HookUpEvents()
{
foreach (var child in FindVisualChildren<Hyperlink>(richtxtblock))
{
child.Click += MyFunction;
}
}
public static IEnumerable<T> FindVisualChildren<T>(DependencyObject depObj) where T : DependencyObject
{
if (depObj != null)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(depObj, i);
if (child != null && child is T)
{
yield return (T)child;
}
foreach (T childOfChild in FindVisualChildren<T>(child))
{
yield return childOfChild;
}
}
}
}
Obviously, it isn't working. It looks like the FindVisualChildren function isn't returning any Hyperlinks. Any ideas on how I can achieve this?
Well, I'm sure late to the party, but RichTextBlock won't place his Blocks and their Inlines in VisualTree most of the time. To find all Inline-based elements (Run,Span,Bold etc.) you will need to loop through all content, by visiting each Block and subsequent Inline's. I would suggest something like this:
public static IEnumerable<T> GetAllTextElements<T>(this RichTextBlock rtb) where T : TextElement
{
var result = new List<T>();
var blocks = rtb.Blocks;
foreach (var block in blocks)
{
if (block is T)
{
result.Add(block as T);
continue;
}
var inlines = ((Paragraph)block).Inlines;
var res = TraverseInline<T>(inlines);
if (res != null && res.Any())
result.AddRange(res);
}
return result;
}
private static IEnumerable<T> TraverseInline<T>(IEnumerable<Inline> inlines) where T : TextElement
{
var result = new List<T>();
foreach (var item in inlines)
{
if (item is T)
{
result.Add(item as T);
continue;
}
else if (item is Span) // first Inline derived class to have own `Inlines`
{
var spanItem = item as Span;
var spanInlines = spanItem.Inlines;
var results = TraverseInline<T>(spanInlines);
if (results != null && results.Any())
result.AddRange(results);
}
}
return result;
}
So you can look for any TextElement-derived item with it.
Usage would be something like:
var textHyperlinks = myRichTextBlock.GetAllTextElements<Hyperlink>();
This will do as far as you don't use InlineUIContainer. That type of Inline behaves differently, as you can put anything UIElement-based as it's Child property. In that case your initial approach should work.
There's a couple of things here:
If you're trying to find the hyperlink inside of the RichTextBlock, its type is: Windows.UI.Xaml.Documents.Hyperlink. Not the type of the HyperLinkButton.
You can put the Click event handler in your text and then provide the handler method in your code behind file. If you dynamically generate text that looks like:
<Paragraph>
Text with a
<Hyperlink x:Name="link" Click="link_Click">link.</Hyperlink>
</Paragraph>
Feed that to the XamlReader, and put the following code in your code behind file:
private void link_Click(Windows.UI.Xaml.Documents.Hyperlink sender, Windows.UI.Xaml.Documents.HyperlinkClickEventArgs args)
{
Debug.WriteLine("Handle link click, by: " + sender.Name);
}
Then it should connect up correctly at runtime. And you can do whatever you want on the Click event handler. Even if there are multiple links, you can name them differently and just use one click handler to process.

Foreach loop through controls that are textbox doesn't return

I've used this code before in another program, but now I'm having trouble understanding why it won't run the code after my second line.
foreach (Control c in Controls)
if (c.GetType() == typeof(TextBox)) //doesn't run any further
{
if ((string)c.Tag == "Filled")
{
...
}
...
}
I'm either missing some minor little detail or something else is incorrect. Any ideas?
EDIT: my textboxes are inside a panel.
It might be simpler to do this:
foreach ( TextBox tb in this.Controls.OfType<TextBox>())
{
if ((string)tb.Tag == "Filled")
// .....
}
When you call Control.Controls, it will only return the controls at the outermost level. It won't recursively descend into any container controls that hold other controls.
If your controls are in another container, you will need to use that container's .Controls property instead.
Alternatively you can generalize it by writing a method to recursively return all the controls from the parent and all it's children, like so:
public IEnumerable<Control> AllControls(Control container)
{
foreach (Control control in container.Controls)
{
yield return control;
foreach (var innerControl in AllControls(control))
yield return innerControl;
}
}
You can then use that instead of Control.Controls as follows:
private void test() // Assuming this is a member of a Form other class derived from Control
{
var textboxesWithFilledTag =
AllControls(this).OfType<TextBox>()
.Where(tb => (string) tb.Tag == "Filled");
foreach (var textbox in textboxesWithFilledTag)
Debug.WriteLine(textbox.Text);
}
As the comment says, I'm assuming that the test() method is a member of your Form or another class derived from Control. If it isn't, you will have to pass the parent control to it:
private void test(Control container)
{
var textboxesWithFilledTag =
AllControls(container).OfType<TextBox>()
.Where(tb => (string) tb.Tag == "Filled");
foreach (var textbox in textboxesWithFilledTag)
Debug.WriteLine(textbox.Text);
}
The following method has identical results to the one above, for reference (and is more readable IMHO):
private void test(Control container)
{
foreach (var textbox in AllControls(container).OfType<TextBox>())
if ((string)textbox.Tag == "Filled")
Debug.WriteLine(textbox.Text);
}
For your code, your button click handler might look something like this:
void button1_Click(object sender, EventArgs e)
{
foreach (var c in AllControls(this).OfType<TextBox>())
{
if ((string) c.Tag == "Filled")
{
// Here is where you put your code to do something with Textbox 'c'
}
}
}
Note that you also need the AllControls() method, of course.
To get all controls (not only the direct children of the form) you can use this recursive Linq
Func<Control, IEnumerable<Control>> allControls = null;
allControls = c => new Control[] { c }
.Concat(c.Controls.Cast<Control>()
.SelectMany(x=>allControls(x)));
Now you can filter the TextBoxes
var tbs = allControls(this).OfType<TextBox>()
.Where(t=>(string)t.Tag=="Filled")
.ToList();
Better use if (c is TextBox).
Furthermore, if you want to know why your code breaks, use try/catch
I'd recommend to use following syntax:
foreach (Control c in Controls)
if (c is TextBox)
Are you setting tag property from yourself. This is a string type of property.so you can try this:
if (c.Tag == "Filled")
{
Console.WriteLine(c.Name);
}
if you want to check that text box is not empty then you can simply try this :
if (c.Text.Trim().Length == 0)
{
Console.WriteLine(c.Name);
}

Generic way to get the values from multiple controls

I have a Form with multiple different controls like ComboBox, TextBox and CheckBox. I am looking for a generic way to get values from these controls while looping over them.
For example, something like this:
foreach(Control control in controls)
{
values.Add(control.Value);
}
Is it possible or do I need to treat each control separately?
Try this:
Panel myPanel = this.Panel1;
List<string> values = new List<string>();
foreach (Control control in myPanel.Controls)
{
values.Add(control.Text);
}
But make sure you get only the controls you want. You can check the type just like
if(control is ComboBox)
{
// Do something
}
The Text solution is OK if every Control is a TextBox, but if you have some Label you'll end up with the text of the labels among the values, unless you fill your code with if's. A better solution could be to define a set of delegates that for each kind of Control return what is considered the value (e.g. Text for the TextBox and Checked for the CheckBox), put them in a dictionary, and use them to get the value for each control. The code could be something like this:
public delegate object GetControlValue(Control aCtrl);
private static Dictionary<Type, GetControlValue> _valDelegates;
public static Dictionary<Type, GetControlValue> ValDelegates
{
get
{
if (_valDelegates == null)
InitializeValDelegates();
return _valDelegates;
}
}
private static void InitializeValDelegates()
{
_valDelegates = new Dictionary<Type, GetControlValue>();
_valDelegates[typeof(TextBox)] = new GetControlValue(delegate(Control aCtrl)
{
return ((TextBox)aCtrl).Text;
});
_valDelegates[typeof(CheckBox)] = new GetControlValue(delegate(Control aCtrl)
{
return ((CheckBox)aCtrl).Checked;
});
// ... other controls
}
public static object GetValue(Control aCtrl)
{
GetControlValue aDel;
if (ValDelegates.TryGetValue(aCtrl.GetType(), out aDel))
return aDel(aCtrl);
else
return null;
}
Then you can write:
foreach (Control aCtrl in Controls)
{
object aVal = GetValue(aCtrl);
if (aVal != null)
values.Add(aVal);
}

Accessing a UserControls methods from its object

I have created a UserControl called AutorControl with a method to clear its textbox:
public void LimpiarAutorTextbox()
{
textBox1.Text = "";
}
Then my intention is from another form with a Panal, using a for loop add X ammount of the above user control. Then I want to call the UserControls method: "LimpiarAutorTextbox" (which is just a method for clearing the text of the textbox) using a foreach loop like this, however it's not working. I'm not sure what to do in this case:
AutorControl usercontrolAutorControl = new AutorControl();
private override void ClearControls()
{
txtTitulo.Text = "";
//Panel1 will only hold controls of the same type: "AutorControl"
foreach (Control X in panel1.Controls)
{
X as AutorControl;//?????? I want to access each created usercontrols' method.
}
}
The panel will always hold a usercontrol of AutorControl, never anything else. How can I achieve this programatically?
Thanks.
Your line here is fine:
X as AutorControl
just add:
(X as AutorControl).LimpiarAutorTextbox()
that should do the trick.
Also, I know you said that there would only be AutorControls in there, but you may want to do something more like this:
AutorControl currentControl == X as AutorControl;
if (AutorControl != null)
{
currentControl.LimpiarAutorTextbox();
}
Or alternatively, you can change your declaration of the for foreach loop to do the cast for you:
foreach(AutorControl currentControl in form.Controls)
{
if (currentControl != null)
{
currentControl.LimpiarAutorTextbox();
}
}
Some alternatives :)

Categories

Resources