C# Lock WinForm Controls - c#

In a program I have written users can add controls to the form and move them around and set some properties in a pseudo design mode. I want to be able to lock all these controls into one location when they press a button to switch to "data mode". How can I do this? I wanted to do be able to loop through all the controls and use the Lock Property but I noticed it didn't show up in intellisense.
Thanks!

The Locked property is not a real property -- it is one which is added in by the Windows Forms designer (like the Generate Member and Modifiers "properties"). You would therefore need to simulate it yourself, either at the form level or (if required) at the control level (say with a dictionary of which controls are locked), and manually check it in the code you've written for moving controls around.

I am assuming by "pseudo-design mode" you do mean that your application is in a run-time state, and the end-user is experiencing a "virtual design mode" : please correct me if I am wrong.
But, I am assuming you are referring to the design-time 'Locked property of controls, and that you wish to "emulate" this at run-time ... correct ?
I'm also assuming you are attaching mouse up/down/move handlers to the controls you do allow to move around, probably by looping through all, or a subset of, the controls on the form (or a collection you are maintaining of controls allowed to be moved).
If my assumptions are correct, I would go for removing the event handlers that enable moving when you need to disable control movement, then restoring those event handlers when you need to allow controls to be moved again.
One main reason being that it is, imho, "best practice" to control event-handling rigorously (leaving event handlers "in-place" can interfere with object disposal ... although that may, in no way, apply to your scenario here).
One more idea : you have an "invisible" Panel docked 'fill to the Form : on this panel are all controls that can be moved : this may allow you to more easily "narrow your focus" on which controls you "spend" this extra code on. The drawbacks in using this approach are usually :
if you use hostingForm.ActiveControl to determine which control got the mousedown (and, thus, can then be moved) : you'll find some controls, like labels, and pictureboxes, do not become the activecontrol of the form when clicked, but most do.
you have a "z-order" thing to think about since a control not in your panel encapsulating the controls you wish to allow to move sent behind the pseudo-transparent panel will be hidden.
For these reasons, imho, I think disabling and re-enabling event handler attachments is best, most simple, and since it can be done when the controls are "down-cast" to their control "identity" :
private void enableControlsMove()
{
foreach (Control theControl in panel1.Controls)
{
Console.WriteLine(theControl.Name);
theControl.MouseDown += new MouseEventHandler(theControl_MouseDown);
theControl.MouseUp += new MouseEventHandler(theControl_MouseUp);
theControl.MouseMove += new MouseEventHandler(theControl_MouseMove);
}
}
private void disableControlsMove()
{
foreach (Control theControl in panel1.Controls)
{
Console.WriteLine(theControl.Name);
theControl.MouseDown -= theControl_MouseDown;
theControl.MouseUp -= theControl_MouseUp;
theControl.MouseMove -= theControl_MouseMove;
}
}
I use it this way.
best, Bill

Locking controls prevents them from
being dragged to a new size or
location on the design surface.
However, you can still change the size
or location of controls by means of
the Properties window or in code.
MSDN
I guess it's a visible-to-designer-only property. I think you'd have to implement your own freeze mechanism - a little flag to toggle between Design and Use modes.
Update: It seems that custom designer classes can add properties to controls based on whether they are in Design Mode or not.
More details available here if you intend to take the VS architectural hammer path. In any case, worth 10 mins of reading time.
Custom Design-time Control Features in Visual Studio .NET - Dino Esposito

Related

Control not visible after toggling visibility recursively?

A Quick Note
I've tried to be as thorough as possible with my question but you may still need additional clarification; if this happens to be the case, feel free to comment your concerns and I will update the post to answer them as best as I can.
I came across a rather odd issue yesterday when toggling controls on my form. I have a period of loading on FormShown and all controls but the loading display should be hidden. The toggle works to turn visibility off for everything but the loading display, but when loading completes only some of the controls are visible.
I stepped through the code that turns everything back to visible and ensured that everything is indeed being set to Visible = true. I think it may have something to do with the Dock property on the controls, or maybe the TabIndex or something similar but I'm having trouble tracking the underlying issue down.
This is the code I use to toggle the control visibility:
private void ToggleAllControlVisibility() {
foreach (Control c in Controls)
ToggleControlVisibility(c);
}
private void ToggleControlVisibility(Control c) {
if (c.Name == "loadingContainer")
return;
if (!(c is SplitContainer || c is SplitterPanel))
c.Visible = !c.Visible;
foreach (Control child in c.Controls)
ToggleControlVisibility(child);
}
It is a recursive toggle in which all child controls are also toggled. The ToggleAllControlVisibility method is called before loading begins, and again after loading completes.
A More Detailed Look
Now that you know the problem, there are certain controls in particular that I know do not show (at least the way they should). Take the following control tree for example:
pTimePanel (Panel)
timeSlider (TrackBar)
lblStartTime (Label)
lblStopTime (Label)
Out of the controls above, only the pTimePanel actually displays in the foreground. I believe its child controls may be displaying behind it somehow because as I step through the code and it reaches the lblStartTime control, I can briefly see the outline of the label (no content) and then when it moves to the next control it's gone. The timeSlider control doesn't seem to show the same behavior but it does get back to Visible = true.
The full tree from form to the above controls (with docking properties) is as below:
splitContainer : Dock-Fill
panel1 (SplitterPanel)
loadingPanel (Panel) : Dock-Fill
pTimePanel (Panel) : Dock-Bottom
pTimeLabels (Panel) : Dock-Bottom
lblStartTime (Label) : Dock-Left
lblStopTime (Label) : Dock-Right
timeSlider (TrackBar) : Dock-Fill
Notes
Some of these may be less helpful than others, but as I think of things that may help clarify what's going on, I'll add them here.
When visibility is toggled in either direction, the Resize event on the loadingPanel is raised.
Question
What could possibly be occurring to make this odd behavior possible?
It turns out that this was related to the order in which the controls were added to the form. Due to some copying and pasting controls a while back things ended up in an improper order. I had to rebuild the form from scratch to get any toggling to actually work. Once I rebuilt the form, all of the available options for toggling began working the way I expected them to.

Multiple forms one windows C# possible or only panels/usercontrol?

So here's my Question, I'm new to C#(teaching my self at that) Here's the thing, I'm working on a basic sim game, nothing to complex but I've got the design and basic functions done.
However In order to implement it, I'm currently using multiple Forms(Visual Studio 2013)
I have my "main" form which has the "action" buttons to it
So when i want to go to a user Profile page I have
Btn_profileview Click(object sender, EventArgs e){
Form profile = new Form();
profile.Show();
}
The User would then implement the changes(for instance change name) which is written to a text file, for use in other areas of the program.
However It opens a new windows, I've tried modal and nonmodal windows and while the benefit of Modal so they have to actual close the window solves the issue, i'd rather have it just overwrite the preexisting Form, and then on close go back to the "main" screen without actually using multiple windows.
Now I was told UserControl and/or Panel would solve the issue, but it would cause a complete redesign moving from the multiple forms to the multiple panel screens and figuring out how to get those to work(Visible and Invisible), i'm assuming it wouldn't be extremely difficult something along the lines of Panel"name".show(); and panel"name".close();
But would it be possible to actually add a line of code to the pre-existing code(so as not to cause a complete reesign) or are Panels and UserControl the only real way to implement within 1 continuous windows?
paqogomez is right: There are many ways to do it.
Here is one that combines a lot of the pros:
You create an invisible Tab on your window with as many pages as you need. Place a Panel on each tab and create all your controls on of them. This does not mean that you have to do it all over - you can move and drop the controls you already have without much hassle. Of course you need to turn off docking and maybe anchors, but other than that this is a simple process.
If you have controls on the 2nd form with the same name, these names should be changed to something unique though. I hope all Controls have proper names already, but especially Labels get neglected, at least here.. (With a little luck you can even use cut and paste to get Controls from another form to panel2!)
The big pro of this trick is that you can do all work in the designer of the same form. The Tab control serves only as a container where you keep your panels without adding to the UI and without giving the user control of what is shown.
Next you create one Panel variable in your main form:
Panel currentPanel;
On Load you assign the first 'real' Panel to it like this:
currentPanel = panel1;
this.Controls.Add(currentPanel);
Later, each time you want to switch, you re-assign the panels you need like this:
this.Controls.Remove(currentPanel);
currentPanel = panel2; // or whichever panel you want to show..
this.Controls.Add(currentPanel );
If your real panels are docked to fill the tabpage, as they should, the currentPanel will fill the form. You still have access to each panel and to each control by their names at any time but you see no overhead of tabs and your form never changes, except for the full content.

Correct UserControl Usage?

I just started breaking up my GUI application into UserControls. I have a TabControl with a bunch of TagePages. Obviously my MainForm.cs file was filled up with tons of events and controls etc and it got very messy quick.
So a previous question gained me the insight of how to create a UserControl. I intend on creating a UserControl for each TabPage and I was wondering how I can interact with Components on the main form or other UserControls.
Here is an example of a TabPage that I have made using a UserControl, which needs to Enable or Disable a button depending which TabPage is currently selected. Is this proper usage or is there a better way?
public partial class TabDetails : UserControl
{
private RequestForm fRequestForm;
public TabDetails()
{
InitializeComponent();
}
public void CustomInitialization(RequestForm pRequestForm)
{
fRequestForm = pRequestForm;
pRequestForm.TabControl_Main.SelectedIndexChanged += SelectedTabIndexChanged;
}
private void SelectedTabIndexChanged(object pSender, EventArgs pEvents)
{
fRequestForm.Button_SubmitRequest.Enabled = fRequestForm.TabControl_Main.SelectedTab != fRequestForm.Tab_Details;
}
}
In the MainForm.cs constructor I call:
this.tab_Details1.CustomInitialization(this);
This doesn't look like a good use of a user control. The user control should not decide how things in the form should behave when something is changed in the user control. A user control should be unaware of its container and should operate in any container.
The user control should notify the form that something has changed without telling what's the internal implementation and the form should decide what to do.
Example:
A user control named "NameUserControl" consists of TitleComboBox, FirstNameTextBox and LastNameTextBox. The user control wants to notify when one of the values has changed.
Wrong Way:
Create events:
TitleComboBox - SelectedIndexChanged.
FirstNameTextBox, LastNameTextBox - TextChanged.
The problems here:
You expose the internal controls behavior. What will happen if you want to change the TitleComboBox to TextBox? You'll have to change the event name and implementation.
You expose the fact that you use exactly 3 different controls. What will happen if you want to use the same text box for first and last name? You'll have to delete one event and change the name of the other.
Good Way:
Create only a single event: NameChanged and expose 1 property of FullName or three different properties for the values.
Either way the form subscribe to the event and decide what to do next.
Another thing to think about: the more you add more functionality to your user control, you either make it less reusable or you make its code more complex. For example, if you add validation inside the user control, you'll find one day that you need it without validation, so you'll add a property "bool ValidateData" or it will be so complicated that you'll need to build another control. One way to solve that is to build very small user controls, but combine them in one or more bigger user controls that fit all your current needs.

C# Using a form to load other user controls and have access to a base control or property

Currently I have a C# program with a windows form and then a user control template put onto the form. The user control template is really just used as a placeholder. I have a series of other controls which inherit from this user control template.
Each of those controls have navigation buttons like 'Continue' and 'Back' on them and each control knows which control needs to be loaded next. However what I need to figure out is an easier way to have variables that are global to these controls.
The only workaround I have is that I pass the form to each control when they are loaded and use variables inside of the form to read and write to. What would be the proper way to have each of these user control screens be built off of a base control which contained objects all of the controls could get to?
Sorry for the rambling nature of the post but I've been thinking about this problem all morning.
Here is some of the code:
Most of what I have written was based on hiding and showing the user controls so that content in the controls wouldn't be lost during navigation. I won't be needing to do that as eventually it will be loading the fields of data from a database.
Code for initially loading control from form click:
conTemplate1.Controls.Clear();
conInbound Inbound = new conInbound(this);
Inbound.Dock = DockStyle.Fill;
Inbound.Anchor = (AnchorStyles.Left | AnchorStyles.Top);
conTemplate1.Controls.Add(Inbound);
Code for Continue button inside of one of the controls:
if ((Parent.Controls.Count - 1) <= Parent.Controls.IndexOf(this))
{
UserControl nextControl = new conPartialClear();
nextControl.Dock = DockStyle.Fill;
Parent.Controls.Add(nextControl);
this.Hide();
Parent.Controls[Parent.Controls.IndexOf(this) + 1].Show();
}
else
{
this.Hide();
Parent.Controls[Parent.Controls.IndexOf(this) + 1].Show();
}
The best-practice for communicating from a control to a parent is to use events, and for communicating from a parent to a control is to call methods.
However, if you don't want to or can't follow this practice, here's what I would recommend.
Each UserControl has a ParentForm property that returns the Form that contains the control. If you know that the UserControl will always be attached to MyParentForm, you just cast the ParentForm and then you can access all public controls, methods, etc.
Here's what I mean:
public class conTemplate
{
public MyParentForm MyParentForm
{
get
{
return (MyParentForm)this.ParentForm;
}
}
}
This way, you can easily access any public members of MyParentForm. Your conInbound class could have code such as this.MyParentForm.GlobalSettings.etc..., and could even have access to any public controls.
I'm not totally sure I understand your problem. It sounds like you want the user control to "do something" with it's parent form. If that's the case, you may want to consider adding events to the UC and then handle them on the form itself.
Basically, for your UC's "continue", you'll have an event that's fired when it's pressed. You'll want to handle that in your form. I'm not real sure about the syntax from memory, or I'd work something out for you code-wise. But I think that's the route you'll want to take. Think of your UC like any other windows form control. If you add a button to your form, you assign it it's event method. Do the same with the UC.
I found this and thought it may be helpful. Scroll down to where it talks about UC's and events.
http://www.akadia.com/services/dotnet_user_controls.html
Hope this helps.
EDIT after new info from OP.
You could declare a global variable inside the UC of type yourForm and then set that variable to the ParentForm at run-time, if I'm understanding you correctly.
So, inside your UC Class, you could do:
private parentFormInstance;
then inside the constructor of the UC, you could set it as such:
parentFormInstance = this.ParentForm; (or whatever the property name is).
This allows you at design-time to use:
parentFormInstance.DoSomething();
without the compiler yelling at you.
Just basic advice, but if you can go back and make it easier on yourself, even if it takes some additional time re-working things, it'd be worth it. It may save you time in the long run.

.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.

Categories