Problem
I was trying to create a custom control which contains nothing but a label. However, I wanted the label's text to be changed to what the name property of the custom control has received at design time.
This is what my custom control's class looks like:
public partial class Tile : UserControl
{
public Tile()
{
InitializeComponent();
}
[Browsable(true)]
public override string Text { get => label1.Text; set => label1.Text = value; }
}
As you can see, I've overridden the UserControl's Text property in a way that it updates the Label's text too as soon as the Name is updated which in the end did not work. What happened was when I dragged the control from Toolbox to form the label got updated as expected but the moment I build the project, the designer got refreshed and the Label's text was lost.
What I tried
DesignerSerializationAttribute
Going through Google I came upon a solution given at StackOverflow
itself by Hans Passant that using
DesignerSerializationAttribute(DesignerSerializationVisibility.Visible)
can solve the problem as the value the Text property is given will
be persisted in the initialization code which seems valid when it was
getting lost at first (value didn't persisted and lost upon designer
repaint).
So I changed my property like this:
[Browsable(true), DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public override string Text { get => label1.Text; set => label1.Text = value; }
Doing such change actually solved the issue and now it was working even If I build the project.
What I found
Though my issue got solved but then I started to look for another way
to approach the same result.
While experimenting more with my code I found that If I create
another property that exposes the label's Text property and update
it using the overridden Text property it works exactly what it
was working using DesignerSerializationAttribute.
Here what the new code looks like:
[Browsable(false)]
public string LabelText { get => label1.Text; set => label1.Text = value; }
[Browsable(true)]
public override string Text { get => LabelText; set => LabelText = value; }
I wanted to know that
Why this works (even without DesignerSerializationVisibility):
Text---->LabelText---->Label's Text
I might be asking something very obvious right now but I've been reading about it since hours which made it a bit confusing for me .
If you look at the source code of UserControl, you can see the Text property is marked as not serializable by designer, so basically the value of the property will be lost after you close the form:
[Browsable(false),
EditorBrowsable(EditorBrowsableState.Never),
Bindable(false),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public override string Text {
get {
return base.Text;
}
set {
base.Text = value;
}
}
But in your alternative solution, designer will serialize LabelText property, and later will use its value to return as Text. That's why it works.
Related
I probably have a very simple problem, but could not find a solution. I have a problem with the property binding to a ToolStripLabel. A Label is bound to COM port value in App.Config.
If I bind the property for a System.Windows.Forms.Label label, the update of Text-Property by changing the COM Port works fine as it is supposed to. But when the label is IN ToolStrip (System.Windows.Forms.ToolStripLabel), the label is not updated by changing the value for COM Port at runtime.
It will be changed only by new start of Application.
In the picture there is a current settings of PropertyBinding to ApplicationSettings.
I've already tried:
Application.DoEvents()
toolStrip.Update()
toolStrip.Refresh()
toolStrip.Invalidate()
Nothing makes difference.
Does anyone have an idea what the problem could be?
Greetings,
Sasha
ApplicationSettings
Label Properties Settings
Example
The ToolStripLabel Component doesn't implement DataBindings, as the Label Control does (that's why you can see a Label Control update its Text when the current setting is changed). When you add PropertyBindings to the Text property through the Designer, the Text is just set to the Properties.Default setting selected (you can see that in the Designer.cs file).
You can build your own ToolStripLabel that implements IBindableComponent, decorate it with ToolStripItemDesignerAvailability flags that allow the ToolStrip or StatusStrip to acknowledge the existence of this custom Component, so you can add it directly from the selection tool.
Add a PropertyBinding to the Text property and now, when the Setting changes, the Text is updated. You can see in the Designer.cs file that a DataBinding has been added.
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design;
[ToolStripItemDesignerAvailability(
ToolStripItemDesignerAvailability.ToolStrip |
ToolStripItemDesignerAvailability.StatusStrip),
ToolboxItem(false)
]
public class ToolStripDataLabel : ToolStripLabel, IBindableComponent
{
private BindingContext m_Context;
private ControlBindingsCollection m_Bindings;
public ToolStripDataLabel() { }
public ToolStripDataLabel(string text) : base(text) { }
public ToolStripDataLabel(Image image) : base(image) { }
public ToolStripDataLabel(string text, Image image) : base(text, image) { }
// Add other constructors, if needed
[Browsable(false)]
public BindingContext BindingContext {
get {
if (m_Context == null) m_Context = new BindingContext();
return m_Context;
}
set => m_Context = value;
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public ControlBindingsCollection DataBindings {
get {
if (m_Bindings == null) m_Bindings = new ControlBindingsCollection(this);
return m_Bindings;
}
}
}
I made Custom Control and I added a new property that is meant to point to a Resource.
How can I get the value, which is set in the Form Designer, of this property from another property of the Custom Control?
When I try to read it, the value it return is null.
My code related to the of Custom Control and the mentioned property:
class ResourceLabel : Label
{
private string resourceKey;
[Category("Appearance")]
[Browsable(true)]
[Description("Sets the resource key for localization")]
public string ResourceKey
{
get { return resourceKey; }
set {
if (resourceKey != value) {
resourceKey = value;
Invalidate();
}
}
}
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
[Bindable(true)]
public override string Text
{
if (base.Text != value) {
Console.WriteLine($"Test: {ResourceKey}");
if (ResourceKey != null) {
var locale = CultureInfo.GetCultureInfo(Properties.Settings.Default.Language);
var textFromResource = Resources.ResourceManager.GetString(ResourceKey, locale);
base.Text = textFromResource;
}
else {
base.Text = value;
}
}
}
}
This string Console.WriteLine($"Test: {ResourceKey}"); returns null.
Given the description of the problem and the Description attribute applied to the ResourceKey property of your Custom Control, it appears that you're localizing your application, setting - as a consequence - the Localizable property of the parent Form to true.
This changes some details in the Form's initialization.
If you look inside the Form.Designer.cs file, you'll notice that some new parts have been added:
The usual:
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(YourForm));
has now new companions. In the initialization section of each control, resources.ApplyResources(this.controlName, "controlName"); has been added.
Your custom Label should show, e.g.:
resources.ApplyResources(this.resourceLabel1, "resourceLabel1");
Many of the standard properties derived from Control are localizable (are decorated with a [Localizable(true)] attribute). The Text property is of course among these. The value of these properties is now stored in the resource files used for the different localizations. Hence, these properties are not set in the Designer.cs file anymore and also are initialized before other properties.
Your ResourceKey property is not defined as Localizable, so it's added in the Designer.cs file and initialized after the localizable properties.
▶ A simple fix is to define the ResourceKey property as Localizable, so it will be initialized before the non-localized properties, along with the Text property.
[Localizable(true), Browsable(true)]
[Category("Appearance"), Description("Sets the resource key for localization")]
public string ResourceKey {
get { return resourceKey; }
set {
if (resourceKey != value) {
resourceKey = value;
Invalidate();
}
}
}
Note that this is somewhat a breaking change, you should:
1 - Add the Localizable attribute to the ResourceKey Property
2 - Remove the old ResourceLabel Control from the Form Designer
3 - Compile the Solution
4 - Add the custom ResourceLabel back where it was
5 - Set the required Control's properties in the Properties Panel
6 - Run the application or compile the Project
Check the Designer.cs file of your Form, see that the ResourceKey property has disappeared, its value is now set through resources.ApplyResource().
▶ If you don't want to make that property localizable, you'll have to read its value later, when the Control's initialization is complete; overriding OnHandleCreated, for example. Note that the Handle of a Control can be re-created at run-time in a few occasions (setting key properties that require the Control to recreate the Handle).
Note:
You should not rely on the initialization sequence of your properties, there's no real guarantee that a Property is initialized before another. Take this into consideration while designing a Control's behavior.
I have created my custom UserControl with some custom properties for it. For example:
[Description("Example Description"),Category("CustomSettings"),DefaultValue("Transmedicom")]
public string DatabaseAddress
{
get; set;
}
Everything works fine. I can change custom property in code and in design-time.
What I'm looking for (and cannot find anything) now is: How could I repaint (reacreate) my UserControl in design-time when my custom property change in design-time.
Let's say when DatabaseName will be changed to localhost UserControl will add and display some Label on my UserControl. It's important to work in Design-Time.
Nothing special there. You just have to set the text to Label inside the property setter. That should update the UI.
private string databaseAddress;
[Description("Example Description"), Category("CustomSettings"), DefaultValue("Transmedicom")]
public string DatabaseAddress
{
get { return databaseAddress; }
set
{
databaseAddress = value;
yourLabel.Text = value;//Set value to Label or whatever
}
}
Try this
private string _databaseAddress = "localHost";
[Description("Example Description"), Category("CustomSettings"), DefaultValue("Transmedicom")]
public string DatabaseAddress
{
get
{
return _databaseAddress;
}
set
{
if(!string.IsNullOrEmpty(value))
{
_databaseAddress = value;
lblAddress.Text = value;
lblAddress.Invalidate();
}
}
}
Both previous answers are correct.
I just want to add that the user control can even react on resizing during design time, using the Layout event.
I have found this article from http://www.codeproject.com/Articles/687430/Selecting-Forms-Controls-at-Design-Time
This is written in VB.NET, so I converted into C# version.
And I created a class called "DataGridViewBandColumn" inherits from DataGridViewColumn like below.
public class DataGridViewBandColumn : DataGridViewColumn
{
private Collection<DataGridViewTextBoxColumn> _TargetControls = new Collection<DataGridViewTextBoxColumn>();
[EditorAttribute(typeof(BANANA.Windows.Controls.Design.UITypeEditorDropDownCollection), typeof(System.Drawing.Design.UITypeEditor))]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Collection<DataGridViewTextBoxColumn> ATargetControls
{
get { return _TargetControls; }
set { _TargetControls = value; }
}
}
I can see the property and select other columns from form design time like below.
But as soon as I click OK button on property window, it loses the items that I selected and it is not saved on the Form1.Designers.cs file.
Where do I have to check to fix this?
Anyone has any idea?
I've made a C# usercontrol with one textbox and one richtextbox.
How can I access the properties of the richtextbox from outside the usercontrol.
For example.. if i put it in a form, how can i use the Text propertie of the richtextbox???
thanks
Cleanest way is to expose the desired properties as properties of your usercontrol, e.g:
class MyUserControl
{
// expose the Text of the richtext control (read-only)
public string TextOfRichTextBox
{
get { return richTextBox.Text; }
}
// expose the Checked Property of a checkbox (read/write)
public bool CheckBoxProperty
{
get { return checkBox.Checked; }
set { checkBox.Checked = value; }
}
//...
}
In this way you can control which properties you want to expose and whether they should be read/write or read-only. (of course you should use better names for the properties, depending on their meaning).
Another advantage of this approach is that it hides the internal implementation of your user control. Should you ever want to exchange your richtext control with a different one, you won't break the callers/users of your control.
Change the access modifier ("Modifiers") of the RichTextBox in the property grid to Public.
Add a property to the usercontrol like this
public string TextBoxText
{
get
{
return textBox1.Text;
}
set
{
textBox1.Text = value;
}
}
I recently had some issues doing this with a custom class:
A user control had a public property which was of a custom class type. The designer by default tries to assign some value to it, so in the designer code, the line userControlThing.CustomClassProperty = null was being automatically added.
The intent was to be able to provide the user control with a custom class at any point while running the program (to change values visible to the user). Because the set {} portion did not check for null values, various errors were cropping up.
The solution was to change the property to a private one, and use two public methods to set and get the value. The designer will try to auto-assign properties, but leaves methods alone.
You need to make a public property for the richtextbox, or expose some other property that does the job of setting the richtextbox text like:
private RichTextBox rtb;
public string RichTextBoxText
{
get
{
return rtb.Text;
}
set
{
rtb.Text = value;
}
}