I'm trying to make an object, configureable/editable with a propertygrid.
This is all going well, except for objects inside objects.
I've got an object/class named "ContactInformation". And inside that object I've got an object named "Correspondence".
This is how that part looks:
[Browsable(false)]
public Correspondence Correspondence
{
get;
set;
}
public int CorrespondenceStatus
{
get { return this.Correspondence.Status; }
set { this.Correspondence.Status = CorrespondenceStatus; }
}
public string CorrespondenceComment
{
get { return this.Correspondence.Comment; }
set { this.Correspondence.Comment = CorrespondenceComment; }
}
public DateTime CorrespondenceDate
{
get { return this.Correspondence.LastSend; }
set { this.Correspondence.LastSend = CorrespondenceDate; }
}
That way I can show the properties/variables of the object inside the object, in the propertygrid.
Anyway, when I edit the values now, and press enter, or click somewhere else, instead of keeping it the value I just typed in, it changes back..
Anyone got an idea why this is happening? Or maybe a better idea to show the properties of objects in objects in the propertygrid?
To edit properties inside an object (this is what you see for example with the winform editor with properties like Font, or Padding, ... where you can "expand" the oject clicking on the 'plus' icon) , you can use the ExpandableObjectConverter class, like this:
[TypeConverter(typeof(ExpandableObjectConverter))]
public class Correspondence
{
...
}
and remove the Browsable(false) of course:
public Correspondence Correspondence
{
get;
set;
}
Related
I will make an industrial HMI application with OPC. I want to display variables of PLC with radio buttons. But I want to choose the plc varaible on radiobutton properties area. There is a class which includes all PLC's variables. I want to choose different variable for each radiobutton from this class. And if variable is true it will be checked.
To do this I want to make custom radio button on c# and add custom propeties to it.
I can make a custom radio button but I could not relate it's property area with another class varibales. When I clicked property area It should display all variables of a class
How can I do that?
public partial class My_RadioButton : RadioButton
{
private VarsFromPLC _FrPLC;
[Description("Displaying PLC Variables"),
Category("Appearance"),
TypeConverter(typeof(VarsFromPLC)),
Browsable(true)]
public VarsFromPLC FrPLC
{
get { return _FrPLC; }
}
public My_RadioButton()
{
_FrPLC = new VarsFromPLC();
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
}
}
public class VarsFromPLC
{
public bool bTry1 { get; }
public bool bTry2 { get; }
public bool bTry3 { get; }
public bool bTry4 { get; }
public bool bTry5 { get; }
public bool bTry6 { get; }
public bool bTry7 { get; }
public bool bTry8 { get; }
public bool bTry9 { get; }
public bool bTry10 { get; }
}
Before you read the answer about adding such drop-down to property grid, consider these notes:
The usage of a group of RadioButton controls is like the usage of ComboBox to show/modify selected option among available options.
If you want to show value of those properties, it seems you are looking for data-binding.
If just one of those properties can be set to true, you can create a group of RadioButton controls and bind each control to the corresponding property of that class. This way the radio buttons can be used to show/modify those properties.
Note: In this case it seems it's better to have an enum containing all options and just a single property of type of that enum in the class.
If more than properties can have true values, you can use a group of CheckBox controls and bind them to corresponding property of the class.
Anyway if you want to show such drop-down in property grid, you can use either of these options:
You can create an Enum and define your property of that enum type. This way a drop-down will be shown in property grid for your property. (The most simple option)
You can register a custom TypeConverter for your property and overriding GetStandardValuesSupported provide some standard values for the property to show in drop-down. To see an example, take a look at: Type Converters That Provide a List of Standard Values to a Properties Window
You can register a UITypeEditor for the property. As an example take a look at Walkthrough: Implementing a UI Type Editor
I'm using a PropertyGrid class to edit objects within my application. These are the relevant classes (or rather, simplifications thereof):
public class Inner
{
public int A { get; set; }
public string B { get; set; }
}
public class Outer
{
public List<Inner> InnerData { get; set; }
public int Id { get; set; }
}
I will set an object of type Outer as the SelectedObject field of my property grid. The problem comes when an Outer object has it's InnerData property set to null. Null is considered an acceptable value for this property as the InnerData property represents "optional" data, and not having it specified is not the same thing as specifying an empty list. Ideally I'd like a user to be able to replace a null InnerData property with a real value by specifying the components of the new list, modify an existing non-null InnerData value, and replace an existing InnerData value with null.
Anybody know how to make this happen?
Have a look at creating a UITypeEditor, i think that if you use an editor you will have more control over the list and be able to tell if the current value is null and if so you can have the editor show a blank grid or something where list items can be added or removed, you could also add a checkbox to tell the editor to return null again and set null on the property, the editor is basically a WinForm so you can do almost anything in it.
internal class GenericTypeEditor : UITypeEditor
{
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
IWindowsFormsEditorService winFormEditorSvc = (IWindowsFormsEditorService)provider.GetService(typeof(IWindowsFormsEditorService));
using (MyForm editorForm = new MyForm())
{
if (winFormEditorSvc.ShowDialog(editorForm) == System.Windows.Forms.DialogResult.OK)
value = editorForm.ReturnObject;
}
return value; //this can be null if you wish
}
public override UITypeEditorEditStyle GetEditStyle(System.ComponentModel.ITypeDescriptorContext context)
{
return UITypeEditorEditStyle.Modal;
}
}
Then just set the attribute on your property
[EditorAttribute(typeof(GenericTypeEditor), typeof(System.Drawing.Design.UITypeEditor))]
public List<Inner> InnerData { get; set; }
This article helped me in the past, maybe it is of help to you:
http://msdn.microsoft.com/en-us/library/ms171840(v=vs.100).aspx
The property grid tries to add new Inner items to the InnerData object, but since you have not initialized it, the property grid has no where to save the added items. You need a constructor in Outter that will initialize InnerData just as a new List. You dont have to put any items into it, the user can do this at runtime, and can empty them back out as well, but the InnerData list Object needs to be initialized.
If you just want an Inner as a property, add System.ComponentModel to your usings and try this
[TypeConverter(typeof(ExpandableTypeConverter))]
public Inner DefaultInner { get; set; }
This will make your object expandable in the property grid so that you can set its nested properties
Try handling the PropertyGrid.SelectedGridItemChanged event:
private void propertyGrid1_SelectedGridItemChanged(object sender, SelectedGridItemChangedEventArgs e)
{
if ((e.NewSelection.Label == "InnerData") && (_outter.InnerData == null)) _outter.InnerData = new List<Inner>();
}
Then whenever the InnerData item is selected, if the collection is null, its initialized to a new list.
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<>
When I add an item to the CheckedListBox list box I also want to store a reference to another object. I tried adding a new instance of this object to the CheckedListBox.
public class CheckedListBoxExtention : CheckedListBox
{
private ReferenceItem _referenceItem;
public ReferenceItem storedItem
{
get { return _referenceItem; }
set { _referenceItem = value; }
}
public CheckedListBoxExtention(ReferenceItem storedItem)
{
_referenceItem = storedItem;
}
}
This works in that later when I foreach though the items in CheckedListBox I have a reference to the _referenceItem object. However, when I add items like this, CheckedListBox shows up as blank (the list in the GUI itself). So I am trying to find a way to override the item text or something like that.
This is the code I used to fix the problem
class ReferenceItemWrapper
{
private ReferenceItem _item;
public ReferenceItemWrapper(ReferenceItem item)
{
_item = item;
}
public ReferenceItem getItem
{get {return _item;}}
public override string ToString()
{
return _item.ToString();
}
}
I am a bit new to wrappers. Why exactly did it work after it was wrapped when it did not work when I added the ReferenceItem directly to the CheckedListBox?
The CheckedListBox uses the ToString method of the objects in the list to populate the captions in the box. Rather than extend the CheckedListBox, just create a wrapper class that lets you store both your reference and a caption, and implements a ToString method which returns your caption. Just create one of your wrapper objects, stick the text in it, stick your reference in it, then add the wrapper object to the list box.
Hi
I created two usercontrol (the line & the station) and use the STATION in THE LINE usercontrol and finally i use the "LINE" usercontrol in some forms.
There is no problem at design time, but whem i want to add "Line" usercontrol to form.
In this case, visual studio stop working and then closed.
I comment some changes in my code and find that problem is from property about number of stations in line. whem i comment it every thing go ok but i need this property.
this is my code
[Serializable]
public class ActionPoint
{
public string CarInfo;
public string RightStationName;
public string RightStationInfo;
public string LeftStationName;
public string LeftStationInfo;
public ActionPoint()
{
}
}
and use this class in my property. I use this property to define stations in line.
public class Line : UserControl
{
public List<ActionPoint> Stations
{
get { return Stations; }
set { Stations = value; }
}
}
That code is recursive (when you try to get or set that property it goes on forever - follow the code...); the IDE is probably trying to either display that in a property-grid, or write code for the designer.cs - and is hitting your friendly stack-overflow exception.
As appropriate implementation would be:
private List<ActionPoint> stations = new List<ActionPoint>();
public List<ActionPoint> Stations { get { return stations; } }
I think the problem with property, not list, try this.
List<ActionPoint> _Stations;
public List<ActionPoint> Stations
{
get { return _Stations; }
set { _Stations = value; }
}
or
public List<ActionPoint> Stations
{
get;
set;
}