We have several winforms applications we want to restyle and make consistent (styling wise). We want to create some base classes for some of the windows controls such as buttons and toolstrip. All my base classes intend to do is specify the specific styling we wish to apply. This works the first time I bring into my form but later on if I wish to update a property it doesn't apply it back to the forms that previously already have pulled it in. Anyway to get this to work? Here's an example, lets say I want to create a base class for a toolstrip. All I want to do is set the backcolor to green (I really want to set more properties but this is to demo my issue). So I create a class library called BaseControls and create a class called BaseToolStrip and set the backcolor to green in its constructor.
public class BaseToolStrip : ToolStrip
{
public BaseToolStrip()
{
BackColor = Color.Green;
}
}
Now if I create a demo project and add an instance of BaseToolStrip to the form it works. It creates a green toolstrip. Later if I decided I want my base toolstrip to have a backcolor of red if I update the constructor in my BaseToolStrip class and set the BackColor = Color.Red and run my solution the color is still green. It appears that once I've brought that control in it set backcolor = green. So even though in my base class I set backcolor = red in my form that instantiates it still has backcolor = green. Any way to override all instances and set backcolor equal to the color specified in my BaseToolStrip class?
Your problem is that the WinForms designer is saving the current value in InitializeComponent() in any designer that uses that control.
That happens after your ctor runs and replaces your value.
You can prevent that by overriding / shadowing the property and adding [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] (you'll need to reopen & resave all designers that use it to remove the property).
Try setting the default value for the property (as well as setting the actual value in the ctor):
[DefaultValue(typeof(Color), "Green")]
public override Color BackColor {
get { return base.BackColor; }
set { base.BackColor = value; }
}
I do not have VS available to test right now, but I think that will cause the designer to notice that the current value is the default, and therefore doesn't need to be saved in the Designer.cs.
So if you change the color in both places later, it should update everywhere.
Of course, this will only work for controls that you drag onto a form after you add this code, since the others already have the value saved.
Related
I have a class library with a custom control in it:
using System.ComponentModel;
using System.Windows.Forms;
namespace ClassLibrary1
{
public sealed class CustomLabel : Label
{
[DefaultValue(false), Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override bool AutoSize
{
get => base.AutoSize;
set => base.AutoSize = value;
}
public CustomLabel()
{
AutoSize = false;
}
}
}
Notice that AutoSize is set to false in both the constructor and the designer attribute on the overridden method.
I have a winforms project where I want to use the control. I drag/drop it from the toolbox, but it doesn't have AutoSize set to false:
If I save and close the form and then re-open it, now it's set correctly:
How can I make it respect the property value when first dropped on the form?
The default values which you assign in constructor are respected in general. But for some cases the default values will be changed using designer, for example by the CreateComponentsCore method of ToolboxItem of the control.
The default value for AutoSize property for Label is false and you even don't need to override it or set it in constructor. But an AutoSizeToolboxItem has been assigned to Label which sets AutoSize to true when you drop an instance of Label on designer. To remove this behavior, it's enough to assign a new ToolboxItemto your control:
using System.ComponentModel;
using System.Drawing.Design;
using System.Windows.Forms;
namespace ClassLibrary1
{
[ToolboxItem(typeof(ToolboxItem))]
public sealed class CustomLabel : Label
{
}
}
Note 1: Just for your information, the ToolboxItem has a CreateComponentsCore method which you can use it to to some initialization tasks when dropping control on design surface.
Note 2 I should also add, the CreateComponentCore method will just run when you drop the component from toolbox to design surface. It describes why after dropping it on form, it's auto-size, because it's set by CreateComponentCore after your constructor. But after you open the form again, this time, just your constructor will run and set the property to false.
The DefaultValueAttribute has no bearing on it: it mainly controls whether the property value should be serialized or not and whether the value should show in bold in the property editor window.
If you watch the designer code, initially it gets written out explicitly saving AutoSize as true. Apparently it saved the value because it doesnt match the value specified by the DefaultValue but it is saving the wrong value - apparently the base control hasnt gotten the update yet. Any change causes it to serialize the form again, this time with the correct value.
I dont know exactly why certain properties dont like being overridden and changed from the constructor, but there are a few that don't immediately take. AutoSize is one that gets handled thru SetStyle calls and/or thru some CommonProperties helper.
One way to set some of these is to implement ISupportInitialize to set the value after the control has been set from the designer properties. A simpler way is to override OnHandleCreated:
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
base.AutoSize = false;
}
Seems to work as desired.
I have a very annoying problem I'm trying to solve for couple of weeks. I have a WinForms C# project where I developed my custom control (ListView + ToolStrip with ToolStripButtons). This control is used in different forms inside solution - but in other projects. For different forms I need to make certain buttons visible or hidden, so I have added to my control corresponding properties like
public Boolean DeleteButtonVisible
{
get
{
return tsbDelete.Visible;
}
set
{
tsbDelete.Visible = value;
}
}
Some buttons are visible by default, some are hidden. In designer when editing a form with my control I'm able to change those properties, buttons on control become visible or hidden as they should. But every time I'm changing anything in my control source file in all forms those properties are reset to default values regardless of what I have set in designer and I have to restore those values manually. Well, I'm using a source control so this is not that hard, but performing "Undo" on a couple dozen of files every time I change a bit in another file is a damn disaster.
I have tried to use [DesignerSerializationVisibility] attribute to fix this issue. If I used it with value "Hidden" it didn't do any good at all - values were just not saved. "Content" made buttons randomly disappear even if by default they were visible. "Visible" lead to no effect, as this is default value...
I don't want to set every button visibility for every form in my code - this is just not the way it should be done.
Does anyone know something about this?
Yes, the Control.Visible property is special. The getter does not return the last assigned value, it only returns true when the control is actually visible. That can have side-effects, you've found one. In this case probably induced when the control switches out of design mode. To do this correctly, you must store the assigned state in a backing variable. Like this:
private bool tsbDeleteVisible;
public bool DeleteButtonVisible {
get { return tsbDeleteVisible; }
set { tsbDelete.Visible = tsbDeleteVisible = value; }
}
Be sure to initialize the default value of the backing variable to the default value of tsbDelete.Visible. Use the constructor to be sure.
I created SubCtrl inheriting UserControl. It has no code.
I created then Ctrl, which also inherits UserControl. It has a SubCtrl in it and its only code means to expose it publicly so it appears in the property list of Ctrl:
public subctrl.SubCtrl SUBCTRL
{
get { return this.subCtrl1; }
}
Then I created a simple Form project which only has a Ctrl in it and no code.
As I wanted, SUBCTRL appears in the property list of Ctrl so I can change things. I changed the background color (say, to red), and the subctrl turned red in the designer.
But magically, when I run the project, it turns back to the standard gray. It appears that no code is generated in Form1.Designer.cs to change SUBCTRL's back color to red. If I write it by hand, it works, but that's not what I want. It should be automatic, obviously.
The Ctrl, on the other hand, behaves normally. The code is generated and everything works happily.
What's wrong with the subcontrol?
Add [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] to the property.
I've got a really weird problem and i'm wondering if it's a visual's bug or if i'm doing something wrong. Here's a simple code of an overriden Panel class:
public class MyPanel : Panel
{
private TableLayoutPanel table = new TableLayoutPanel();
public MyPanel()
{
this.Controls.Add(table);
table.BackColor = Color.Green;
}
public override System.Drawing.Color BackColor
{
get
{
return table.BackColor;
}
set
{
table.BackColor = value;
}
}
}
If i put the control on a form and build the project, visual will generate an error and opening the project again will be impossible. However if i change TableLayoutPanel to TextBox, it works fine. Also, if i set the BackColor in the constructor before adding the control to the Controls collection, it also works fine.
What is the problem? or is it just a bug?
I suspect recursion may be an issue; by default (if not set explicitly) a control inherits color from the parent. This leads to the scenario where the child's color (if not set) asks the parent, which asks the child (forever).
TextBox, however, overrides this behaviour to return SystemColors.Window if there isn't an explicit color set. Hence no recursion.
Either way, I'm not sure this is a good idea - the designer might start duplicating controls if you aren't careful.
I have a user control panel that has two buttons on it. Other user controls inherit from this control and set a property to true if the buttons should be visible. Everything runs how I want it to but what I'm looking for is a way to clear these buttons from the designer window for forms where this property is left at false.
looks like:
[DefaultValue(false)]
public bool ShowButtons{
set
{
mShowButtons = value;
UpdateButtons();
}
get
{
return mShowButtons;
}
}
This property shows in the properties window and the buttons are always shown in the designer window. Is there some way to have the designer evaluate this when the property is changed to get the buttons to clear from the inheriting form? I was unable find a designer attribute to do this.
Try adding a get:
bool mShowButtons;
[DefaultValue(false)]
public bool ShowButtons
{
get
{
return mShowButtons;
}
set
{
mShowButtons = value;
UpdateButtons();
}
}
Now when editing your derived class in the Designer, you should be able to see a ShowButtons property in properties window when the derived UserControl is selected. (It will be in the "Misc" section unless you add the appropriate attribute). If you set it there, it should have the appropriate affect in the Designer (Assuming the contents of the UpdateButtons() function work correctly)).
A property must be public and have bot get and set in order to display in the Properties editor window. Once it is, then setting the value in the properties window will "save" that setting for the designed control in the control's resources/implementation.
I use this functionality quite often to specialize derived UserControls, so I know it should work for you (although there may be other issues at play).