Follow-Up: Finding the Child of a Control - c#

I believe the proper term is recursively. I have a Windows Form, and inside that I have a Tab Control, and inside the Tab Control are four Tabs, and inside each tab are multiple controls - Buttons, text boxes, etc. I want to change the cursor of every button to a type hand.
Below is where I have gotten so far with this inquiry:
foreach (Control c in tabControl1.Controls)
{
// The only controls that will be found here are the tabs themselves. So, now I must run a *foreach* loop through every tab found, and look if buttons are present.
}
The commented area explains my issue to some extent. I have found an example of a recurisively finding a control on a form but I am not sure why I would need to pass the contro's name as an argument as I am trying to find Every control of type button.
Here is the code that I found online:
http://www.dreamincode.net/code/snippet1663.htm
Thank you once again. I love hearing from all of you as it's an excellent learning experience for me.
Thank you very much for your time.

private void FindAll(Control myControl)
{
if (myControl is Button)
doStuff();
foreach (Control myChild in myControl.Controls)
FindAll(myChild);
}
I believe this will work. When you call it the first time, you'd pass in the form. The form isn't a button, but it will have children. Each child it has will be passed into FindAll(). If that control is a button, it will call doStuff() (you can set the cursor in there). Likewise if that control has any children, they'll be passed in.
You are correct, the term is recurisve (generally speaking, any function or sub that calls itself). So, in this example FindAll() will call FindAll() in a certain case.
Also, this is just sample code; you may want to check for null references depending on the nature of your application.
EDIT: Just as an FYI if you aren't familiar with recursion, it's pretty easy to get the dreaded StackOverflow exception. When you end up in a never-ending loop of calling yourself, you'll run out of stackspace and see the StackOverflow exception. Hence, the name www.StackOverflow.com
In this case, we don't have to worry because .NET prevents us from adding controls that create a circular reference. For example - this code will fail:
GroupBox g1 = new GroupBox();
GroupBox g2 = new GroupBox();
GroupBox g3 = new GroupBox();
g1.Controls.Add(g2);
g2.Controls.Add(g3);
g3.Controls.Add(g1);
I don't know if any of this makes sense, but hopefully it helps. Recursion is generally considered one of the 'harder' concepts to grasp for a lot of people. Then again, I'm not very good at explaining things.

You are correct that the correct term is recursion. In the link you have posted, it is indeed recursive because the function calls itself, which is a property common of recursive functions.
The function needs to take a Control instance because the function is trying to solve the problem "For a given control (which is the Control container that is passed in), find all controls inside." Notice how this method doesn't care about what 'level' the control is at, it can solve it regardless.
You are correct that if you ran the code in your example, it would not work. It would only pick up controls one level inside of the 'parent' control. This is why the function needs to call itself.
With the function calling itself, you get the following:
Call function with the outermost control.
Do I have any children?
If so, call the same function again for each child (which will again ask "Do I have any children?" on the child).
By calling the function inside the function, you will hit all levels.
WARNING: Just as a note of caution, recursion used carelessly can lead to problems. If you apply this on something that has 1000 'levels', your algorithm will take forever and possibly crash as you will run out of memory to handle it, since it is digging deeper and deeper (a stack overflow!). Separately, I suspect there is a better way to do what you are doing such that you don't need to use recursion, although it will work.
Hope this helps!

Related

How to create a class that clears the screen of buttons and other controls?

I am reusing the same code for clearing the screen many times in my program, and I thought about turning it into a class, but I still don't get how classes work and how to properly make one.
My code to clear buttons and other controls is as follows:
List<RichTextBox> _richTextBoxes = this.Controls.OfType<RichTextBox>().ToList();
List<Button> _buttons = this.Controls.OfType<Button>().ToList();
List<Label> _labels = this.Controls.OfType<Label>().ToList();
List<TextBox> _textBoxes = this.Controls.OfType<TextBox>().ToList();
foreach (var rich in _richTextBoxes)
{
this.Controls.Remove(rich);
}
foreach (var button in _buttons)
{
this.Controls.Remove(button);
}
foreach (var label in _labels)
{
this.Controls.Remove(label);
}
foreach (var textBox in _textBoxes)
{
this.Controls.Remove(textBox);
}
As others already mentioned, it's a rare practice to remove/create all controls of a container (Form, Panel, etc) at runtime, and a possible waste of PC resources.
Of course you can use:
Form.Controls.Clear();
Or
Panel.Controls.Clear();
But, what's wrong with placing all your controls in a Panel, for example, and simply hiding said panel? seen you get the same result in a more efficient way
If you opt for this, it's as simple as this line:
Panel.Visible = false; // or true
Hiding 100s or 1000s of controls is inefficient and wasteful
Contrary to the other answer's idea that
Panel.Visible;
...is somehow more efficient, it is not. Hiding a control does not release any resources the control might have requested the obvious being a window handle. Handles, fonts etc are all part of the GDI pool, a rather limited resource. Depending on what version of Windows you are running you might be limited to anything between 256 and 16,384 GDI handles per process.
So think before hiding 100s of controls. Much better to just destroy the unwanted and create the ones you need.
Back to the question
I am reusing the same code for clearing the screen many times in my program
The bigger question is why you want to do that?
Though a legitimate programming scenario, given that "but I still don't get how classes work" , it is unlikely you are at the dynamic-UI-apps stage based on say dynamic selection of a database table.
Consider creating multiple forms via the Designer and don't modify them at runtime. That's alot easier if dynamic UIs isn't really a requirement.
An easier way to change things at runtime
Otherwise if you are super keen to continue with changing things at runtime, consider placing all the controls in a child Panel belonging to the form.
Then you can just call Clear():
myPanel.Controls.Clear(); // Job done

C# WinForms: A function that executes more than expected

It sounds stupid, but this happens to me.
I activate a function, and it does its work, and after it finishes it enables itself again. With other words, the function activates twice, not once. I tried to find out why by debugging, but I didn't find the reason.
When I click on a panel this function activates:
private void Play(object sender, EventArgs e)
{
Play0(MousePositionX, MousePositionY);
if (swich_player == true && AI_enabled == true)
{
AI_playing();
swich_player = false;
}
}
The whole code is really long.
Is it me not searching correctly? Are there other reasons? In order for this function to start again, something has to activate it. I can't find what is activating the function again.
Any suggestions, where to search for the problem? Or what is the problem?
EDIT:
I finally found the problem. There is a button (button1) that changes the panel's properties, and a second button (button2) that makes the panel 0 pixels wide and 0 pixels high (in order to make it invisible). The first button also adds an EventHandler that activates the function. But button2 does not remove the EventHandler. This way the function gets called as much times as the times I have pressed button1.
Looks like what you're looking for (via your comments/edit) is the source of multiple calls to this function. With the current snippet of code, it's not possible for us to tell. But what you should do is put a breakpoint on the entry of the function and then when it is hit, look at the "Call Stack" window in visual studio.
This can be accessed via the Debug -> Windows -> Call Stack menu item (when running the program. It will not show up in Windows if you're not running)
You can then see what is calling your function through this window. It is an extremely useful tool.
Pre Edit:
Your question isn't very clear, but I think (from your variable names) that you're looking for a game loop that will continually run. If so, take a look at this blog post which has some very good information on different styles of game loops. It ranges from simple to more complex (and scalable) loops.
Your question states that you activate something once and it "enables again", and also that "In order this function to start again, something has to activate it. I can't find what". These seem to be contradictory statements. Can you edit your question to be a little clearer? If my answer is not what you were looking for after your edit, I will do my best to add whatever is needful.
check against which event you've registered this function. if you registered into to something no "OnClick" of that button, it might be the root cause

C# Label Properties won't update upon resize

I recently started getting acquainted with Visual Studio 2010 and C# for an internship. C# doesn't include a built-in InputBox function, so I made my own form, with a text box, two buttons and a simple label.
I have a function set up to allow the programmer to call the form in regular format (where the user enters input via the textbox) or yes/no format (where the form just displays a question and the yes and no buttons).
When I switch over to yes/no format, I want to center the label programmatically. I've been using the code:
labelNote.Left = inputBox.Left + (inputBox.Width / 2) - (labelNote.Width / 2);
This should put the center of the note in the center of the form. However, if the contents of the label change (making the new label longer or shorter) the properties don't update to reflect the new size. It won't center unless it includes the original text. Is there some way to force an update? I foresee this becoming a problem with positioning objects for scalability in the future.
Thank you for your time
I'm assuming you have the sizing within an actionlistener. Specifically the forms' resize action listener. Then whenever the form is resized it is called and all of you code is recalled. Then to force an update from somewhere else you just have to call the actionlistener.
Actionlistener:
Private Sub formName_Resize(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Resize
Calls actionlistener:
formName_Resize(sender, e)
Well, you could attach an event to Label.TextChanged. Frankly it would be better to change the TextAlign or something like that though: try to perform the layout in a declarative fashion instead of doing it explicitly through code. That tends to make things work rather better.
I've found the [TableLayoutPanel]1 control to be reasonably easy to work with - most of the time (and occasionally a complete pain).
It turns out that I made a stupid mistake (a common theme for me in debugging. The really small stuff goes unnoticed for the longest amount of time).
The label resizing was not the issue. The issue was the order in which I changed the contents of the label and then called the function to calculate its new location. I was calling the location calculation first, so it found where to center the label based on the old contents. I didn't notice for so long, because the text was changing properly. I took it for granted that the functions were being called in the correct order.
So, when it doubt, check the order in which you're writing your code. Thanks for you help anyway, everyone. I ended up finding out some neat things that could be applicable to other scenarios (such as the MeasureString function in the Graphics class).

.Net C# Design View errors

I have subclassed a Treeview and on instantiation it loads a new ImageList (and the associated Images).
Whenever I switch to the designer view, it's also trying to run this code, however the images aren't in the designer's path, so it crashes. I ended up putting in a hack to see if the current directory is "Visual Studio", then do nothing... but that's so ugly.
I find this happening for other things. If a control is trying to use objects during load/initalization that are only available while the program is running, then the Design View cannot bring up the control.
But is there a way to get around this?
I guess what I'm hoping for is having a try/catch for the Designer (only) with the ability to ignore a few errors I know will be happening (like FileNotFoundException, etc.).
Thanks
Everything that inherits from System.Windows.Forms.Control has a DesignMode property that returns a boolean indicating if you are in design mode or not. You could use this to determine when to/when not to load external resources.
Usually it is better to move the loading of these resources to an override of OnLoad as they are rarely required directly at construction. This fixes the issue you are seeing and means that only trees which get displayed at least once will perform these additional resource loading steps.
Otherwise, you can just exclude these steps during design time by checking the DesignMode property and acting accordingly.
This is a fine pattern to use if you're making a control library with a sample of images when shown in the designer or hook ins to other designer features but as a pattern for development I'm not sure it's very effective.
I would suggest shifting your "business logic" (in this case your loading of certain images into a treeview) outside of the bounds of your treeview control. In your case I would place the logic within the Load event of the form that the control is inside:
public void Load(object sender, EventArgs e)
{
string path = "c:\somePath\toAwesome\Images";
myFunkyTreeView.AddImages(path);
}
For larger apps I personally think you want to shift the logic even out of the forms themselves, but this is debatable measure as it requires additional plumbing as a trade-off for the flexibility this provides.
Thanks for pointing me in the right directioon guys.
I had tried registering to the OnLoad event, but that event is triggered when the Design View comes up, so that didn't quite work for me (am I doing something wrong?).
Anyway, I looked a bit more into the DesignMode property. It can only work for Controls, and sometimes your object may not even be a control.
So here's the answer I prefer:
if (LicenseManager.UsageMode == LicenseUsageMode.Designtime) {
// design-time stuff
} else {
// run-time stuff
}
Found it here.

Error Creating Control - Custom Control

I have a custom control and it works fine...except that the control cannot be rendered on Design Time. ( I am using VS 2008)
I am thinking many people who develop custom controls encounter this problem...The error I get is "Error Creating Control - CustomControlName" Object reference not set to an instance of an object.
I want a work around. or at least debug this...(Since this is a design time issue how to debug?)
I have tried if( !DesignMode) code on OnInit, OnPreRender, RenderContents, CreateChildControls Methods ( I am just shooting in the dark)...
Help pls. I really hope this is not a VS bug!
BFree's comment is the most likely issue, for a control to display in the design view it needs a parameterless constructor as the design viewer doesn't know how you would normally instantiate the control.
If you do have a parameterless constructor, can you paste some code in to show what's happening?
As Glenn mentioned the first issue could be no parameterless constructor.
The second could be you are calling methods during the OnLoad or other methods you mentioned that have parameters that are not initialized or some sort of attempt at database calls etc that is normally done at run-time.
Unless they fixed this bug recently* and I'm not aware, something to keep in mind is the DesignMode property works for the first and second level of nested controls but beyond that it normally doesn't work right. (Such as form containing a UserControl[1] that holds another UserControl[2], the DesignMode works on the form and [1] but not [2]).
Also to agree with Glenn, seeing some of the code will help.
*From my very recent experience working with nested usercontrols it hasn't been fixed.
In your OnPreRender & CreateChildControls methods it's making a call to this.Page. You might want to try wrapping them in a
if (this.Page != null)
{
.....
}
Because I don't think you'll have a Page object at that point & I'm pretty sure PreRender & CreateChildControls will be called in design view. I haven't written custom server controls for a while though, so I could be wrong (been working in MVC lately).
Glenn, the error ur getting a VS bug and no fix has been released yet.

Categories