I have my Control.When I change the properties of the control. I get this:
this.myLabel1.BorderShadow = true;
this.myLabel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
And I need to get this:
this.myLabel1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.myLabel1.BorderShadow = true;
How to do so is done automatically in Form.Desinger.cs ?
If you say Why?
private bool BorderShadow_ = false;
public bool BorderShadow
{
get
{
return BorderShadow_;
}
set
{
if (Border_Style_ == BorderStyle.FixedSingle)
{
BorderShadow_ = value;
}
else
{
throw new ArgumentOutOfRangeException("BorderShadow", "BorderShadow can be true if BorderStyle=FixedSingle");
}
}
}
You could look into the ISupportInitialize interface. It allows you to skip the validity check when initializing your controls. For example (adapted from one of my projects):
public class MyControl : Control, ISupportInitialize
{
private bool _created = true;
public void BeginInit()
{
_created = false;
}
public void EndInit()
{
_created = true;
//check all your properties here too
}
private bool BorderShadow_ = false;
public bool BorderShadow
{
get
{
return BorderShadow_;
}
set
{
BorderShadow_ = value;
if (_created && Border_Style_ != BorderStyle.FixedSingle)
throw new ArgumentOutOfRangeException();
}
}
}
If I remember correctly, the VS designer will automatically add calls to BeginInit and EndInit for you as well.
I think they will appears in the same order as they are defined. So you can override with new old setting:
public new BorderStyle BorderStyle
{
get {return base.BorderStyle;}
set {base.BorderStyle = value;}
}
and then declare your BorderShadow setting.
The designer will always order the properties alphabetically and this can't be changed.
The sense of a property is that it is side effect free and that it can be changed at any time in any order. This means that if you have multiple properties which representing some kind of complex state and not all combinations are making sense, this error should not be reported while switching the property itself.
So to accomplish these problems you have two possibilities:
Like #Andrew already mentioned implement ISupportInitialize and take care if you are within this state.
Within the property setter call a method that checks if all settings currently made are making sense and perform the desired action only in this case:
public class MyControl : Control
{
private bool _BorderShadow;
private BorderStyle _BorderStyle;
public bool BorderShadow
{
get { return _BorderShadow; }
set
{
if(_BorderShadow != value)
{
_BordeShadow = value;
ApplyBorderShadowIfNeeded();
}
}
}
public BorderStyle BorderStyle
{
get { return _BorderStyle; }
set
{
if(_BorderStyle != value)
{
_BorderStyle = value;
ApplyBorderShadowIfNeeded();
}
}
}
private void ApplyBorderShadowIfNeeded()
{
if(_BorderStyle == BorderStyle.FixedSingle
&& _BorderShadow)
{
// ToDo: Apply the shadow to the border.
}
}
}
Related
I made a textbox that only accepts numbers and a "-" for negative numbers. I would like there to be an option to disable negative numbers.
In the constructor method I want to reference the allowNegatives bool that is defined properties editor and do different things depending on if it allows negatives values. I'm running into the problem that the 'allowNegatives' bool is always its default value in the constructor. If I reference it elsewhere it is the correct value.
Is there an way to get the assigned property value rather than the default value in the constructor?
public partial class ControlIntEntry : TextBox
{
private bool allowNegatives = false;
[Description("Allow negative values"), Category("Behavior")]
public bool AllowNegatives
{
get { return allowNegatives; }
set { allowNegatives = value; }
}
public ControlIntEntry()
{
// user sets AllowNegatives to true using properties editor
InitializeComponent();
Console.WriteLine(allowNegatives); // returns false
if (allowNegatives)
{
//do one thing
}
else
{
// do something else.
}
Task.Run(() => AfterConstructor()); // use for testing
}
private async Task AfterConstructor()
{
await Task.Delay(1000);
Console.WriteLine(allowNegatives); //returns true
}
}
Before you can assign a value to an instance property, the class should be instantiated, so first constructor will run and then you can assign property values.
That said, to have a better understanding of what is happening here, when you drop an instance of a control on your form at design time and set some of its properties, designer will generate a code like this:
private void InitializeComponent()
{
...
this.myControl1 = new MyControl();
...
//
// myControl1
//
this.myControl1.Location = new System.Drawing.Point(0, 0);
this.myControl1.Name = "myControl1";
this.myControl1.Size = new System.Drawing.Size(100, 22);
this.myControl1.MyProperty = true;
...
}
I believe it's now clear that what is happening here. You see first the constructor of your control will run, then later property values will be set.
To use property values to configure your object can put the logic inside the setter of the property:
private bool myProperty = false;
public bool MyProperty
{
get { return myProperty;}
set
{
myProperty = value;
// some logic here.
}
}
It's the most common scenario.
Another option is delaying the initializations to some time later, for example when the control handle is created by overriding OnHandleCreated or another suitable time.
// This is just an example, the event may not be a good one for your requirement
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
// some logic here
}
Another option for complex initialization scenarios which may involve multiple properties, you can implement ISupportInitialize and put the logic inside EndInit:
public class MyControl : TextBox, ISupportInitialize
{
public void BeginInit()
{
}
public void EndInit()
{
// some logic here
}
}
Then when you drop an instance of the control on the form, this code will be generated in addition to the common code that I showed at beginning of this answer:
...
((System.ComponentModel.ISupportInitialize)(this.myControl1)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
...
(I expect it's obvious now, that) All above options will run after running the constructor.
Putting that code in the setter worked
public partial class ControlIntEntry : TextBox
{
private bool allowNegatives = false;
[Description("Allow negative values"), Category("Behavior")]
public bool AllowNegatives
{
get { return allowNegatives; }
set
{
allowNegatives = value;
if (allowNegatives)
this.KeyPress += KeyPress_AllowNegatives;
else
this.KeyPress += KeyPress_PositiveOnly;
}
}
public ControlIntEntry()
{
InitializeComponent();
}
private void KeyPress_PositiveOnly(object sender, KeyPressEventArgs e)
{
Char newChar = e.KeyChar;
if (!Char.IsDigit(newChar) && newChar != 8)
{
e.Handled = true;
}
}
private void KeyPress_AllowNegatives(object sender, KeyPressEventArgs e)
{
Char newChar = e.KeyChar;
int cursorIndex = this.SelectionStart;
if (cursorIndex == 0)
{
if (!Char.IsDigit(newChar) && newChar != 8 && newChar != 45)
{
e.Handled = true;
}
}
else
{
if (!Char.IsDigit(newChar) && newChar != 8)
{
e.Handled = true;
}
}
}
}
I want to get value of TextBox in Form1, to another class.
I try to make a set and get, but I can't do this, because VS shows me error about ambiguity in code.
public partial class Form1 : Form
{
private TextBox _textBox1;
public Form1()
{
this._textBox1 = textBox1;
InitializeComponent();
}
public string _textBox1
{
get { return _textBox1.Text; }
set { _textBox1.Text = value; }
}
}
How to make this correct? My control is private.
You have one field and one property in you class with the same name, change the name of the property, for instance to
public string FormTextBox1
{
get { return _textBox1.Text; }
set { _textBox1.Text = value; }
}
as naming standard the public properties must be Pascal Case notation
Capitalization Conventions
You can pass textBox1.Text to a variable, and make a getter/setter for it.
Like this:
public class A : Form1
{
// assuming it's a string. If it's not, change the type
// for the getter method below accordingly
private string textBoxValue;
// at some point, you'll have to make this line below:
textBoxValue = textBox1.Value;
public string GetTextBoxValue()
{
return textBoxValue;
}
}
public class B
{
A aReference = new A();
// you can get the value you want by doing
// aReference.GetTextBoxValue();
}
public void yourFormLoadMethod()
{
//this instantiates a new object of your class
nameOfYourClass newYourObject = new nameOfYourClass(//put any params you need here);
txtNameOfYourTextBox.DataBindings.Add("Enabled", newLTDObjectBenInt, "YourTextBoxEnabled", true, DataSourceUpdateMode.OnPropertyChanged);
txtNameOfYourTextBox.DataBindings.Add("Value", newLTDObjectBenInt, "YourTextBoxEntered", true, DataSourceUpdateMode.OnPropertyChanged);
txtNameOfYourTextBox.DataBindings.Add("Visible", newLTDObjectBenInt, "YourTextBoxVisible", true, DataSourceUpdateMode.OnPropertyChanged);
}
public class nameOfYourClass
{
//constructor
public nameOfYourClass(//same params here from the Load method)
{
//place any logic that you need here to load your class properly
//this sets default values for Enable, Visible and the text
//you use these fields to manipulate your field as you wish
yourTextBoxVisible = true;
yourTextBoxEnabled = true;
yourTextBoxEntered = "this is the default text in my textbox";
}
private bool yourTextBoxEnabled;
public bool YourTextBoxEnabled
{
get
{
return yourTextBoxEnabled;
}
set
{
yourTextBoxEnabled = value;
}
}
private bool yourTextBoxVisible;
public bool YourTextBoxVisible
{
get
{
return yourTextBoxVisible;
}
set
{
yourTextBoxVisible = value;
}
}
private string yourTextBoxEntered;
public string YourTextBoxEntered
{
get
{
return yourTextBoxEntered;
}
set
{
yourTextBoxEntered = value;
}
}
}
I want to set a property which contains other properties inside (Like font or Location Properties), I've done the following:
class Example:DataGridView
{
private PlusProperties X;
public Example()
{
X = new PlusProperties();
AdditionalProperties = X;
}
[ComVisibleAttribute(true)]
[System.ComponentModel.Browsable(true), System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Always)]
[System.ComponentModel.DesignerSerializationVisibility(System.ComponentModel.DesignerSerializationVisibility.Content)]
public PlusProperties AdditionalProperties
{
get { return X; }
set
{
X = value;
}
}
}
public class PlusProperties
{
private Color Pcolor = Color.DimGray;
private Color Ccolor = Color.DimGray;
public PlusProperties()
{
}
[System.ComponentModel.Browsable(true), System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Always)]
public Color ParentColor
{
get { return Pcolor; }
set
{
if (value != Pcolor)
{
Pcolor = value;
}
}
}
[System.ComponentModel.Browsable(true), System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Always)]
public Color ChildColor
{
get { return Ccolor; }
set
{
if (value != Ccolor)
{
Ccolor = value;
}
}
}
}
How can achieve the same behavior on design time (I mean show the properties of the class)?
Thanks in advance for your help.
You'll want to create a class that inherits ExpandableObjectConverter and add a TypeConverter attribute to your class that uses it. See the following example here: https://msdn.microsoft.com/en-us/library/system.componentmodel.expandableobjectconverter(v=vs.110).aspx
I have created a UserControl which extends PictureBox Control
public partial class AudioMonitor : PictureBox
{
private SelectionSettings _selectionSettings;
[Description("Various settings regarding to the selection visuals"), Category("Custom")]
[Browsable(true), EditorBrowsable(EditorBrowsableState.Always)]
public SelectionSettings SelectionSettings
{
get { return this._selectionSettings; }
set { this._selectionSettings = value; }
}
}
SelectionSettings property is a custom class which I have created as follows:
[Serializable]
public class SelectionSettings
{
private SelectionMarker _startMarker;
private SelectionMarker _endMarker;
private SelectionPen _selectionStyle;
public SelectionMarker StartMarker
{
get { return this._startMarker; }
set { this._startMarker = value; }
}
public SelectionMarker EndMarker
{
get { return this._endMarker; }
set { this._endMarker = value; }
}
public SelectionPen SelectionStyle
{
get { return this._selectionStyle; }
set { this._selectionStyle = value; }
}
}
[Serializable]
public class SelectionMarker
{
private Color _color = Color.White;
private DashStyle _style = DashStyle.Solid;
private float _width = 1.0F;
public Color Color
{
get { return this._color; }
set { this._color = value; }
}
public DashStyle Style
{
get { return this._style; }
set { this._style = value; }
}
public float Width
{
get { return this._width; }
set { this._width = value; }
}
public Pen Pen
{
get
{
Pen pen = new Pen(this._color);
pen.DashStyle = this._style;
pen.Width = this._width;
return pen;
}
}
}
[Serializable]
public class SelectionPen
{
private Color _color = Color.White;
private DashStyle _style = DashStyle.Solid;
private float _width = 1.0F;
private float _alpha = 100;
public Color Color
{
get { return this._color; }
set { this._color = value; }
}
public DashStyle Style
{
get { return this._style; }
}
public float Width
{
get { return this._width; }
}
public float Alpha
{
get { return this._alpha; }
}
public int AlphaPercent
{
get { return (int)Math.Round(this._alpha * 100 / 255); }
set
{
if (value > 0 && value <= 100)
this._alpha = (value * 255 / 100);
else
throw new ArgumentException("Alpha percentage should be between (0, 100]");
}
}
public Pen Pen
{
get
{
Pen pen = new Pen(Color.FromArgb((byte)this._alpha, this._color.R, this._color.G, this._color.B));
pen.DashStyle = this._style;
pen.Width = this._width;
return pen;
}
}
}
When I place my custom control on a Form and open the Properties Window I can see it as follows :
As you can see I can not set "SelectionSettings" property from the "Properties" window at the design time. What I need is to place the "..." button next to the Property name and open a pop-up to set values.
It should look something like this :
How can I accomplish this task?
What you want to do is add an Editor to AudioMonitor's SelectionSettings property.
To do this, you should create a custom class derived from the UITypeEditor Class.
In order to inherit from UITypeEditor, your project must reference System.Design, which can be done by going to the Project menu, selecting Add Reference to bring up the Reference Manager, navigating to Assemblies->Framework on the left panel and making sure System.Design is checked in the list.
In your custom UITypeEditor derived class, override the method EditValue to bring up a custom dialog that edits a value of type SelectionSettings. Then set the Editor Attribute on your SelectionSettings property to your custom UITypeEditor derived class.
Here's a generic code example of how this would look:
using System;
using System.Drawing.Design;
using System.ComponentModel;
using System.Windows.Forms;
// ...
[Editor(typeof(SomeProperty_Editor), typeof(UITypeEditor))] // You might be able to place this attribute on class SomeType, but I haven't tried yet
public SomeType SomeProperty
{
get { /* stuff */ }
set { /* stuff */ } // optional, really
}
class SomeProperty_Editor : UITypeEditor
{
public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
IWindowsFormsEditorService service = (IWindowsFormsEditorService)(provider.GetService(typeof(IWindowsFormsEditorService)));
SomeProperty_EditorWindow EditorWindow = new SomeProperty_EditorWindow(value as SomeType);
service.ShowDialog(EditorWindow);
if (EditorWindow.EditCancelled)
return value;
else
return EditorWindow.GetEdittedValue();
}
}
class SomeProperty_EditorWindow : Form
{
public SomeProperty_EditorWindow(SomeType CurrentProperty) : base()
{
InitializeComponents();
// Grab info in CurrentProperty here and display it on form
}
public void InitializeComponents()
{
// write yourself or use designer
}
public SomeType GetEdittedValue()
{
// return editted value from form components
}
public bool EditCancelled = false; // Set true if cancel button hit
}
You may want to look up the PropertyGrid Control as it could be very useful in your EditorWindow. Also, you can go to the UITypeEditor Class's MSDN page, look at all of the .NET derived classes under "Inheritance Hierarchy" to see if there is a built in editor that is close in functionality to what you want to do and inherit directly from that class.
I have a class UserPane l: Panel. I do so.
private bool AutoSize_ = true;
public bool AutoSize
{
get
{
return AutoSize_;
}
set
{
AutoSize_ = value;
}
}
But when I change Autosize_ still returns are always true. How to make that value correctly transmitted.
The Panel class already have a property AutoSize.
You define a new Property with the same name. Check your warnings, you must have the following :
warning CS0114: 'UserPanel.AutoSize' hides inherited member 'System.Windows.Forms.Panel.AutoSize'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
When you are calling the AutoSize property of UserPanel, you are in fact calling the property of the superclass Panel. So your field AutoSize_ will never get changed.
Act accordingly to the warning :
public class UserPanel : Panel
{
private bool AutoSize_ = true;
public override bool AutoSize
{
get
{
return AutoSize_;
}
set
{
AutoSize_ = value;
}
}
}
On a side note, why do you want to change the already working AutoSize functionnality? Are you sure this is what you need?
You need to override the property, because it is already defined in Panel.
public override bool AutoSize {
get {
return base.AutoSize;
}
set {
base.AutoSize = value;
}
}
Isn't the compiler giving you the warning over this?
If you are doing something like this.
public partial class myPanel : Panel
{
private bool AutoSize_ = true;
public bool AutoSize
{
get
{
return AutoSize_;
}
set
{
AutoSize_ = value;
}
}
}
You are hiding Panels AutoSize property. You should override this property. You can override it to return the base value (which actually does not do much).
public override bool AutoSize
{
get
{
return base.AutoSize;
}
set
{
base.AutoSize = value;
}
}
Or maybe you can do some custom work there.
public override bool AutoSize
{
get
{
//return custom value
}
set
{
//set some custom value
}
}
If you have that class
class P : Panel
{
bool AutoSize_ = true;
public bool AutoSize
{
get
{
return AutoSize_;
}
set
{
AutoSize_ = value;
}
}
}
And then you create an instance and change the value of AutoSize like this:
var p = new P();
p.AutoSize = true;
This should work fine (at least in my tests), the problem arise when you do something like this:
var p2 = (Panel)p;
p2.AutoSize = false; // this change the AutoSize property of the Panel not the property of the derived class P
If this isn't your case then no idea what happens.