PropertyGrid with possibly-null List - c#

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.

Related

Deriving from ComboBox

I need to derive a class from ComboBox and change its Items property. Here is my code:
public class MyComboBox2 : ComboBox
{
private MyObjectCollection MyItems;
public MyComboBox2()
{
MyItems = new MyObjectCollection(this);
}
//new public ComboBox.ObjectCollection Items
new public MyObjectCollection Items
{
get {
return MyItems;
}
}
}
public class MyObjectCollection : ComboBox.ObjectCollection
{
public MyObjectCollection(ComboBox Owner) : base(Owner)
{
}
new public int Add(Object j)
{
base.Add(j);
return 0;
}
}
As you can see, I am creating a new class MyComboBox2 derived from ComboBox. This class is supposed to have a new Items property, which would be of type MyObjectCollection rather than ComboBox.ObjectCollection. I have a comboBox called myComboBox21 on the form of type MyComboBox2. When I want to add a new object to my ComboBox, I would execute code like this: myComboBox21.Items.Add("text");
In this case, I end up executing the Add method of MyObjectCollection that I implemented myself. However, the ComboBox on the form does not end up containing value 'text'. I am attaching screenshot of debugger showing ComboBox values. MyComboBox21 contains Items Property (which does contain "text", as shown in screenshot "2.png"), and it contains base.Items (which does not contain "text" as shown in "1.png"). So, apparently, MyComboBox21 contains its own Items property (which I can insert to), and its base class's Items property, which gets displayed in the Windows Form. What can I do so that I can successfully add to comboBox with my own method? Since my ComboBox has 2 Items properties, can I specify which Items property's values should be shown in ComboBox?
Just by looking very quickly on the code:
The original Item index is declared as
virtual Object this[int index] {...}
Does the new keyword maybe be exchanged by override in your implementation in order to make the runtime pick the intended code?

User Control and List<Class>

i am trying to use Property Editor for my user control but it doesn't work.
if i set the property in the form load if works, but if i want to use the property editor it don't save my changes (when i click again in the property editor it comes clear)
this is how i define the property in my user control:
private List<Field> _searchField;
public List<Field> SearchField
{
get { return _searchField ?? (_searchField = new List<Field>()); }
}
You need to apply DesignerSerializationVisibility attribute to your property with DesignerSerializationVisibility.Content.
This tells the code generator to produces code for the contents of the object, rather than for the object itself. It helps in code generation for types other than primitive types.
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public List<int> SearchField { get { return _searchField ?? (_searchField = new List<int>()); } }

Display specified text for listbox items

I have a list that contains custom objects. These objects have different properties, and I have ~100 of them. I want to create a list of them in a listbox, but the listbox displays only
MyNamespace.MyClass
MyNamespace.MyClass
MyNamespace.MyClass
MyNamespace.MyClass
...
Is it possible to make the listbox display a certain value for each item? Lets say my objects have an ID string value. Can I display the ID for each item without discarding my objects' other properties?
I currently fill the listbox this way:
lbox.Items.Clear();
lbox.Items.AddRange(list.ToArray());
Set the DisplayMember to the property of your class that you'd like the user to see.
lbox.Items.Clear();
lbox.Items.AddRange(list.ToArray());
lbox.DisplayMember = "ID"; // ID is a public property in MyClass
Lets say you MyClass looks like this:
public class MyClass
{
public int Id { get; set; }
}
There are two options available.
You can use DataBinding for that.
Set the DisplayMember to the propertie of your MyClass which you would like to display
lbox.DisplayMember = "Id";
Set the items using the DataSource propertie of your ListBox
lbox.DataSource = list.ToArray();
You can simple override the ToString method of your MyClass object and return the text you would like to display.
Override the ToString method of your MyClass
public class MyClass
{
public int Id { get; set; }
public override string ToString()
{
return Id.ToString();
}
}
Set the items the same way as you mentioned
lbox.Items.AddRange(list.ToArray());
More Information
MSDN: ListControl.DisplayMember Property
MSDN: Object.ToString Method
Without discarding the object you can attach the object to the tag after.
list.ToList().ForEach(item => lbox.Items.Add(new ListItem(item.ID){Tag = item});
then to retreive it :
var myitem = ((ListItem)lbox.SelectedItem).Tag as MyClass;
Try using Linq.
lbox.Items.AddRange(list.Select(x => x.ID).ToArray());
Where ID is a property with the value you want to show.
You can also override ToString() in the class.

Using custom objects for (CheckedListBox).Items.Add()

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.

Editing a property of an object inside an object in PropertyGrid

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;
}

Categories

Resources