How to force a certain UserControl design - c#

I am writing a Base UserControl, that will be inherited by a bunch of other UserControls. I need to enforce a certain design for all these descendant controls (e.g. a couple of buttons must be on the top along with a label or two).
The rest of the descendant UserControl area is free to have whatever on it.
Initially, I thought that I could just plop a Panel onto the Base UserControl, set the Dock=Fill and the designer of the descendant control would be forced to add all the UI into this said panel. Then, I could resize the panel to my content.
But that is not the case - when you drop a control (say a GridView) onto the descendant UserControl, it adds it to the .Controls collection of the descendant user control, not the Panel I added.
Is there a way to force a certain layout from the Base user control?

The short answer is "Yes"... However to get this behavior delves into the ugly world of writing your own designers which you have to associate with the each control which will need to inherit the special placement into the content panels..
The following link should be a good starting point as it gives an overview of what needs to be done as well as provides sample code.
http://support.microsoft.com/?id=813808
Be warned, however, this is not a trivial task. And there is a lot of code (30+ files) to sift through in order to understand how to implement a designer. It's been about 2 years since I have tried this.
On the other hand, have you considered changing your design to have a single parent control which has the labels and buttons, and which populates the content area with the appropriate child control(s)? Perhaps even having your child controls for the content area implement a specific interface to guarantee a contract so that the parent can interact with them without having to know a specific class name?

AngryHacker -
I went off thinking this was to be very easy, and found myself embarking on an interesting journey into WinForm design-time land. This is definitely not as easy as it was in VB6 :-) .
Anyhow, after a bit of research, I found a number of references to a method called EnableDesignMode(). However, this is not directly accessible from a WinForm component. This has to be called from a class that subclasses ParentControlDesigner, which is injected into the UserControl via a Designer attribute. In this project, I have called the subclass ButtonBarDesigner, and have overridden the Initialize() method, which allows me to tell the designer that the component ButtonBar has a child component fillPanel which can be accessed via the public property "FillPanel".
At this point, it seemed to be working. I managed to drop a control onto the ButtonBar and it was appearing in the fill panel. However, if you saved the form, and then reloaded it, it turned out that the control was instantiated, but not placed in the ButtonBar control. It seems that there was one sneaky last bit which the documentation for EnableDesignMode() conveniently leaves out. You need to have the DesignerSerializationVisibility attribute on the FillPanel property. Adding this attribute makes this work.
You need a reference to System.Design in your project for the design time stuff to work.
The following code is for the putative base class, ButtonBar:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Threading;
namespace ForceUserControl
{
[Designer(typeof(ButtonBarDesigner))]
public partial class ButtonBar : UserControl
{
public ButtonBar()
{
InitializeComponent();
}
/// <summary>
/// Returns inner panel.
/// </summary>
/// <remarks>Should allow persistence.</remarks>
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Panel FillPanel
{
get { return fillPanel; }
}
}
private class ButtonBarDesigner : ParentControlDesigner
{
public override void Initialize(IComponent component)
{
base.Initialize(component);
Panel fillPanel = ((ButtonBar)component).FillPanel;
// The name should be the same as the public property used to return the inner panel control.
base.EnableDesignMode(fillPanel, "FillPanel");
}
}
}

Related

C# WinForms FlowLayoutPanel locking child controls

I have a base form containing some elements like this:
- pnlSearch of type Panel: search button
- pnlActions of type FlowLayoutPanel: add, edit, delete, export.. etc buttons
nothing is locked, both panels' modifiers are Private & buttons' modifiers are Protected
FlowLayoutPanel is used to customize options in the child forms (e.g. removing the delete option) without leaving empty spaces since the elements will flow accordingly.
In a child form, the search button only is accessible. Buttons in pnlActions are locked in the designer but by checking the properties Locked = False and Modifiers= Protected
Tried setting the pnlActions' modifiers to Protected but it's still the same.
Any idea what's causing this behavior?
what's the difference between Panel and FlowLayoutPanel other than inner controls layout?
I'd post code samples if I've hand-coded anything but it's all generated by designer
I'm using VS 2013 on Win7 if that would matter
Thanks in advance
this is a problem of the Designer. if you do your changes via code all work...
The problem won't be resolved because the platform is not mainteined by Microsoft anymore.
I know it's an old question, but I share a possible solution, in case someone needs it.
We will create a class that inherits FlowLayoutPanel, I name it FlowLayoutPanelHeritable. You can place it in the namespace that you consider appropriate, for this example the namespace is WindowsFormsApp.
using System.ComponentModel;
using System.Windows.Forms;
using System.Windows.Forms.Design;
namespace WindowsFormsApp
{
[Designer(typeof(ParentControlDesigner))]
public class FlowLayoutPanelHeritable : FlowLayoutPanel
{
}
}
First we must set the modifiers of the FlowLayoutPanel that is in FatherForm, in Protected. Now we must make a modification by code in FatherForm, accessing FatherForm.designer.cs. We will replace each instance of FlowLayoutPanel, by FlowLayoutPanelHeritable (in the creation, and initialization of variables). Then we save, and recompile.
Now if you access ChildForm, you will see that you no longer have the modification restriction at design time.
Important: if you want to edit FatherForm, you will have to access FatherForm.designer.cs and replace all the FlowLayoutPanelHeritable to FlowLayoutPanel again; When you finish editing, perform the reverse process.

Editing controls embedded in custom control (C#, WinForms) [duplicate]

I'll try to explain what I'm after. I don't know the technical term for it, so here goes:
Example 1:
If I place a ListView on a Form and add some columns I am able, in Design-Time, to click-and-drag the columns to resize them.
Example 2:
Now, I place a ListView in a UserControl and name it "MyCustomListView" (and perhaps add some method to enhance it somehow).
If I now place the "MyCustomListView" on a Form I am unable to click-and-drag the column headers to resize them in Design-Time.
Is there any way to easily make that happen? Some form of "pass the click-and-drag event to the underlying control and let that control do its magic". Im not really looking to recode, just pass on the mouseclick (or whatever it is) and let the, in this case, ListView react as it did in the first example above.
The Windows Forms designer has dedicated designer classes for most controls. The designer for a ListView is System.Windows.Forms.Design.ListViewDesigner, an internal class in the System.Design.dll assembly. This class gives you the ability to drag the column headers.
A UserControl uses the System.Windows.Forms.Design.ControlDesigner designer class. It doesn't do anything special, just puts a rectangle around the control with drag handles. You can see where this is heading: after you put your user control on a form, it is ControlDesigner that is used to design the class, ListViewDesigner is not in the picture. You thus lose the ability to drag the column headers. Also note that ControlDesigner doesn't give access to the controls inside the UC.
That's fixable however by creating your own designer. Start with Projects + Add Reference, select System.Design. You'll need to add a public property to the UC to expose the list view and apply the [DesignerSerializationVisibility] attribute to allow changed properties to be saved. And apply the [Designer] attribute to the UC class to replace the default designer. It all should resemble this (using the default names and a ListView that displays "employees"):
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design; // Note: add reference required: System.Design.dll
namespace WindowsFormsApplication1 {
[Designer(typeof(MyDesigner))] // Note: custom designer
public partial class UserControl1 : UserControl {
public UserControl1() {
InitializeComponent();
}
// Note: property added
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ListView Employees { get { return listView1; } }
}
// Note: custom designer class added
class MyDesigner : ControlDesigner {
public override void Initialize(IComponent comp) {
base.Initialize(comp);
var uc = (UserControl1)comp;
EnableDesignMode(uc.Employees, "Employees");
}
}
}
The list view in the user control can now be clicked and designed as normal.

Forms editor automatically adding child control to WPF Integration ElementHost

I'm attempting to use the WPF TextBox in a WinForms application while completely encapsulating WPF-related details within another assembly, but the Forms editor is not making it simple.
Namely, the Child accessor is always being assigned to a new System.Windows.Controls.TextBox, even if the accessor is replaced with another datatype using new and smothered with various attributes that should cause it to be ignored. Removing the entry causes it to be regenerated by the forms editor.
The value is assigned by the control itself, and additionally ruins the encapsulation I hope to achieve.
Is there a way to prevent the Forms editor from automatically generating the Child?
//
// textBox_SpellCheck1
//
this.textBox_SpellCheck1.Location = new System.Drawing.Point(12, 12);
this.textBox_SpellCheck1.Name = "textBox_SpellCheck1";
this.textBox_SpellCheck1.Size = new System.Drawing.Size(200, 100);
this.textBox_SpellCheck1.TabIndex = 0;
this.textBox_SpellCheck1.Text = "textBox_SpellCheck1";
//The Forms editor should not be generating the following line:
this.textBox_SpellCheck1.Child = new System.Windows.Controls.TextBox();
Example which reproduces the issue, when it is placed within a form:
using System;
using System.ComponentModel;
using System.ComponentModel.Design.Serialization;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Controls; //reference PresentationCore, PresentationFramework
using System.Windows.Forms.Integration; //reference WindowsFormsIntegration
using System.Windows.Forms.Design;
namespace wtf
{
[Designer(typeof(ControlDesigner))] //reference System.Design
public class TextBox_SpellCheck : ElementHost
{
private System.Windows.Controls.TextBox textbox;
public TextBox_SpellCheck()
{
textbox = new System.Windows.Controls.TextBox();
base.Child = textbox;
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
[EditorBrowsable(EditorBrowsableState.Never)]
[DefaultValue(0)]
public new int Child { set { } get { return 0; } }
}
}
Edit:
The three workarounds I've found thus far. Added here because none of these qualify as an answer.
Let the Forms editor take care of allocation of the TextBox.
Unacceptable, due to the above desire of the encapsulation of WPF details and assemblies.
Don't let the Forms editor manage the component at all.
Annoying at best. It would be much preferred to create and manage the components using the Forms editor.
Place the TextBox_SpellCheck (ElementHost) within a UserControl.
Works as long as the Forms editor doesn't regenerate the designer code for the UserControl, if not built manually in the first place. However, this adds a layer of unnecessary control nesting.
Further Information:
Removal of the Designer attribute on the TextBox_SpellCheck makes matters worse, causing a separate hosted component to be generated within the designer code.
Using different types either doesn't improve matters, or makes them worse.
A few examples:
ParentControlDesigner still generates the child element.
ScrollableControl still generates the child element.
DocumentDesigner throws exceptions that cause the Forms editor to be unusable.
System.ComponentModel.Design.ComponentDesigner generates the control as an indirectly usable component, such as adding data sources or whatever via the Forms editor.
Not sure you'll find a way if you're set on doing it that way. ElementHost by design uses Child for the exact reason you're using it for - you're hosting an WPF element inside of a Windows Form control. Its always going to generate the code you are adverse to in the designer.

How prevent container control from being moved in user control during design-time?

I have a custom control that contains 6 panel controls that act like containers for other controls that are dropped in during design time. This was done by creating a custom designer inheriting from ParentControlDesign. In the designer, I use EnableDesignMode to enable design time functionality for each panel control. The problem is that when I use the control, I can move the panels around. What can I do to prevent them from moving while in design time?
I couldn't find an ideal solution for this problem, just a partial one. I'll assume you enabled design mode for the panels following the approach in this answer.
Altering the design behavior of the panels requires given them their own designer. Here's a sample class that does this:
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Windows.Forms.Design;
[Designer(typeof(MyPanelDesigner))]
public class MyPanel : Panel {
private class MyPanelDesigner : ScrollableControlDesigner {
public override SelectionRules SelectionRules {
get { return SelectionRules.None; }
}
}
}
Replace the panels in your UC with MyPanel. The custom SelectionRules property ensures that the user cannot easily drag the panel into another position with the mouse. The Location and Size properties are however still editable in the property grid. To get rid of that I think you'll need to override PreFilterProperties().

How can I easily keep consistent UI settings in C# Winform application?

I have a lot of different UserControls and would like to maintain consistent UI settings (mainly colors and fonts). My first try was this:
public class UISettings
{
//...
public void SetupUserControl(ref UserControl ctrl)
{
ctrl.BackColor = this.BackColor;
}
}
to be called in every control like this:
settings.SetupUserControl(ref this);
As this is read-only it cannot be passed by ref argument so this does not work. What are other options to keep consistent UI without manually changing properties for every item?
Inheritance! If you have a form or control that will constantly be using the same styles and you want to set that as your base, just create your own user controls that inherit from a form/control. By default all of your forms will inherit from "Form". Instead of inheriting from the default form, create a new user control that inherits from Form, and then have that as your base class.
CustomForm : Form // Your custom form.
Form1 : CustomForm // Inherit from it.
...the same works for components. If you want a button to have the same styles across the board, create a user control and have it inherit from the button control -- then use the custom control.
Whenever you want to make a change to your base styles, or any settings, simply change your custom controls settings -- your new forms/controls will automatically be updated!
Do the same thing. Don't pass it by ref. UserControl is a reference object already, so there's no need to pass it into your method using the ref keyword.
You may also want to consider a recursive method that will find all the UserControls on the form and pass it into your method.
How about a base class which provides such settings?
Two answers:
You don't need ref, controls are objects are reference types. Just drop it.
Create a Base UserControl and derive your controls form that base. You can still do that, just edit the class definitions of the controls. For new controls you can follow the Wizard.
A tip: setup the styling in the baseControl. Then make sure the derived controls don't override, the best way to do that is scanning the *.Designer.cs files and remove all settings that you know should come from the base.

Categories

Resources