Create a designer-aware UserControl collection to be used runtime - c#

I have a Panel I want to fill with some UserControl(s) at runtime. These controls are complex and may be interdependent, so I'd like them:
to be editable with Visual Studio designer;
to be in the same context (= defined in the same class);
Both of the requirements are a must-have.
Considering UserControl is itself an indexed collection of Control(s), I started designing my controls in the same class, without caring about real positioning of them (that will be specified runtime). I already used the same identical approach with DevComponents ribbon containers (with much satisfaction), so I initially thought the same was possible with standard UserControl(s).
I eventually realized that a Control can be inside only one Control.ControlCollection instance at a time, so I couldn't use the Controls property and add controls to another panel without removing them from the original "dummy" UserControl.
My question is: is there a clean and supported way to create this designer-aware UserControl collection? I would call this approach a pattern because it really adds code clearness and efficiency.
Thanks,
Francesco
P.S.: as a workaround, I created a DummyControlContainer class that inherits UserControl and keeps a Dictionary map filled at ControlAdded event (code follows). Wondering if there's something cleaner.
public partial class DummyControlContainer : UserControl
{
private Dictionary<string, Control> _ControlMap;
public DummyControlContainer()
{
InitializeComponent();
_ControlMap = new Dictionary<string, Control>();
this.ControlAdded += new ControlEventHandler(DummyControlCollection_ControlAdded);
}
void DummyControlCollection_ControlAdded(object sender, ControlEventArgs args)
{
_ControlMap.Add(args.Control.Name, args.Control);
}
public Control this[string name]
{
get { return _ControlMap[name]; }
}
}

After testing and using it in a real world project, I'm more and more convinced that my solution was clean and safe if you need such a pattern. This container is intended to be filled with controls such as panels or similar. To prevent some bad behaviors with bindable data sources, I provided each new control added to this container with its own BindingContext. Enjoy.
public partial class DummyControlContainer : UserControl
{
private Dictionary<string, Control> _ControlMap;
public DummyControlContainer()
{
InitializeComponent();
_ControlMap = new Dictionary<string, Control>();
this.ControlAdded +=
new ControlEventHandler(DummyControlCollection_ControlAdded);
}
void DummyControlCollection_ControlAdded(object sender,
ControlEventArgs args)
{
// If the added Control doesn't provide its own BindingContext,
// set a new one
if (args.Control.BindingContext == this.BindingContext)
args.Control.BindingContext = new BindingContext();
_ControlMap.Add(args.Control.Name, args.Control);
}
public Control this[string name]
{
get { return _ControlMap[name]; }
}
}

Related

WinForms Inheritance InitializeComponent

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.

How to access a custom control dependency propery from its viewmodel

I'm working on a multiple document viewer (a simple window with a custom control, each with a separate viewmodel). When clicking on a filename, a new instance of the user control is added to the main window. The user control has a dependency property which holds the path to the filename, defined in it's code-behind. Now i'm struck on how to get the value of this property from the user control to the viewmodel, so it can show the actual document. Any Hints?
<ctrl:DocViewerControl x:Key="docviewer" DocumentSource="{Binding SelectedItem.Path, ElementName=docList}"/>
I use this code in main window to make new instances of my user control where DocumentSource is the dependency property i need access to, as stated above.
Edit:
Following is the (relevant) code for the view and the viewmodel of my control, specific to the dependancy property value capture problem i have.
UserControl.xaml.cs
public partial class ToolboxControl : UserControl
{
public static readonly DependencyProperty DocumentSourceProperty = DependencyProperty.Register("DocumentSource",
typeof(string), typeof(ToolboxControl), new UIPropertyMetadata(new PropertyChangedCallback(OnDocumentSourceChanged)));
public ToolboxControl()
{
InitializeComponent();
}
public string DocumentSource
{
get { return (string)GetValue(DocumentSourceProperty); }
set { SetValue(DocumentSourceProperty, value); }
}
private static void OnDocumentSourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
}
}
PV_ViewModel.cs
public class PV_ViewModel : ObservableObject
{
.....
public string DocumentSource
{
get { return (String.IsNullOrEmpty(_documentsource)? (_documentsource = #"about:blank") : _documentsource); }
set { SetField<string>(ref _documentsource, value, "DocumentSource"); }
}
.....
public PV_ViewModel()
{
PropertyChanged += DocumentSourceChanged;
}
.....
protected void DocumentSourceChanged(object sender, PropertyChangedEventArgs e)
{
if (sender != null)
{
switch(e.PropertyName)
{
case "DocumentSource":
{
// show the document and whatsoever
break;
}
}
}
}
.....
}
Neither the getter nor the setter of the viewmodel DocumentSource property get accessed from anywhere, despite the UserControl in MainWindow had is DocumentSourceProperty filled in with the current document path string. (i can see it form a collection of currently opened document on the main app).
To clarify: the application solution contains MainWindow project (the main view, a simple window with a TreeView and the UserControl container), the UserControl project (the (hopefully) standalone application used to show a single document when providing the path to the doc to show through the DocumentSource property.
I am not really sure I understand your problem (or if you understand how Dependency Properties work), so you may have to post a bit more of your code behind (with the DI for example)
Typically your DocViewerControl looks like this
public abstract class DocViewerControl : UserControl
{
public string Path
{
get { return (string)GetValue(PathProperty); }
set { SetValue(PathProperty, value); }
}
public static readonly DependencyProperty PathProperty =
DependencyProperty.Register("Path", typeof(string), typeof(DocViewerControl), new PropertyMetadata(string.Empty));
}
This will expose a Property in XAML of the control.
It's important here that you make it TwoWay binding, so any change from the UserControll will update the bounded field in your ViewModel.
Your ViewModel:
public class Doc1ViewModel : ViewModelBase {
private string path;
public string Path
{
get { return path;}
set {
if(path!=value) {
path = value;
OnPropertyChanged("Path");
}
}
}
}
Now, each time when you assign the property in your UserControl, the value in the ViewModel will be updated. As you can see, the Dependency Property consists from two properties. One static Dependency Property called PathProperty and one instance property called Path.
But having a closer look at it, it's not a real instance property at all. It just wraps calls around the Dependency Property by using GetValue and SetValue (which are derived from DependencyObject class, which every UI control inherits).
Hope this clears it up how Dependency Properties work, as it hard to tell what's wrong with your approach without seeing the code used.
In a nutshell, Dependency Properties (together with Attached Properties) extend the XAML code with TwoWay bindable properties (normal instance property can only be bound in one direction).

Retrieve all winforms components in all subclasses

I would like to retrieve all components which are part of a Form's or UserControl's components collection.
The components collection is added by VS winforms designer. The components variable is private and the problem is how to retrieve all components from all descendants. I would like to have a method which returns list of components throught the type hierarchy. For example let's say I have MyForm (descendant of BaseForm) and BaseForm (descendant of Form). I would like to put method "GetComponents" which returns components of both MyForm and BaseForm.
Do you suggest any other option than using the reflection?
Some time ago I have implemented the solution in which I created custom base form and control implementations, adding one property and overriding the OnLoad method:
public partial class FormBase : Form
{
public FormBase ()
{
this.InitializeComponent();
}
protected ConsistencyManager ConsistencyManager { get; private set; }
protected override void OnLoad(System.EventArgs e)
{
base.OnLoad(e);
if (this.ConsistencyManager == null)
{
this.ConsistencyManager = new ConsistencyManager(this);
this.ConsistencyManager.MakeConsistent();
}
}
}
The ConsistencyManager class finds all controls, components and also supports search of custom child controls within specific control. Copy/paste of code from MakeConsistent method:
public void MakeConsistent()
{
if (this.components == null)
{
List<IComponent> additionalComponents = new List<IComponent>();
// get all controls, including the current one
this.components =
this.GetAllControls(this.parentControl)
.Concat(GetAllComponents(this.parentControl))
.Concat(new Control[] { this.parentControl });
// now find additional components, which are not present neither in Controls collection nor in components
foreach (var component in this.components)
{
IAdditionalComponentsProvider provider = GetAdditinalComponentsProvider(component.GetType().FullName);
if (provider != null)
{
additionalComponents.AddRange(provider.GetChildComponents(component));
}
}
if (additionalComponents.Count > 0)
{
this.components = this.components.Concat(additionalComponents);
}
}
this.MakeConsistent(this.components);
}
If anyone would like full sample or source let me know.
Best regards,
Zvonko
PS: In the same manner I have also created the performance counter that counts number of invocations on main thread.

Are there any caveats to swapping out DesignMode for LicenseManager.UsageMode in a WinForms UserControl constructor?

If you have a Form that displays data, one thing you can do is reference this.DesignMode in the constructor to avoid populating it in the designer:
public partial class SetupForm : Form
{
private SetupItemContainer container = new SetupItemContainer();
public SetupForm()
{
InitializeComponent();
if (!this.DesignMode)
{
this.bindingSource1.DataSource = this.container;
this.Fill();
}
}
}
However, if you decide to re-write that form as a UserControl, keeping the same constructor logic, something unexpected happens - this.DesignMode is always false no matter what. This leads to the designer invoking your logic that's meant to happen at runtime.
I just found a comment on a blog post that seem to give a fix to this but it references functionality of the LicenseManager class as a replacement that works as expected in a UserControl.
So for a UserControl I can do:
public partial class AffiliateSetup : UserControl
{
private AffiliateItemContainer container = new AffiliateItemContainer();
public AffiliateSetup()
{
InitializeComponent();
if (LicenseManager.UsageMode == LicenseUsageMode.Runtime)
{
this.bindingSource1.DataSource = this.container;
this.Fill();
}
}
}
Does using the LicenseManager instead of DesignMode have any caveats or implications that might dissuade me from putting in my production code?
According to someone who posted a comment on my answer to another question, using LicenseManager doesn't work in an OnPaint method.

Best practice for accessing Page / controls on Page from other classes?

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)

Categories

Resources