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.
Related
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.
OK, weird one. I have many usercontrols with a repeater, the layout of the repeater is the same in all controls, and they all have a bindData() method publically available.
I'm wondering, can I setup another usercontrol for paging without having to specify the parent control?
I'm able to do the following:
((controls.specificuserControlClass)Parent).bindData();
Which is all fine - however I'd need to specify the specificuserControlClass into the pager and then would need it "per repeater" if you see what I mean?
So can I call Parent.bindData() blindly from the child control? I "know" that method exists (or would build checks to make sure), however Visual Studio isn't happy as it doesn't know of the method.
Why not make your controls all implement a specific interface?
public interface IBindData
{
void bindData();
}
Then, you would simply do:
((IBindData)Parent).bindData()
And it should invoke each control's method as appropriate.
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.
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.
I want to serialize wpf user control xaml and codebehind. XamlWriter.Save() only serialize's xaml so what can I do in this situation? My situtation is I have a usercontrol which include custom methods, subscribed events (ex: button click) When I deserialize usercontrol (for create usercontrol at runtime) I want to run that events and methods. Sorry my english isn't very good.
Just an idea, you can use MEF to built up a plugin structure for your user control. You want to create a user control on the fly but the event handler still should be hardcoded somewhere else in your project; with the plugin structure, you can just collect the plugin and reuse it; the events can be handled by a command something. Maybe giving a scenario and we can figure out more detail.
Plus, ISerializable provides a way for custom binary serialization for field, not for methods or events. Here is a related question:
What is the point of the ISerializable interface?
; on the other hand, you can still try some pattern like how the web control save its view state; for example
two virtual methods:
public virtual byte[] SaveState(); // it saves the states for your custom control
public virtual void LoadState(byte[] state) // it restore your state back to the control.
The custom code should be like:
byte[] state = controlA.SaveState(); // this method saves its own state.
YourControl controlB = new YourControl();
controlB.LoadState(state); // this method load the save state from A to B itself.
For the event, every event has a handler name, you can also serialize its handler name to the state and find it back by the saved name from its naming container alike. I don't have any experience to save the method.
If you still want to save and load state including fields, events and method, maybe serialization is not the proper way you are looking for I think.