I'm currently making a custom control, and some of the properties are generating these weird blank comments when VS writes the Designer.cs file. Example:
//
// myControl
//
this.myControl.Name = "myControl";
this.myControl.Property = 30;
this.myControl.OtherProperty = 20;
//
//
//
this.myControl.Options1.Name = null;
this.myControl.Options1.Option = "example";
//
//
//
this.myControl.Options2.Name = null;
this.myControl.Options2.SomeProperty = 50;
this.myControl.Options2.SomeEvent += new System.EventHandler(this.myControl_Options2_SomeEvent);
this.myControl.OtherProperty = 10;
Does anybody know what's causing these blank comments? I'd prefer no comments at all, but if I can at least have the name "myControl.Options1" shown that would be acceptable.
Here is the rough structure of my classes (although very simplified):
[ToolboxItem(false)]
public class Options : IComponent
// I implement IComponent so this class appears in the Properties window nicely. Not sure why exactly it works though.
{
#region Implement IComponent
public ISite Site { get; set; }
public void Dispose()
{
// Nothing needs to be disposed
Disposed?.Invoke(this, EventArgs.Empty);
}
public event EventHandler Disposed;
#endregion
}
public partial class MyControl : UserControl
{
#region Options
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Options Options1 { get; private set; }
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Options Options2 { get; private set; }
public MyControl
{
Options1 = new Options();
Options2 = new Options();
}
#endregion
}
Any ideas would be very helpful! I haven't been able to find this problem anywhere else online. My assumption is that I'm misusing the IComponent interface, but I'm not sure how to fix it.
Those comments are automatically generated and have no impact on the output binary. When you compile, they'll all be dropped, so you needn't worry about bloat or anything like that. For maintainability, other developers should be directed to the designer, not the outputted code. Their edits won't be preserved if someone makes a change in the designer.
I suggest ignoring what's actually emitted by the designer entirely. It's not really meant to be edited- useful to view what source actually gets created from the designer, though.
That is the default behavior of the ComponentCodeDomSerializer when serializing the component name.
You can derive from the ComponentCodeDomSerializer and override Serialize, call the base class, then remove the CodeCommentStatement objects from the returned CodeStatementCollection.
What you have linked, looks a bit weird. Well the commenting system works as follow:
Your auto generated designer 'Methods' will be signed with an xml comment which describes their behavior.
It also writes comments to define Which control properties are being set on the current block.
in your case:
//
// myControl
//
this.myControl.Name = "myControl";
this.myControl.Property = 30;
this.myControl.OtherProperty = 20;
the 'myControl' means there is a control named 'myControl' that the following lines are setting it's properties.
As u said u r gonna ship this control. I think event these short nonsense comments are useful.
Edit: Nevermind, this doesn't work. It seemed like it was working until I used the control elsewhere and the problem came back up.
I found that adding the following attribute to my options class worked for me:
[ToolboxItem(false)]
[DesignerSerializer(typeof(CodeDomSerializer), typeof(CodeDomSerializerBase))]
public class Options : IComponent
{
...
}
Related
In a xamarin app on a xaml page I am loading localized strings using a xaml extension (the details are described here). For example:
<Label Text={i18n:Translate Label_Text}/>
Now, I want the user to be able to change the language of the app at runtime (using a picker). If that happens, I want to change the language immediately.
Can I somehow reload all translated texts?
I could delete all pages and recreate them, but I am trying to avoid that.
I could also bind all localised texts to strings in the pages model. But that is a lot of unnecessary code for truly static strings.
Unfortunately you cannot force controls set up with markup extensions in XAML to reevaluate their properties using those extensions - the evaluation is only done once upon parsing XAML file. What basically happens behind the scenes is this:
Your extension is instantiated
ProvideValue method is called on the created instance and the returned value is used on the target control
The reference to the created instance is not stored (or is a weak reference, I'm not sure), so your extension is ready for GC
You can confirm that your extension is only used once by defining a finalizer (desctructor) and setting a breakpoint in it. It will be hit soon after your page is loaded (at least it was in my case - you may need to call GC.Collect() explicitly). So I think the problem is clear - you cannot call ProvideValue on your extension again at an arbitrary time, because it possibly no longer exists.
However, there is a solution to your problem, which doesn't even need making any changes to your XAML files - you only need to modify the TranslateExtension class. The idea is that under the hood it will setup proper binding rather than simply return a value.
First off we need a class that will serve as a source for all the bindings (we'll use singleton design pattern):
public class Translator : INotifyPropertyChanged
{
public string this[string text]
{
get
{
//return translation of "text" for current language settings
}
}
public static Translator Instance { get; } = new Translator();
public event PropertyChangedEventHandler PropertyChanged;
public void Invalidate()
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(Binding.IndexerName));
}
}
The goal here is that Translator.Instance["Label_Text"] should return the translation that your current extension returns for "Label_Text". Then the extension should setup the binding in the ProvideValue method:
public class TranslateExtension : MarkupExtension
{
public TranslateExtension(string text)
{
Text = text;
}
public string Text { get; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
var binding = new Binding
{
Mode = BindingMode.OneWay,
Path = new PropertyPath($"[{Text}]"),
Source = Translator.Instance,
};
return binding.ProvideValue(serviceProvider);
}
}
Now all you need to do is to call Translator.Instance.Invalidate() every time the language is changed.
Note that using {i18n:Translate Label_Text} will be equivalent to using {Binding [Label_Text], Source={x:Static i18n:Translator.Instance}}, but is more concise and saves you the effort of revising your XAML files.
I'd tried to implement #Grx70's great proposed solution, but some of the classes and properties the example used are internal to Xamarin so couldn't be used in that way.
Picking up on their last comment though, was the clue to get it working, though not quite as elegantly as initially proposed, we can do this:
public class TranslateExtension : IMarkupExtension<BindingBase>
{
public TranslateExtension(string text)
{
Text = text;
}
public string Text { get; set; }
object IMarkupExtension.ProvideValue(IServiceProvider serviceProvider)
{
return ProvideValue(serviceProvider);
}
public BindingBase ProvideValue(IServiceProvider serviceProvider)
{
var binding = new Binding
{
Mode = BindingMode.OneWay,
Path = $"[{Text}]",
Source = Translator.Instance,
};
return binding;
}
}
and this the Translator class as initially proposed, but reproduced here for clarity with the GetString call:
public class Translator : INotifyPropertyChanged
{
public string this[string text]
{
get
{
return Strings.ResourceManager.GetString(text, Strings.Culture);
}
}
public static Translator Instance { get; } = new Translator();
public event PropertyChangedEventHandler PropertyChanged;
public void Invalidate()
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(null));
}
}
Then as the original post suggested, instead of binding text with:
{i18n:Translate Label_Text}
Bind
{Binding [Label_Text], Source={x:Static i18n:Translator.Instance}}
I'd hit this right at the end of a project (adding the multiple languages), but using Visual Studio Community and Search/Replace with RegEx, the binding can be replaced across the project, replacing:
\{resources:Translate (.*?)\}
with:
{Binding [$1], Source={x:Static core:Translator.Instance}}
NOTE: The Regex assumes the 'resources' namespace for the original Translate macro, and 'core' namespace for the Translator class, you may have to update as appropriate.
I appreciate this is a small tweak to #Grx70's otherwise great solution (I'm standing on the shoulders of giants with this one), but I'm posting this here for any that follow with the same problem of getting this working.
I am creating a user interface for an iOS app and I am looking for the correct way to create a reusable custom control. I got it generally working when running the app, but at design time setting my "exported" properties has no visible effect in the designer. I think I am doing something fundamentally wrong, so perhaps someone could give me guidance
What I am doing:
I have created a subclass of UIControl.
In the constructor I call an Initialize method.
In the Initialize method, I add several subviews and constraints to layout them within my control
Here is some hollowed out code that shows the above:
[Register("RangedValueSelector"), DesignTimeVisible(true)]
public sealed class RangedValueSelector : UIControl
{
public RangedValueSelector(IntPtr p)
: base(p)
{
Initialize();
}
public RangedValueSelector()
{
Initialize();
}
public int HorizontalButtonSpacing
{
get { return _horizontalButtonSpacing; }
set
{
_horizontalButtonSpacing = value;
}
}
[Export("LabelBoxVerticalInset"), Browsable(true)]
public int LabelBoxVerticalInset
{
get
{
return _labelBoxVerticalInset;
}
set
{
_labelBoxVerticalInset = value;
}
}
private void Initialize()
{
//Code that creates and add Subviews
//Code that creates and add the required constraints, some of which should depend on the design time properties
}
}
So the control works perfectly fine if I set the exported properties via the designer, however they do not have an immediate effect in the designer itself.
What is the suggested way of having design-time settable properties that change the constraint values? I would like to avoid having to recreate all the subviews each time someone in the code or in the designer sets a property.
You are missing constructor with RectangleF which is used by designer.
public RangedValueSelector(RectangleF bounds):base(bounds){}
The rest seems to be correct.
Here is my first experience creating a custom control. My real example is much larger, but this is boiled down for clarity. Ultimately, I need to hide as many properties of the custom control as possible so that when I share my new control with the rest of my team, they need only worry about the few properties that are required.
I have a control called TimeNow which inherits System.Web.UI.WebControls.Literal and basically just prints the current time on the web page:
public class TimeNow : Literal
// Set to private so Text is hidden from the editor.
private string Text
{
get;
set;
}
protected override void Render(HtmlTextWriter writer)
{
// Get and write the time of now.
}
This works, but it seems clunky. I no longer see Text available in intellisense when I drop the control on a web page, but I do receive a warning that my Text is hiding the inherited Text. Is there a better way to hide the Text property?
There should be more to that warning message, suggesting you use the new keyword if you really intend to hide the inherited member, do what it says:
public class TimeNow : Literal
{
new private string Text
{
get;
set;
}
}
Try this:
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
private string Text
{
}
I think you're doing something wrong if you derive from a Literal that behaves like an ITextControl (Literal has implemented this Interface) and then you try to remove the essential Text property? This is like deriving from cat but don't want to let them do "meow" and force them flying like a duck. You are searching for the Animal class instead, I think.
I don't know much about ASP.NET (only .Net for the Desktop). Maybe it is possible to use composition instead of inheritance (if you not really need the Literal -> "Cat") and you can inherit from System.Web.UI.Control (-> "Animal") instead.
public class TimeNow : System.Web.UI.Control
{
// no need to do something here with the Text property, is not defined
}
or with composition
public class TimeNow : System.Web.UI.Control
{
private readonly Literal literal;
public TimeNow()
{
this.literal = new Literal();
// ... and set the Text etc., no one else can access
}
// or something like this
public TimeNow(ILiteralFactory literalFactory)
{
// the factory is just an example... don't know your context but this way your newly created literal can't be accessed
this.literal = literalFactory.CreateNewLiteral();
// do what you want with the text control, e.g. store internally
// Clone() the Literal etc.
// the
}
}
Update: Had a very quick look into the MSDN and maybe a Content Control is the thing you are looking for, instead of the Literal. (sorry if wrong, writing Desktop Apps)
If you have a Form that displays data, one thing you can do is reference this.DesignMode in the constructor to avoid populating it in the designer:
public partial class SetupForm : Form
{
private SetupItemContainer container = new SetupItemContainer();
public SetupForm()
{
InitializeComponent();
if (!this.DesignMode)
{
this.bindingSource1.DataSource = this.container;
this.Fill();
}
}
}
However, if you decide to re-write that form as a UserControl, keeping the same constructor logic, something unexpected happens - this.DesignMode is always false no matter what. This leads to the designer invoking your logic that's meant to happen at runtime.
I just found a comment on a blog post that seem to give a fix to this but it references functionality of the LicenseManager class as a replacement that works as expected in a UserControl.
So for a UserControl I can do:
public partial class AffiliateSetup : UserControl
{
private AffiliateItemContainer container = new AffiliateItemContainer();
public AffiliateSetup()
{
InitializeComponent();
if (LicenseManager.UsageMode == LicenseUsageMode.Runtime)
{
this.bindingSource1.DataSource = this.container;
this.Fill();
}
}
}
Does using the LicenseManager instead of DesignMode have any caveats or implications that might dissuade me from putting in my production code?
According to someone who posted a comment on my answer to another question, using LicenseManager doesn't work in an OnPaint method.
Today at work, I stumbled upon a problem that was driving me nuts.
Basically my goal is this:
I have a UserControl1, with a field of the type Collection<Class1> and a corresponding property Collection<Class1> Prop. Like this:
public class UserControl1 : UserControl
{
private Collection<Class1> field = null;
// later changed to:
//private Collection<Class1> field = new Collection<Class1>();
[Category("Data")]
[DefaultValue(null)]
[Description("asdf")]
public Collection<Class1> prop
{
get { return field; }
set { field = value; }
}
}
// later added:
//[Serializable]
public class Class1
{
private bool booltest; public bool Booltest { get...set...}
private int inttest; public int Inttest { get...set...}
}
If you already know what I screwed up: no need to read the rest. I am going to describe what exactly I did.
Now I put the UserControl onto a random Form and change the Prop property. A generic "Collection Editor" appears, like the one used for the columns and groups in a listview control. I can enter data as expected. However, when I click OK, the data is gone.
It took me over hour to figure out that I actually have to instantiate my field: private Collection<MyClass> field = new Collection<MyClass>();. Very good, only that the designer entered superspazzing mode. Cascading nightmare error message that can be reduced to: "You must put [Serializable] before your Class1." After doing that I could actually put my UserControl1 on the Form again.
But that only works once. When opening the designer of the Form where I use the UserControl1 after editing something, it gives me an error:
Object of type 'userctltest.Class1[]' cannot be converted to type 'userctltest.Class1[]'.
Well. The Error List says:
Warning: ResX file Object of type 'userctltest.Class1[]' cannot be converted to type 'userctltest.Class1[]'. Line 134, position 5. cannot be parsed.
The designer tries to fetch the Property's data from the resx file. Removing the resx file "solves" that exactly once.
The Form can now be displayed again, with my UserControl1. The Collection property is editable, and it is being saved. It actually works. Once. Whenever I change something and then try to open the Form's designer again, the above error occurs again. I can delete the resx file, but that will of course also delete my data.
Relevant resources that helped me so far (among a ton of not so helpful search results):
http://www.codeproject.com/Answers/190675/Usercontrol-with-custom-class-property#answer1
http://www.codeproject.com/KB/cs/propertyeditor.aspx
http://www.csharpfriends.com/Articles/getArticle.aspx?articleID=94
http://msdn.microsoft.com/en-us/library/system.runtime.serialization.iserializable.aspx
(I also tried implementing ISerializable and overriding GetObjectData with
{ info.AddValue("testbool", testbool); info.AddValue("testint", testint); }
didn't help either (I also tried the property names instead of the field names))
Sorry for writing this like a bad horror novel btw.
What you want is a design time support with CodeDom serialization. You do not need SerializableAttribute or ISerializable, those are for binary serialization.
Since you want to serialize the collection, you must tell the designer to serialize it as such. That is done with the DesignerSerializationVisibiliby attribute - value of Content tells the designer to serialize property contents rather than property itself. Contents of the property should of course be CodeDom serializable, which simple classes with simple properties are by default.
So if you change your UserControl1 class like this:
public class UserControl1 : UserControl
{
private Collection<Class1> field = new Collection<Class1>();
[Category("Data")]
[Description("asdf")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public Collection<Class1> prop
{
get { return field; }
}
}
... it should do the trick. Oh and collection properties are usually not writeable, although that is not mandatory. But serializer expects the collection property to be initialized, that is why you had to add initialization for the field.
Another note, if you do not want that your property is marked with bold in the property editor, you can specify a more complex "default value" through a special method ShouldSerializePropertyName, which can even be private. Like so:
private bool ShouldSerializeprop()
{
return (field.Count > 0);
}
Now your property will only be bold when it is not empty. But I digress, this was not a question :)
The perfect exemple is this:
public partial class SCon : UserControl
{
public SCon()
{
InitializeComponent();
if (Persoanas == null)
{
Persoanas = new List<Persoana>();
}
}
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public List<Persoan> Persoanas { get; set; }
}
[Serializable]
public class Persoan
{
public int Id { get; set; }
public String Name { get; set; }
}
Just change Collection<> to List<>