Today I had the need to address a C# Component I wrote and that is part of a larger C# UserControl by name (so by a String). I know there are other ways to address components but for some reason I will not explain in this blog post I needed this feature.
The main problem is that Components do not have a Name property although in the Visual Studio Property Inspector components do show a ‘(Name)’ property containing the value I was after.
I started searching the internet but without luck. Plenty of solutions for Controls but none for Components except for the obvious ‘why do need this anyway’ type of non-solutions and a fair number of unanswered questions.
Some months ago I already solved a minor piece of the puzzle by finding a way to get the Component’s Name at Design Time.
A simple ToString() and some string cutting was enough to get the value of the myserious ‘(Name)’ property. This with one limitation, the Component should not have it’s own ToString() method overriding the default one. The following code does the trick:
1: /// <summary>
2: /// GetName() only works at Design Time.
3: /// </summary>
4: /// <returns>The Name of the Component as shown in the Designer</returns>
5: public string GetName()
7: int split = ToString().IndexOf(' ');
8: return ToString().Substring(0, split);
However nice this works on Design-Time, on Run-Time this code fails completely as the ToString() is empty or at best contains the class name of the Component.
So how do we get this Design-Time value available at Run-Time? The answer was a specially defined property that behaves differently at Design and Run-Time.
3: public String ColumnName
7: //If at designTime we store GetName into fColumnName.
8: if (ToString().IndexOf(' ') != -1)
10: fColumnName = GetName();
12: return fColumnName;
16: //If at designTime we store fColumnName and ignore value.
17: if (ToString().IndexOf(' ') != -1)
19: fColumnName = GetName();
23: fColumnName = value;
27: private String fColumnName;
Above code will use part of the GetName() method’s code to distinguish between Design-Time (ToString()’s value is a combination of Design-Time name and ClassName separated by a space) and Run-Time where ToString() is just the ClassName.
The getter and setters work differently at Design-Time and Run-Time.
Both getter and setter set the private storage field ‘fColumnName’ at Design-Time with the value returned by GetName() and the getter off-course returns this value. So on Design-Time the setter totally ignores the ‘value’ parameter passed.
On Run-Time the getter does return the private storage field and the setter does set it with value as a normal property would do.
Finally, the attributes of the property make sure it is not visible in the property inspector but will be saved into the *.designer.cs file properly. I experimented with the ReadOnly attribute too but that only leads to the form designer not saving the property into the *.designer.cs file. If you remove the ‘Browsable’ attribute or set it to true you can see the ColumnName property at Design-Time.
Basically what happens is that the Design-Time value end up in the private storage field of a property that is saved into the *.designer.cs file. At Run-Time GetName() is totally ignored and the value retrieved from the *.designer.cs file is used as if it where a normal property.
The result is a property that changes nicely when a Component is renamed and cannot be edited at Design-Time but is serialized into the *.designer.cs file as it should.
Note: the code presented in this blog post only works on Components you write yourself and frees you from maintaining a separate Name property that has no actual link to the Components Design-Time name nor will be adjusted when renaming the Component.
Maybe someone can bolt it on with an extension method.