I have a BaseForm that specifies several protected controls which are initialized in BaseForm.InitializeComponent(). I've made these controls protected so that I can access the values of dropdowns, etc, in my DerivedForm. Making these controls accessible to DerivedForm causes the Designer to include them in DerivedForm.InitializeComponent(), which resets them, thus undoing any additional work I've done in the BaseForm constructor.
Is there a way to access my BaseForm controls in DerivedForm, but not have them initialized a second time?
public SettingsDialogBase(Settings settings)
{
InitializeComponent();
// Additional work which initializes dropdowns, etc
InitializeSettings();
}
public SettingsDialog(Settings settings) : base(settings)
{
InitializeComponent();
// InitializeSettings() rendered useless on controls that are set to protected
// because SettingsDialog.InitializeComponent() included them automatically
}
I've made these controls protected so that I can access the values of dropdowns
There's your problem.
Don't make those controls protected. Keep them private to the base class. Expose them to subclasses exactly as you would publicly: by wrapping access to the controls in public properties that allow access to only the aspects of those controls that need to be accessed.
For example:
class BaseForm : Form
{
public string PromptText
{
get { return textBox1.Text; }
set { textBox1.Text = value; }
}
public int SelectedIndex
{
get { return comboBox1.SelectedIndex; }
set { comboBox1.SelectedIndex = value; }
}
// etc.
}
Note that if things like ComboBox uses e.g. enum values, you can make a property like SelectedValue instead, having the enum type and cast when returning from the comboBox1.SelectedValue property.
Note also that another way to approach this type of design issue is to author UserControl objects instead of forms, and use composition to build up the task-specific forms. This avoids inheritance altogether.
BaseForm's implementation of InitializeSettings:
protected virtual void InitializeSettings(Settings settings)
{
//initialization of settings
}
DerivedForm's implementation of InitializeSettings:
protected override void InitializeSettings(Settings settings)
{
base.InitializeSettings(x);
//reinitialization of settings
}
And call of InitializeSettings() in your DerivedForm's constructor will set your settings.
Okay, the goal was not clear for me.
If you want to have just 1 initialization of Settings, do not apply them in constructor. Basically, you should use the
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
//initialization of settings
}
The second way to solve the problem is to not even have parameterized constructor and to call InitializeSettings outside after creation of form by default constructor.
Related
I am creating a user interface for an iOS app and I am looking for the correct way to create a reusable custom control. I got it generally working when running the app, but at design time setting my "exported" properties has no visible effect in the designer. I think I am doing something fundamentally wrong, so perhaps someone could give me guidance
What I am doing:
I have created a subclass of UIControl.
In the constructor I call an Initialize method.
In the Initialize method, I add several subviews and constraints to layout them within my control
Here is some hollowed out code that shows the above:
[Register("RangedValueSelector"), DesignTimeVisible(true)]
public sealed class RangedValueSelector : UIControl
{
public RangedValueSelector(IntPtr p)
: base(p)
{
Initialize();
}
public RangedValueSelector()
{
Initialize();
}
public int HorizontalButtonSpacing
{
get { return _horizontalButtonSpacing; }
set
{
_horizontalButtonSpacing = value;
}
}
[Export("LabelBoxVerticalInset"), Browsable(true)]
public int LabelBoxVerticalInset
{
get
{
return _labelBoxVerticalInset;
}
set
{
_labelBoxVerticalInset = value;
}
}
private void Initialize()
{
//Code that creates and add Subviews
//Code that creates and add the required constraints, some of which should depend on the design time properties
}
}
So the control works perfectly fine if I set the exported properties via the designer, however they do not have an immediate effect in the designer itself.
What is the suggested way of having design-time settable properties that change the constraint values? I would like to avoid having to recreate all the subviews each time someone in the code or in the designer sets a property.
You are missing constructor with RectangleF which is used by designer.
public RangedValueSelector(RectangleF bounds):base(bounds){}
The rest seems to be correct.
I have a WinForm that I have used to build and test an email newsletter. This form contains a number of methods and events.
I am now adding a new feature to my program to allow split testing (A/X Test) and therefore creating up to 4 different newsletters for a campaign.
Therefore I'd like to extend my form to accommodate both normal newsletters and A/X newsletters. I'm going to add two different modes to my form. Something like:
private enum CampaignMode { Normal, AxTest };
They will be very similar in appearance, except a number of controls' visibility will change.
Apart from that, almost all methods and events will have two separate ways of handling.
What design pattern should I use so that I don't have to create a new separate form?
For simplicity, let's say my form has the following methods:
Constructor: probably receiving the EditMode as a parameter
Load
Create: Button click event
SetControlViews: Based on EditMode set the visibility of controls
MethodA: Specific to Normal mode
MethodB: Specific to AxTest mode
Sounds like Template Method would be a good candidate.
Make a base class which takes care of logic for the basic initialization, Load(), Create(), SetControlViews() and then make Method() virtual and override in two derived classes according to specialized logic. Also, the constructor of each derived class could do some specialized initialization.
This way you don't even need a CampaignMode enum (or EditMode if you meant those to be identical). The existence of different entities there is illustrated by the existence of different derived classes.
I would simply have pairs of functions for each method/event and then have the main handler call the appropriate one, depending on the value of CampaignMode.
public enum CampaignMode { Normal, AxTest };
public partial class DemoForm : Form
{
private CampaignMode campaignMode;
public DemoForm(CampaignMode mode)
{
InitializeComponent();
campaignMode = mode;
SetControlsVisibility();
}
private void SetControlsVisibility()
{
if (campaignMode == CampaignMode.Normal)
{
//Set normal controls visible;
//Set axtest controls invisible;
}
else
{
//Set normal controls invisible;
//Set axtest controls visible;
}
}
private void button1_Click(object sender, EventArgs e)
{
if (campaignMode == CampaignMode.Normal)
{
MethodA();
}
else
{
MethodB();
}
}
private void MethodA()
{
}
private void MethodB()
{
}
}
Note: if there are many controls, then it is neater to have a visibility function with a bool parameter:eg
private void SetNormalVisibility(bool isNormal)
{
//normalTextBox.Visible = isNormal;
//normalButton.Visible = isNormal;
//axTestTextBox.Visible = !isNormal;
//axTestButton.Visible = !isNormal;
}
In which case, change the SetControlsVisibility function to:
private void SetControlsVisibility()
{
if (campaignMode == CampaignMode.Normal)
{
SetNormalVisibility(true);
}
else
{
SetNormalVisibility(false);
}
}
HTH
Jonathan
private void referenceDesk_DoubleClick(object sender, EventArgs e)
{
tabControl1.TabPages.Add(new TabPage("Donkey Kong"));
}
there is no tabControl1.Modifier type command to use, and also can't use
new public TabPage("");
The Modifiers design-time property, controls member creation for the object you are modifying. It is not something you can change later. If you want to add tab pages to a tab control and you want to be able to change them later, define class members for them and assign appropriate access-modifier to them:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private List<TabPage> tabPages;
private void referenceDesk_DoubleClick(object sender, EventArgs e)
{
tabPages = new List<TabPage>();
tabPages.Add(new TabPage("First"));
tabPages.Add(new TabPage("Second"));
foreach (var tab in tabPages)
tabControl1.TabPages.Add(tab);
}
....
}
Designer code is not supposed to be user modified, as it gets re-written by Visual Studio every time you make changes to your form in the designer (as you have discovered).
One way forward it to move the control declaration and initialization to the non designer code file. However, that means your control will no longer appear in the designer.
I have a listBox that I would like to carry out a method when its loaded up, although I can't use the Form "On_Load" trigger, since the ListBox is within a TabControl.
Is there a way of getting a method to execute when the object initializes?
The closest analog for controls is the HandleCreated event. This will fire when the underlying control handle is created, which is slightly before the Loaded event of the parent window would fire.
As #SLaks stated, you could put in your class's constructor. However, if what you want to prepare relies on other elements in the form, you can add to the event handler queue at the end of a form loading, but before its actually presented to the user.
In the constructor code of your form (not the designer code), add to the load event, then add your own custom function
public partial class frmYourForm : Form
{
public frmYourForm()
{
Load += YourPreparationHandler;
}
private void YourPreparationHandler(object sender, EventArgs e)
{
// Do you code to prepare list, combos, query, bind, whatever
}
}
Have the same problem, the previous answers apply well, for a single case.
But, I require to do something in most controls, in several forms, in an app.
Solved by using an interface:
interface IOnLoad
{
void OnLoad();
}
And added to descendant control:
public partial class MyButton : Button, IOnLoad
{
void OnLoad() { // call "OnLoadDelegate" }
}
public partial class MyForm : Form
{
public void MyForm_Load(...) {
foreach(Control eachControl in Controls) {
if (eachControl is IOnLoad) {
IOnLoad eachOnLoadControl = (IOnLoad)eachControl;
eachOnLoadControl.OnLoad();
}
} // foreach
}
} // class
Its more complex, but it suit my requirements.
You can use OnHandleCreated(EventArgs e). However, it triggers during design time too. You can override it too.
Can you use the HandleCreated event?
You can just put your code in the constructor.
You usually don't need to wait for any initialization.
I'm refactoring some code and I've gotten into the practice of doing this:
protected void Page_Init(object sender, EventArgs e)
{
Logger.Info("Page Initialization.");
//Provides highlighting/docking functionality at the start, but overshadows controls in more complex scenarios.
RadDockZone1.Visible = (RadControlStates.SplitterStates.Count == 0);
ControlRegeneration.RegenerateReportMenu(lstBxHistorical, lstBxCustom);
ControlRegeneration.RegeneratePaneChildren(RadPane2);
ControlRegeneration.RegenerateDockZones(Page);
ControlRegeneration.RegenerateDocks(RadDockLayout1, RadDock_Command, UpdatePanel1);
}
I'm wondering if it is good practice to pass Page and Page controls to other functions like this.
I was considering creating a singleton that will hold references to the relevant page controls, and then accessing the controls through that instance instead.
Something like...
public class DashboardPageControlsRepository
{
private static readonly DashboardPageControlsRepository instance = new DashboardPageControlsRepository();
private DashboardPageControlsRepository() { }
private Control myPanel;
public static DashboardPageControlsRepository Instance
{
get { return instance; }
}
public void SetPageState(Page page)
{
myPanel = Utilities.FindControlRecursive(page, "UpdatePanel1")
}
public Control Panel
{
get { return myPanel; }
}
}
Then, during page init before anything happens I would go and grab all my controls -- allowing me to access them through here rather than passing them down.
Any thoughts on how to handle this?
The problem with creating singletons in this manner is that the static instance will exist for the lifetime of the AppDomain (until it is recycled). On top of that, multiple requests accessing the singleton will be attempting to mutate the singleton's state independently.
What services would this repository offer other than as a container for control references?
The other thing I would mention, is don't specialise your methods too much, you should consider the least required type approach to method design, e.g. you currently have:
public void SetPageSize(Page page)
In which the method is only really interested in accessing the Controls collection of the System.Web.UI.Control type. You could redefine the method as:
public void SetPageSize(Control control)