I create a label "label1" dynamically in a method. Then when I click a button I want to remove that label created but if I write Controls.Remove(label1) it says that the control doesn't exist in the context.
How could I do to achieve this?
EDIT: Following Jon suggestion I implemented the foreach loop but it doesn't do anything. This is my code, the panel which I use is created by design:
void GenerateControls() {
Label labelOne = new Label();
Button btnContinue = new Button();
panel.SuspendLayout();
SuspendLayout();
//btnContinue
btnContinue.BackColor = System.Drawing.Color.Black;
btnContinue.ForeColor = System.Drawing.SystemColors.Menu;
btnContinue.Location = new System.Drawing.Point(145, 272);
btnContinue.Name = "btnContinue";
btnContinue.Size = new System.Drawing.Size(95, 28);
btnContinue.TabIndex = 13;
btnContinue.Text = "Continue";
btnContinue.Visible = true;
Controls.Add(btnContinue);
btnContinue.Click += new System.EventHandler(btnContinue_Click);
//labelOne
labelOne.Location = new Point(0,65);
labelOne.Size = new System.Drawing.Size(100,20);
labelOne.Text = "labelOne";
labelOne.Name = "labelOne";
labelOne.Visible = true;
labelOne.TextChanged += new System.EventHandler(this.lbl_TextChanged);
labelOne.BackColor = System.Drawing.Color.PaleGreen;
Controls.Add(labelOne);
//panel
panel.Controls.Add(labelOne);
panel.Visible = true;
panel.Location = new Point(0,0);
panel.Size = new Size(240, 320);
//
Controls.Add(panel);
panel.ResumeLayout();
ResumeLayout();
}
And then in when I click on btnContinue:
private void btnContinuar_Click(object sender, EventArgs e) {
foreach (Control control in panel.Controls) {
if (control.Name == "labelOne"){
panel.Controls.Remove(control);
break;
}
}
}
I debug it and in the panel.Control it continues as if it were empty panel.
Thanks for your help!
I suspect it says the variable doesn't exist in that context. You'll have to find the label by its text, or knowing something else about it. For example, when you create it you could set the Name property and find it by that when you want to remove it:
panel.Controls.RemoveByKey("YourLabelName");
EDIT: As noted in the comments, RemoveByKey doesn't exist in the compact framework. So you'd either have to remember the reference yourself (in which case you don't need the name) or use something like:
foreach (Control control in panel.Controls)
{
if (control.Name == "YourLabelName")
{
panel.Controls.Remove(control);
break;
}
}
EDIT2: And to make it even more "generic" and desktop compatible, you could keep the RemoveByKey call and add this to your app:
public static class FormExtensions
{
public static void RemoveByKey(this Control.ControlCollection collection,
string key)
{
if(!RemoveChildByName(collection, key))
{
throw new ArgumentException("Key not found");
}
}
private static bool RemoveChildByName(
this Control.ControlCollection collection,
string name)
{
foreach (Control child in collection)
{
if (child.Name == name)
{
collection.Remove(child);
return true;
}
// Nothing found at this level: recurse down to children.
if (RemoveChildByName(child.Controls, name))
{
return true;
}
}
return false;
}
}
After 20 edits to the OP question, and Jon's answer with no resemblance to the original problem, you are left with one small glitch.
Your Not adding labelOne to the panel you are adding it to the Form.
Change
Controls.Add(labelOne);
to
panel.Controls.Add(labelOne);
Then everything should work
Related
I am facing issue in tabcontrol of Windows application.
We have a tabcontrol on 1 windows form on which first tab is by default and other tabs we are adding dynamically at runtime.
In first case while we opening the form, we able to see all the controls in both the tabs i.e. (Default and other tabs)
Although, when we try opening the form for second time, data is being getting added into the form if we debug but while showing, it shows blank in other tabs which are getting added dynamically.
In CustomgroupControl class, i am adding controls dynamically like textbox, dropdown, grid and etc. Although this is working in first case, but not showing controls in second case. (Flow of code is same for both the cases)
Below is attached screenshot in link and shown sample source code
Screenshot for above query
public void renderTabpage(Dictionary<string, List<Field>> subHeaderMap, TabPage currentTabPage, AsynchTabRenderer asynchTabRenderer, DoWorkEventArgs e)
{
currentTabPage.SuspendLayout();
TableLayoutPanel headerTableLayout = createNewheaferTableLayoutPanel(currentTabPage.Name + TABLE_LAYOUT_NAME_SUFFIX);
currentTabPage.Controls.Add(headerTableLayout);
headerTableLayout.ColumnCount = 1;
headerTableLayout.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100F));
headerTableLayout.RowCount = 0;
headerTableLayout.SuspendLayout();
foreach (string subHeaderName in subHeaderMap.Keys)
{
if (asynchTabRenderer != null && asynchTabRenderer.CancellationPending == true)
{
e.Cancel = true;
return;
}
else
{
CustomGroupControl customGroupControl = createAndPopulateSubheaderGroupControl(subHeaderMap[subHeaderName], subHeaderName);
headerTableLayout.RowCount = headerTableLayout.RowCount + 1;
headerTableLayout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
headerTableLayout.Controls.Add(customGroupControl, 0, ++headerTableLayout.RowCount);
}
}
headerTableLayout.ResumeLayout(false);
headerTableLayout.PerformLayout();
currentTabPage.ResumeLayout(false);
currentTabPage.PerformLayout();
}
private void detailsTabControl_TabIndexChanged(object sender, EventArgs e)
{
tabIndexChangedEvent(((TabControl)sender).SelectedTab);
}
private void tabIndexChangedEvent(TabPage tabPage)
{
try
{
//WordApp.ScreenUpdating = false;
//WordApp.System.Cursor = Word.WdCursorType.wdCursorWait;
if (tabPage.Name == TabName.Replace(" ", "_").ToUpper())
{
FileUtilService service = FileUtilFactory.getInstance();
// service.clearIENetCache();
showForm(Window, forms);
}
//TabPage tabPage = ((TabControl)sender).SelectedTab;
if ((tabPage.Tag == null || true == (Boolean)tabPage.Tag) && !string.Equals(tabPage.Name, FieldConstants.TAB, StringComparison.CurrentCultureIgnoreCase))
{
var controls = WindowsFormControlUtils.GetControlHierarchy(tabPage);
foreach (Control control in controls)
{
if (control is CustomGroupControl)
{
CustomGroupControl customGroupControl = (CustomGroupControl)control;
customGroupControl.fieldRenderDelegater();
}
else
continue;
}
tabPage.Tag = false;
}
}
catch (Exception ex)
{
logger.Error(ex.Message);
}
}
internal TableLayoutPanel createNewheaferTableLayoutPanel(string headerName)
{
TableLayoutPanel subHeaderTableLayout = new TableLayoutPanel();
// resources.ApplyResources(subHeaderTableLayout, headerName.Replace(" ", "_").ToUpper());
subHeaderTableLayout.Name = headerName.Replace(" ", "_").ToUpper() + TABLE_LAYOUT_NAME_SUFFIX;
subHeaderTableLayout.Dock = DockStyle.Fill;
subHeaderTableLayout.AutoSize = true;
subHeaderTableLayout.AutoScroll = true;
subHeaderTableLayout.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowOnly;
subHeaderTableLayout.GrowStyle = TableLayoutPanelGrowStyle.AddRows;
return subHeaderTableLayout;
}
#Jimi & #Lucky
It works. Got the correct direction. Thanks.
Hint - Don't create controls on worker thread
Tried solution and worked - Removed creation of controls from parameterized constructor and added on form_load event.
So my program is generating a bunch of buttons like so:
foreach (var subdir in dir.GetDirectories()) {
var path = subdir.Name;
var button = new Button {
Text = getFlavor(path) + "\t(" + path + ")",
Width = Width,
Height = 35,
Top = y
};
button.Click += buttonClick;
Controls.Add(button);
if (button.Text.Contains("Kittens")
i++;
}
I want to try something like this
if (i == 1) {
[Button.ThatContains("Kitten)].Click;
}
"ThatContains" is not a real method. How do I get references to buttons I've created programmatically ?
You could use OfType<Button> to find all buttons in the container control where you've added them(f.e. a Panel). Then a liitle bit LINQ power gives you the correct button(s):
var kittenButtons = panel.Controls.OfType<Button>()
.Where(btn => btn.Text.Contains("Kittens"));
foreach(Button btn in kittenButtons)
btn.PerformClick();
If you just want to click the first:
Button kittenButton = panel.Controls.OfType<Button>()
.FirstOrDefault(btn => btn.Text.Contains("Kittens"));
if(kittenButton != null)
kittenButton.PerformClick();
For what it's worth, here is also an extension method that returns controls recursively via deferred execution which allows to use only the first found Buttton or consume all down the road:
public static IEnumerable<T> GetChildControlsRecursive<T>(this Control root) where T : Control
{
if (root == null) throw new ArgumentNullException("root");
var stack = new Stack<Control>();
stack.Push(root);
while (stack.Count > 0)
{
Control parent = stack.Pop();
foreach (Control child in parent.Controls)
{
if (child is T)
yield return (T)child;
stack.Push(child);
}
}
yield break;
}
Now you can use similar code as above to get for example the first matching button or all:
var kittenButtons = this.GetChildControlsRecursive<Button>()
.Where(b => b.Text.Contains("Kittens"));
// search just until the first button is found
Button firstKittenButton = kittenButtons.FirstOrDefault();
if(firstKittenButton != null) firstKittenButton.PerformClick;
// loop all
foreach(Button btn in kittenButtons)
btn.PerformClick();
Either create a subclass of Button to store the information you want and instantiate that instead or use the Tag property
public class MyButton : Button
{
public int ButtonID { get; set; }
}
public class MyApplication
{
public void DoSomething()
{
int i; // todo: loop stuff
var button = new MyButton
{
Text = getFlavor(path) + "\t(" + path + ")",
Width = Width,
Height = 35,
Top = y,
ButtonID = i
};
}
}
Or why not cast the sender parameter of the button click event as a Button and check the text?
public class MyApplication
{
public void DoSomething()
{
var b = new Button();
b.Click += b_Click;
}
public void b_Click(object sender, EventArgs e)
{
Button b = (Button)sender;
switch (b.Text) {
case "Kittens":
return;
default:
return;
}
}
}
Something like this
var button = FirstOrDefault(y => y is Button && y.Text.Contains("Kittens"));
if(button != null)
button.PerformClick();
In order to get the references, you may need to what you would do with getting references of any other type - store them somewhere, which does not seem to be the case here at all. Normally, you would register your buttons for interaction from a user by attaching them to a Form. Assuming you're not doing this by the looks of your sample code, I'm going to recommend storing them into a Dictionary<string, Button>.
You could use a dictionary or you could use a simple recursive loop (in case you are sticking the buttons into different containers).
private bool ClickButton(string buttonName, Control control) {
if (control is Button && control.Text.Contains(buttonName) {
((Button)control)PerformClick();
return true;
}
if (control.HasChildren) {
foreach (Control childControl in control.Controls) {
if (ClickButton(buttonName, childControl)) {
return true;
}
}
}
return false;
}
Usage: ClickButton("Kittens", this);
Or you could use a dictionary, as some have suggested.
private Dictionary<string, Button> DynamicButtons = new Dictionary<string, Button>();
private void ClickDictionaryButton(string buttonName) {
var matches = DynamicButtons.Where(x => x.Key.Contains(buttonName));
foreach (var match in matches) {
match.Value.PerformClick();
}
}
Usage: ClickDictionaryButton("Kittens", this);
I have a DataGrid that for some reason I have to declare as a global. everything seems to be working fine at first use. But when I go back to the form for another try, an object disposed exception is called. Is there anyway for me to prevent this? like dispose the public datagrid or something? Here is a sample of my code:
public static DataGrid dataGrid = new DataGrid();
public myForm()
{
InitializeComponent();
dataGrid.Location = pt;
dataGrid.Font.Name = "Tahoma";
dataGrid.Font.Size = 9;
dataGrid.BackgroundColor = Color.Azure;
dataGrid.GridLineColor = Color.Black;
dataGrid.ColumnHeadersVisible = false;
dataGrid.RowHeadersVisible = false;
dataGrid.PreferredRowHeight = 60;
this.Controls.Add(dataGrid);
dataGrid.Height = 524;
dataGrid.Width = 468;
dataGrid.CurrentCellChanged += new
EventHandler(dataGrid_CurrentCellChanged);
}
A Form (or indeed any Control) disposes its child controls when it is disposed. So what you're seeing is normal.
To achieve what you want, you'd need to remove the DataGrid from the Form's Controls collection before it is disposed.
UPDATE
As #ctacke says in comments, there are almost certainly alternatives that will avoid your needing to make the DataGrid static, but without more detail it's difficult to make a recommendation.
If you are going to have a static control, at least provide a wrapper for it so that you can catch and handle your issues.
Consider modifying your code to being something like below. Once you have your bugs ironed out, you can eliminate any of the stuff you don't really need.
private static DataGrid dataGrid;
private static myForm myInstance;
public myForm()
{
InitializeComponent();
myInstance = this; // set 'myInstance' before DataGrid1 stuff
DataGrid1.Height = 524;
DataGrid1.Width = 468;
DataGrid1.CurrentCellChanged += new EventHandler(dataGrid_CurrentCellChanged);
}
public static DataGrid DataGrid1 {
get {
try {
if ((myInstance == null) || myInstance.IsDisposed) {
throw new Exception("myForm is already disposed. No controls available.");
}
if ((dataGrid == null) || dataGrid.IsDisposed) {
dataGrid = new DataGrid();
dataGrid.Location = pt;
dataGrid.Font.Name = "Tahoma";
dataGrid.Font.Size = 9;
dataGrid.BackgroundColor = Color.Azure;
dataGrid.GridLineColor = Color.Black;
dataGrid.ColumnHeadersVisible = false;
dataGrid.RowHeadersVisible = false;
dataGrid.PreferredRowHeight = 60;
this.Controls.Add(dataGrid);
}
} catch (Exception err) {
Console.WriteLine(err); // put a breakpoint HERE
}
return dataGrid;
}
set {
try {
if ((myInstance == null) || myInstance.IsDisposed) {
throw new Exception("myForm is already disposed. No controls available.");
}
if ((dataGrid == null) || dataGrid.IsDisposed) {
dataGrid = new DataGrid();
dataGrid.Location = pt;
dataGrid.Font.Name = "Tahoma";
dataGrid.Font.Size = 9;
dataGrid.BackgroundColor = Color.Azure;
dataGrid.GridLineColor = Color.Black;
dataGrid.ColumnHeadersVisible = false;
dataGrid.RowHeadersVisible = false;
dataGrid.PreferredRowHeight = 60;
this.Controls.Add(dataGrid);
}
} catch (Exception err) {
Console.WriteLine(err); // put a breakpoint HERE
}
dataGrid = value;
}
}
Finally, make sure your dataGrid_CurrentCellChanged event handler (and everything else in your program) references this public DataGrid1 object, and not the dataGrid - or you'll find yourself having those same errors all over again.
hello creating a custom object may be a widely published topic, but my lack of coding skills proves problematic in actually implementing what i'm trying to do.
in a nutshell i'm adding controls at runtime in a flowpanelLayout. right now it's just listboxes, that code is all working fine. i would like a way to label the listboxes that are getting added, i can't think of a better way to do this than to use a text label. i was thinking it would be slick to create some sort of custom control (if possible) which is a listbox and a textlabel like one above the other or something. this way i can add the new custom control in my current code and assign the listbox attributes and label text, etc all in one motion.
this is what i was thinking, maybe there's even a better way to do this.
my current listview creation code:
public void addListView()
{
ListView newListView = new ListView();
newListView.AllowDrop = true;
newListView.DragDrop += listView_DragDrop;
newListView.DragEnter += listView_DragEnter;
newListView.MouseDoubleClick += listView_MouseDoubleClick;
newListView.MouseDown += listView_MouseDown;
newListView.DragOver += listView_DragOver;
newListView.Width = 200;
newListView.Height = 200;
newListView.View = View.Tile;
newListView.MultiSelect = false;
flowPanel.Controls.Add(newListView);
numWO++;
numberofWOLabel.Text = numWO.ToString();
}
maybe the actual best answer is simply to also add a textlabel here and define some set coordinates to put it. let me know what you think.
if a custom control is the way to go, please provide some resource or example for me - i'd appreciate it.
Here is a custom user control that can do that:
You just need to set TitleLabelText to set the title.
[Category("Custom User Controls")]
public class ListBoxWithTitle : ListBox
{
private Label titleLabel;
public ListBoxWithTitle()
{
this.SizeChanged +=new EventHandler(SizeSet);
this.LocationChanged +=new EventHandler(LocationSet);
this.ParentChanged += new EventHandler(ParentSet);
}
public string TitleLabelText
{
get;
set;
}
//Ensures the Size, Location and Parent have been set before adding text
bool isSizeSet = false;
bool isLocationSet = false;
bool isParentSet = false;
private void SizeSet(object sender, EventArgs e)
{
isSizeSet = true;
if (isSizeSet && isLocationSet && isParentSet)
{
PositionLabel();
}
}
private void LocationSet(object sender, EventArgs e)
{
isLocationSet = true;
if (isSizeSet && isLocationSet && isParentSet)
{
PositionLabel();
}
}
private void ParentSet(object sender, EventArgs e)
{
isParentSet = true;
if (isSizeSet && isLocationSet && isParentSet)
{
PositionLabel();
}
}
private void PositionLabel()
{
//Initializes text label
titleLabel = new Label();
//Positions the text 10 pixels below the Listbox.
titleLabel.Location = new Point(this.Location.X, this.Location.Y + this.Size.Height + 10);
titleLabel.AutoSize = true;
titleLabel.Text = TitleLabelText;
this.Parent.Controls.Add(titleLabel);
}
}
Example use:
public Form1()
{
InitializeComponent();
ListBoxWithTitle newitem = new ListBoxWithTitle();
newitem.Size = new Size(200, 200);
newitem.Location = new Point(20, 20);
newitem.TitleLabelText = "Test";
this.Controls.Add(newitem);
}
In my application I want to ensure that a TagVisualization is only displayed if the tagged object is placed on a Ellipse. So I used this code to do that:
private void TagVisualizer_VisualizationAdded(object sender, TagVisualizerEventArgs e)
{
Console.WriteLine("Hitlist");
//Notes
if (e.TagVisualization.GetType() == typeof(NoteVisualization))
{
bool found = false;
Point pt = e.TagVisualization.Center;
hitResultsList.Clear();
VisualTreeHelper.HitTest(RootLayer, null, new HitTestResultCallback(MyHitTestResult), new PointHitTestParameters(pt));
if (hitResultsList.Count > 0)
{
foreach (DependencyObject o in hitResultsList)
{
if (o.GetType() == typeof(Ellipse))
{
Console.WriteLine("Placed on a Sourcefile");
SourceFile sf = (((o as Ellipse).Tag) as SourceFile);
GroupBox gp = e.TagVisualization.FindName("GroupHeader") as GroupBox;
gp.Header = sf.getFullName();
e.TagVisualization.Tag = sf;
SurfaceButton save = e.TagVisualization.FindName("NoteSave") as SurfaceButton;
save.Tag = sf;
found = true;
break;
}
}
}
if (!found)
{
e.TagVisualization.Visibility = System.Windows.Visibility.Collapsed;
Console.WriteLine("Placed somewhere else");
}
}
}
I'm not really sure if this is the correct way, since I don't avoid that the TagVisualization is displayed, but instead I instantly set the Visibility to collpased. I think there have to be better ways to do that?
official guidance for how to do this is shown in one of the sdk samples:
http://msdn.microsoft.com/en-us/library/ee804861(v=Surface.10).aspx
-robert (former program manager for the surface dev platform)