Is there any way to access a subclass' Designer-generated components container? - c#

When you make a form in Visual Studio, the Designer automatically generates a components container:
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
We have a couple dozen forms, all of which share a common base class to make it easy to put in some common functionality ("You have unsaved data, are you sure you want to close this window?" and so on). Each form has its own BarManager component (they have different menus and buttons and so on, so nobody saw a need to have them inherit that component). Now we've realized that we'd like to add an event handler to all of those BarManagers.
I was hoping to be able to do something like this:
public partial class Foo : Form
{
...
private void Foo_Shown(object sender, EventArgs e)
{
this.components.Components.OfType<BarManager>().ToList().ForEach(tb =>
{
tb.SomeEvent += new EventHandler(FooHandler);
});
}
protected void FooHandler(object sender, EventArgs e)
{
// do stuff
}
...
}
If it was in the Controls collection that approach would work just fine, but since components is private, Foo & its controls have separate collections.
Is there any way to accomplish this without modifying every single subclass? I suspect it can be done with reflection (which I'm not very familiar with), but I would prefer a solution without reflection if that's possible.

Since the designer generates the components variable as private, reflection is the only way to go. Of course you can be clever and be sure that reflection will be used only once to fetch the instance that you will thereafter point to with a variable of yours, so that subsequent access will be more efficient.
You could make your own designer code serializer, but I think it's a bit of an overkill. Plus, that won't make existing forms components variable more available (unless you reopen the designer for each of them).
If I may: since every forms only have one toolbar (or so it seems?), why don't you browse the form's collection of controls recursively until you find an instance of it?
EDIT:
Since you're using DexExpress then your form will have DevExpress.XtraBars.BarDockControl instances placed as controls in the form. They are acting as the containers of your component toolbar objects. Look for those at runtime in Form.Controls. They have a Manager property that will give you the bar manager they're attached to. Of course from there you'll have access to every toolbars it contains.

Related

Why does VS2017 keep losing my derived controls?

In my app namespace = DRT, I'm creating control classes (e.g., button, textbox) that derive fron their corresponding Windows control classes, e.g.,
internal abstract class DRT_Button_Abstract : Button
{
....
}
internal class DRT_Button_CancelSearch : DRT_Button_Abstract
{
....
}
internal class DRT_Button_StartSearch : DRT_Button_Abstract
{
....
}
All together I currently have 13 derived classes that derive either from one of my abstracts or from a Windows control class. After a successful build, I see my control classes (e.g., DRT_Button_CancelSearch and DRT_Button_StartSearch) on the Toolbox and I successfully drop them onto my main form. All is fine for a while, but ultimately, I'll go to open the main form.cs [Design] (i.e., the UI designer) and it will show the error The variable '{control property name}' is either undeclared or was never assigned. for some combination of my controls.
When I examine the main form Designer.cs file, the expected code for all the controls is present EXCEPT for the expected new statement. They are not present in the main form Designer.cs file. For example, I expect to see this.drt_Button_CancelSearch = new DRT.DRT_Button_CancelSearch(); but its missing
I've tried ignoring the error, proceeding to the UI designer windows to re-apply the lost controls, but the problem just repeats with the newly applied control
What the heck is going on? Is there a way to recover from this situation?
This is most likely a problem of the Designer not being able to clear/reload its cache. There is not much you can do. In the past I:
closed and reopened all designers that have user controls
put all the controls in a separate project (in the same solution)
put all the controls in a separate solution/Visual Studio instance and set a proper reference to the controls' dll (or even nuget package)
With the first two options I have had varying success. Reopening the designer is not very convenient and doesn't work.
That last option is the best but also the most annoying because every adjustment requires a rebuild of the project and update of the reference/package.
Also make sure that all controls that you create have public default constructors and function well when this constructor is used.

C# OOP concepts: is inheritance from Control class the right way?

My question is about the right way of code writing. I use C# Winforms.
I created an instance of Control class (anyone available in designer), for example Panel class, set some properties of this object, subscribed events, wrote event handlers, etc. But usually I do this by next way: I create my CustomPanel class inherited from Panel and white above code (setting properties, subscribing events, event handlers) in CustomPanel class. So, when I want to use Panel with my setting and functionality, I can drag and drop CustomPanel object from designer. Is this a right way or interitance from Control classes isn't a good idea? Maybe I should create my own class (not inherited) contains Panel settings and bind it to Panel?
Use decorators when possible
Using Visual inheritance with the windows form designer is very very buggy, a lot of times the designer doesn't work and on top of that it never works when your controls are in a 64 bit assembly
http://support.microsoft.com/kb/967050
I would rely on Liskov Substitution Principle when making the decision. If what you're making ia a Panel in spirit, then it's OK to inherit (I feel it's OK).
In Windows Forms there are very limited capabilities to modify the controls(You could try WPF where are more ways to achieve such modification).
1) In WinForms you usually either end with enormous amount of event handlers:
winFormsControl.OnSomethingChanged += new delegate(object sender, SomeEventArgs e)
{
WinFormsControl control = (WinFormsControl)sender;
control.Property = value;
// ...
};
winFormsControl.OnSomethingOtherHappened += this.Handle_WinFormsControl_OnSomethingOtherHappened;
2) or build your own derived class if you have to add a lot of your properties and events or some methods to override like:
public class MyCustomPanel : Panel
{
//Constructor
public MyCustomPanel()
{
base.Layout += this.OnBaseLayout;
...
}
// Overriden methods(i am not sure if there could be any)
public override void SomeVirtualMethod() {...}
// Custom members
public CustomLayoutMode LayoutMode { get; set;}
public event EventHandler CustomLayoutCompleted;
}
3) Creating your own UserControl derived class is an option too.
4) Also you could try to wrap the existing control in a custom control derived directly from the Control:
public MyCustomControl : Control
{
private Panel WrappedPanel;
public MyCustomControl()
...
}
But I am a bit unsure about the way how you will paint inside your wrapper(but it is probably possible, just my WinForms are rusty).
Nonetheless most of the considered scenarios will be non-ideal - usually we need original properties and events of the Panel to be in Control's interface and no one likes boilerplate code.
The simplest way to avoid such problems is to inherit. No one says that it is the best. Especially when you will have to combine some hard-coded behaviours of custom controls into one.

What is the best way to access elements of parent control from a child control?

I have a parent control (main form) and a child control (user control). The child control has some code, which determines what functions the application can perform (e.g. save files, write logs etc.). I need to show/hide, enable/disable main menu items of the main form according to the functionality. As I can't just write MainMenu.MenuItem1.Visible = false; (the main menu is not visible from the child control), I fire an event in the child control and handle this event on the main form. The problem is I need to pass what elements of the menu need to be shown/hidden. To do this I created an enum, showing what to do with the item
public enum ItemMode
{
TRUE, FALSE, NONE
}
Then I created my eventargs which have 6 parameters of type ItemMode (there are 6 menu items I need to manage). So any time I need to show the 1st item, hide the 2nd and do nothing with the rest I have to write something like this
e = new ItemModeEventArgs(ItemMode.TRUE, ItemMode.FALSE, ItemMode.NONE, ItemMode.NONE, ItemMode.NONE, ItemMode.NONE);
FireMyEvent(e);
This seems like too much code to me and what's more, what if I need to manage 10 items in future? Then I will have to rewrite all the constructors just to add 4 more NONEs.
I believe there's a better way of doing this, but I just can't figure out what it is.
you could create an EventArgs which takes an ItemMode[] or a List<ItemMode> or a Dictionary<string, ItemMode> for those items (instead of the current 6 arguments) - that way you don't need to change much when adding more items...
The chain child->parent can be reversed. In such scenario requests will be passed from the mainform to its child controls.
Controls participating in the command processing must implement a special interface:
interface ICommandHandler
{
bool CanInvoke(int commandId);
void InvokeCommand(int commandId);
bool UpdateCommand(int commandId, MenuItem item);
}
The advantage of this approach is that only active controls must be traversed, not all the children.
The weak point - UpdateCommand() method, which could be called from Application.Idle event or timer.
hope this helps
Well, I can't speak to a "best" way unless except in specific cases, since there are often several equally good ways. My first thought, though, would be to create a class that has a property which the parent assigns a reference of its MainMenu, and which has functions for enabling/disabling individual menus or items. In a very simple case, this could be as simple as passing a list of strings like "OptionsScreen=enabled" etc. and then inside the class manually handling those cases, to something more generic like passing strings such as "mnuToolsOptions=enabled" and then finding the menu item via the .Name property. So, on startup, create an instance of your menu handler class, then do something like MenuHandlerHelper.MenuToHandle = MainMenuStrip;.
On the child side, you could perhaps have your classes that update the MainMenu be derived UserObjects that derive from a common one you create that has a public MyMainMenuHandlerHelper MenuHandlerHelper property, and set that in your Parent form's constructor so the Child controls could call the menu updating function. Or, you could have an event that just passed back a List<string> containing all the rules, and fire that as you are doing now.
This is a very simple idea, and doesn't handle things like possible collisions, so you would probably either want to throw an exception (easiest). You might also want to have rule priorities (easy), or try to chain functionality (could be hard to determine orders and such).
I would be happy to implement some examples of my thinking if you can constrain the problem a little for me (desired collision handling, etc.) and I actually wanted to see what some basic code would look like and try perhaps to test a couple of ideas, so if those come to anything I will post the code here for those as well.
If you want to handle all changes from the user control: you could inherit your own user control class and add a reference to the form/collection of menu entries you want to be able to modify. You would pass this reference to its constructor and then you'll be able to easily modify the menu from inside your user control
If, on the other hand, you would like to manage this on an event basis in your form, you could implement your own EventArgs class, but I would do it like this:
class ItemModeEventArgs
{
MenuItemClass target;
EnumType change;
}
So basically for each menu item a separate event is risen. Every event args knows about what item menu is changing and how it is changing. Ofc, if you only have two states for the menu items, the 'change' field is kinda useless.
This way you don't have to hardcode functions with n parameters where n is the number of menu items.
There truly are many ways this could be done. The easiest way, although some will shout "bad practice", would be to just pass a pointer to the main menu when the control is created. Your control would have some code like this:
MenuStrip MainMenu;
internal void SetMainMenu(MenuStrip mainMenu)
{
MainMenu = mainMenu;
}
and when you create the control:
void CreateControl()
{
MyUserControlType MyControl = new MyUserControlType();
MyControl.SetMainMenu(mainMenuStrip); //or whatever you called your main menu
}
This will give your child form unlimited access to the mainform's menu (which is why it's technically a bad practice). From the child form you can access the submenus by name, eg:
if (MainMenu != null)
{
ToolStripMenuItem fileMenu =
(ToolStripMenuItem)MainMenu.Items["fileToolStripMenuItem"];
fileMenu.DropDownItems["exportFileToolStripItem"].Visible = false;
}
If you created the control in the designer, then you can add the SetMainMenu call into the .design file, or add it in the Form's load event.

Is it safe to invoke MemberwiseClone() on custom server controls?

I have written a custom server control which (pseudo-code) looks like
public class MyCustomCtrl : WebControl
{
private Button innerCtrl; //some arbitrary object, doesn't matter
...
protected override void CreateChildControls()
{
//initialization etc, as standard...
}
}
Now I have the case that I have to copy this custom server control. Basically it is added by the programmer declaratively on the aspx or ascx code. At run-time multiple instances of this control have to be created which are then added to some parent control. This is however handled in the context of that parent control.
Anyway, it is a bit difficult to explain the context of use. My main question however is on whether it is safe to use "cloning", especially to invoke the MemberwiseClone() on web controls? What I did so far is to add a method like
public class MyCustomCtrl : WebControl
{
...
public object Clone(){
MyCustomCtrl clone = MemberwiseClone() as MyCustomCtrl;
clone.InnerCtrl = this.innerCtrl.Clone() as Button; //or whatever type
return clone;
}
}
I mean the ASP.net control hierarchy is quite a complex model. So I wanted to hear whether some of you knows any drawbacks that may occur with such a solution.
No, this is not a safe operation for a very general reason: MemberwiseClone is a shallow copy, not a deep copy.
So stuff like the ViewState is shared between your original control and the clone. But also the Controls collection is shared, so any change to the original Controls collection propagates to the clone. And controls use many more hidden references to maintain state (like style, events, etc.), so MemberwiseClone is a very dangerous thing to do.
The only way to reuse controls as a kind of template, is by actually using templates (ITemplate), which have an InstantiateIn method which creates a new control hierarchy. I suggest you look at controls like Repeater which do this.

Unable to access Winforms control in a class

I am currently working in a small windows forms project in C# using Visual studio 2008.
I have added a custom class to the project, but in this class I am unable to access the forms controls (like listbox, textbox, buttons ) in order to programmatically change their properties.
The class file has using system.windows.forms included and all files are in the same namespace.
Surprisingly, I am also unable to access the controls in the form1 class itself, unless I create a method in the class and then intellisense pops up the names of the various controls.
in the custom class however, intellisense does not show the names of the controls at all.
Appreciate if someone coudl shed some light on why this could be happening.
Thanks
Encapsulation means your separate class shouldn't be talking directly to the controls. You should, instead, expose properties and methods on your (outer) Control - for example:
public string TitleText {
get {return titleLbl.Text;}
set {titleLbl.Text = value;}
}
For more complex operations it may be preferable to use a method; properties are fine for simple read/write for discreet values.
This provides various advantages:
you can, if required, abstract the details to an interface (or similar)
you can change the implementation (for example, use the Form's Text for the title) without changing the calling code
it is just... nicer ;-p
Your class needs a reference to the form for this to work. The reason for this is that the form is not a static class so you can have multiple instances of it.
The best way of giving it the reference would probably be to pass it in the classes constructor. Then the class would have a reference to the form and could use that reference to change the controls.
An alternative option that you could use if you are 100% sure that you will have only one instance of your form open is to add a public static property to the forms class that returns the instance of the form. That property would then be available to be used in your other class.
Also, make sure that your controls are public, or better add public methods to your form that can be used to manipulate the controls indirectly.
The controls in Form1 will be private
partial class Form1
{
//elided other good stuff
private System.Windows.Forms.Button button1;
}
So no, you can't access this directly from another class.
You could make it public as #abatishchev suggests (but that would be a really bad idea).
A better plan would be to use properties as #Marc Gravell suggests.
You would still need to pass a reference to the form to the class that you wish to have consume the properties though (as pointed out by #Rune Grimstad).
You are trying to write a class in your application that directly asks the UI for data. This isn't usually considered a very good idea. The class should be entirely concerned with it's own purpose. You should design properties or events for the specific bits of data that the class needs access to and not necessarily pass it the entire form, maybe just the values that it needs to work with or change.
Take a look at how this could be implemented using the MVP pattern (sample code): Implementing MVC with Windows Forms
UPDATE: The code in the class you mention should in fact be part of the form's presenter, which has a reference to the form (through the IView interface). That is how you should be designing your UI code, not by directly accessing other Form's private parts.

Categories