Design Surface override enabled property of a control - c#

I have a little application that implements a DesignSurface as well as a propertyGrid. The propertygrid watches for a property attribute "IdeAccessable". For each property to become availdable I override the base property and add this attribute.
public class MyButton : Button {
[IdeAccessable()]
public new int Height { get { retun base.Height; } set { base.Height = value } }
[IdeAccessable()]
public new int Width { get { retun base.Width ; } set { base.Width = value } }
[IdeAccessable()]
public new bool Enabled { get { retun base.Enabled ; } set { base.Enabled = value }}
[IdeAccessable()]
public new bool Visible { get { retun base.Visible ; } set { base.Visible = value }
}
This works fine for all, but for Enabled and Visible. These two property seem not to be delegated during the "Design Time" and have no effect what so ever. During serilization the keep their default / base value. How to override enabled & visible that the work at design time / are delegated properly?
Details:
The DesignSurface is used in a standalone application written in c#. The designsurface is attached to a standard WinForm which is the base-design-surface. Items are placed by the user via the ToolBoxService. The Controls are mostly default winform-controls like buttons, labels etc. To control the amount of visible items which are shown via a PropertyEditor attached to the DesignSurface-Services, all controls have been derived (see example) to flag the properties with the custom IdeAccessableAttribute.
When these layouts are serialized during save or execution, all controls & components are represented in a script language (lua). During this serilizaion process (essentailly a loop over all children lying on the form) every control is touched and every property flagges with the IdeAccessable attribute is saved. During debugging I found that, as explained, that values like enabled and visible are not beeing set despite the property editor shows the correct value. This results that a component disabled during designtime is enabled during my own runtime. Another effect is that the property editor settings for the visible property allways is false. Changing it in the property editor shows the correct value but is never applied to the real value.
A possible solution might be:
to prevented this with a fake design time value that is transported manually.
The problem with this solution:
Due to the controls not having a shared base class the parts in question would have to be rewritten for every derived control individually which would result in a lot of redundant code. Not derived controls aren't anymore possible. Any change to the logic would mean that it has to be changed in about 50 classes. Also implementing new controls would result in much more work than nessary.

Related

How to refactor duplicated code in Windows Forms?

I'm currently working on refactoring a lot of duplicated code in a couple of UserControls in Windows Forms project.
The architecture leaves much to be desired with a lot of logic implemented inside the UI layer. This will be fixed eventually, I'm now working on improving what I have on my hands now.
The problem is, that a lot of duplicated code relates directly to controls, for instance:
private void InitDestinationPathControls(string path)
{
if (someField)
{
tbOne.Enabled = false;
tbOne.Visible = false;
btnTwo.Enabled = false;
btnTwo.Visible = false;
tbOne.Text = string.Empty;
return;
}
// (...)
}
Don't get too attached to the cited code itself, it is just an example.
I'd like to move this code to a common base class, but it relies directly on specific fields (even though they are exactly the same in all controls too). Those fields, on the other hand, are generated by the designer, so I cannot extract them to the base class.
The only thing that comes to my mind is to pass those fields as parameters to a method in base class, but then if some method uses a lot of them, I'll end up with a monstrous interface part and that won't actually improve the readability too much.
How can I deal with such common parts of user controls in Windows Forms?
Apparently you have a combination of several controls that appears in several forms. In other words: you have for instance some buttons, comboboxes, etc, that you want to show on different forms, and you want them to have the same behaviour.
If not only the behaviour of these controls is the same on all forms, but also the layout, then consider to create a class derived from UserControl.
The UserControl hides from the outside world which controls are shown, how they are visualized and how they behave.
I assume that you already pulled the model out of the visualisation of the data.
If all instances of this user control should all call the same functions of possibly a different object of the same class, then give your special user control a property that represents this class, or at least an interface. During construction you can plug in the actual class that should handle the actions after operator input.
If, on the other hand, the layout differs on each form, but the collection of buttons, comboboxes, etc and their behaviour is similar on all forms that show this collection of controls and they have a lot of common behaviour, consider to create your own ControlCollection.
For instance, if on several forms you have a button to select a (text) file, labels with the name, size and creation date of the selected file, and an edit box that shows the content of the text file, but you want to layout them differently, consider something like this:
class FileDisplayControls : IDisposable
{
public Button ButtonSelectFile {get;} = new Button();
public Label labelFileName {get; } = new Label();
public Label labelFileSize {get; } = new Label();
public TextBox textFileContents {get; } = new FileContents();
private void ButtonSelectFile_Clicked(object sender, ...)
{
// TODO: open file dialog, display result in labels and text box
}
}
Constructor can set initial layout properties of the controls, and subscribe to events, such that the controls will react on user input.
The user of the class (= code, not operator) immediately has a collection of controls that have some standard behaviour, like react on button click. All he has to do is set the location of the items in his own form. If desired change other layout properties (colour, background) and put them on his own form.
If you want to prevent that others change other visual aspects of the controls than the position, don't publish the control themselves, only the position of the control:
public System.Drawing.Point LocationSelectFileButton
{
get => this.buttonSelectFile.Location;
set => this.buttonSelectFile.Location = value;
}
public System.Drawing.Point LocationFileContentTextBox
{
get => this.textBoxFileContent.Location;
set => this.textBoxFileContent.Location = value;
}
etc.
If needed, you can add events for users:
public event EventHandler SelectedFileChanged;
public string FileName => this.labelFileName.Text;
public string FileContents => this.textBoxFileContent.Text;
etc.
Conclusion
The solution that you choose depends on the similarity between the controls on the various forms:
if Behaviour and Layout are all the same: UserControl
If only position and a few properties different: special class with the properties that are different. This way you can force a more similar style: all "Select File" buttons look similar.
If only one or two behaviours are different: add Action<...> properties or events
If you want full control of the layout: expose the Controls.
The behaviour that is common for all you forms that show these controls (in my example: how to select a file and what to do when a file is selected) is inside the class.
repeated code can be extracted to method (possibly in base class, or as static method in helper class)
public void DisableControls(params Control[] controls)
{
foreach(var c in Controls)
{
c.Enabled = false;
c.Visible = false;
if (c is TextBox t)
{
t.Text = string.Empty;
}
}
}
private void InitDestinationPathControls(string path)
{
if (someField)
{
DisableControls(tbOne, btnTwo);
return;
}
// (...)
}

Winform Custom Control: Why Setter not called when used from Constructor?

I have an initial value property like this:
[Category("Main")]
[Description("Intial Value")]
[DefaultValue(10)]
public int InitialValue
{
get { return m_initialValue; }
set {
m_initialValue = value;
this.TrackBar.Value = this.m_initialValue;
}
}
So in my constructor I do this for example:
this.InitialValue = 10;
To my surprise when dragging the custom control on a form the setter is not called so that my trackbar value is not synchronized.
Why ?
Only when I change the property in dialog box the setter is called.
I decided to take your advice as suggested in one of the comments:
You can try by yourself will take 2 minutes.
So I did (it took about 3 minutes), and I was unable to reproduce the behavior that you described.
Here are the exact steps that I followed:
Created a new Windows Forms Application.
Added a new User Control to my project.
Opened the new User Control in design view and added a TrackBar control (leaving the TrackBar control's properties all set to their defaults).
Added the following code to the User Control class (exactly the same as you posted above, with the addition of a private field m_initialValue that you omitted from the original example):
public class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
this.InitialValue = 10;
}
[Category("Main")]
[Description("Intial Value")]
[DefaultValue(10)]
public int InitialValue
{
get { return m_initialValue; }
set
{
m_initialValue = value;
this.trackBar1.Value = this.m_initialValue;
}
}
int m_initialValue;
}
Built the project.
Opened the default Form (Form1) that was created with the new project in design view.
Dragged the User Control that I had just created (UserControl1) out of the toolbox where it was automatically placed and onto the surface of the form.
The indicator on the slider bar appeared all the way to the right side (the correct and expected position given the default Maximum value of 10). Now, you tell me: What are we doing differently?
Try adding [Browsable(true)] .
The key portion of your question is here:
when dragging the custom control on a form
You're still in the designer, and the designer cheats a bit to render things. Does this still happen when you actually run the application?

How to refresh a winform custom control at design time after changing a property

Let's say I create a custom control which embed a trackbar. I also create an orientation property for my custom control.
When I drop the custom control on a form by default it will be horizontal. Then I set it to vertical, the trackbar should refresh to be vertical at design time.
How to do so ?
I think you should call Refresh() after changing the value:
public OrientationProperty Direction
{
get
{
return _direction;
}
set
{
_direction = value;
if (DesignMode)
{
Parent.Refresh(); // Refreshes the client area of the parent control
}
}
}
private OrientationProperty _direction;
Here's my solution to this issue:
1. Whenever you set something property, call Invalidate() in the setter.
2. After correspondent properties and refreshing method (for eg. overridden OnPaint) are implemented, rebuild!!! then you'll see the modifications taken effect in design time
3. During design, always check whether compilation errors are present, as this might stop VS performing all his tasks.
With this, when I put my control on a form, and adjust its own properties, refreshing happens immediately as expected.
PS.: old post, but at least verified the behavior in VS2015 too :)

Set PropertyGrid Default Popup Editor Startup Size (WinForms)

). How can you set the default size with which the Popup Editor shows up when you invoke it from a Property Grid.
This is for everybody who is familiar with Windows Forms' Property Grid Editor.
You know that if you throw a List property to a Grid, it shows the little [...] button which if you press it pops up its default sub-value editor. I actually use the editor for another type of object, but I gave this example just so you know what I'm referring to. And here's a picture, at least until the link lives:
http://www.perpetuumsoft.de/sf/en/ims/rssSilverlight/GetStart/image032.jpg
My understanding is that (both for modal and non-modal editors) it is completely up to the whim of the control being shown. If the UITypeEditor involved chooses a big form, it will be big...
The only way to change that would be to define your own UITypeEditor and associate it with the types involved (sometimes possible with TypeDescriptor.AddAttributes(...), that creates the same form as the runtime wanted to show, but resizes it before showing.
You can achieve this by inheriting from the standard System.ComponentModel.Design.CollectionEditor and then set the desired size in the CreateCollectionForm override.
Decorate your collection to use the custom collection editor.
Below is an example that will start up the collection editor in full screen
class FullscreenCollectionEditor : System.ComponentModel.Design.CollectionEditor
{
protected override CollectionForm CreateCollectionForm()
{
var editor = base.CreateCollectionForm();
editor.WindowState = System.Windows.Forms.FormWindowState.Maximized;
return editor;
}
public FullscreenCollectionEditor(Type type) : base(type)
{
}
}
And then decorate your collection property with [Editor(typeof(FullscreenCollectionEditor), typeof(UITypeEditor))] i.e.
public class MyModel
{
[Editor(typeof(FullscreenCollectionEditor), typeof(UITypeEditor))]
public List<FileModel> Files { get; set; }
}

Setting custom control properties

I though it would be very simple but I can not get it today.
I have a user control, with a grid control contained in it.
public Unit Width
{
get
{
return CustomerGrid.Width;
}
set
{
CustomerGrid.Width = value;
}
}
I expose the width property and when I set it in the designer it works at run-time but not design time.
What class do I inherit from or method to override to get my controls to function at design time.
Note I tried to inherit from WebControl but got the message
Make sure that the class defined in this code file matches the 'inherits' attribute, and that it extends the correct base class
I understand you're talking about user controls (ascx) and not about custom controls (no ascx). If this is the case, you should inherits from UserControl and you would have the property available on design time without any other addition.
In case you're talink about custom controls, here you have a good article about adding design time support to custom controls
http://msdn.microsoft.com/en-us/library/aa478960.aspx

Categories