To reproduce:
PropertyStoreItem prop1 = new PropertyStoreItem(typeof(DateTime), "Date", DateTime.Now);
PropertyStoreItem prop2 = new PropertyStoreItem(typeof(Nullable<DateTime>), "NullableDate", DateTime.Now);
PropertyStoreItem prop3 = new PropertyStoreItem(typeof(DateTime?), "Date?", DateTime.Now);
RadPropertyStore store = new RadPropertyStore();
store.Add(prop1);
store.Add(prop2);
store.Add(prop3);
this.radPropertyGrid1.SelectedObject = store;
Open the editor for one of the properties and press the Clear button in the PropertyGridDateTimeEditor's popup. The value is cleared, but the PropertyValueButtonElement is not displayed.
Workaround: specify the initial value as default value for the property:
public Form1()
{
InitializeComponent();
DateTime initialValue = DateTime.Now;
PropertyStoreItem prop1 = new PropertyStoreItem(typeof(DateTime), "Date", initialValue);
prop1.Attributes.Add(new DefaultValueAttribute(initialValue));
PropertyStoreItem prop2 = new PropertyStoreItem(typeof(Nullable<DateTime>), "NullableDate", initialValue);
prop2.Attributes.Add(new DefaultValueAttribute(initialValue));
PropertyStoreItem prop3 = new PropertyStoreItem(typeof(DateTime?), "Date?", initialValue);
prop3.Attributes.Add(new DefaultValueAttribute(initialValue));
RadPropertyStore store = new RadPropertyStore();
store.Add(prop1);
store.Add(prop2);
store.Add(prop3);
this.radPropertyGrid1.SelectedObject = store;
}
To reproduce: use the following code snippet and refer to the attached gif file.
public Form1()
{
InitializeComponent();
this.radPropertyGrid1.SelectedObject = this;
this.radPropertyGrid1.RightToLeft = System.Windows.Forms.RightToLeft.Yes;
}
Workaround:
public Form1()
{
InitializeComponent();
this.radPropertyGrid1.CreateItemElement += radPropertyGrid1_CreateItemElement;
this.radPropertyGrid1.SelectedObject = this;
this.radPropertyGrid1.RightToLeft = System.Windows.Forms.RightToLeft.Yes;
}
private void radPropertyGrid1_CreateItemElement(object sender, CreatePropertyGridItemElementEventArgs e)
{
if (e.ItemElementType == typeof(PropertyGridItemElement))
{
e.ItemElementType = typeof(CustomPropertyGridItemElement);
}
}
public class CustomPropertyGridItemElement : PropertyGridItemElement
{
private bool isResizing;
private Point downLocation;
private int downWidth;
protected override Type ThemeEffectiveType
{
get
{
return typeof(PropertyGridItemElement);
}
}
private const int resizePointerOffset = 3;
public override bool IsInResizeLocation(Point location)
{
return (location.X >= this.ControlBoundingRectangle.X + this.PropertyTableElement.ValueColumnWidth - resizePointerOffset &&
location.X <= this.ControlBoundingRectangle.X + this.PropertyTableElement.ValueColumnWidth + resizePointerOffset);
}
protected override void OnMouseDown(MouseEventArgs e)
{
if (IsInResizeLocation(e.Location))
{
if (this.PropertyTableElement.IsEditing)
{
this.PropertyTableElement.EndEdit();
}
this.Capture = true;
this.isResizing = true;
this.downLocation = e.Location;
this.downWidth = this.PropertyTableElement.ValueColumnWidth;
}
base.OnMouseDown(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (this.isResizing)
{
int delta = e.Location.X - downLocation.X;
if (this.RightToLeft)
{
delta *= -1;
}
this.PropertyTableElement.ValueColumnWidth = downWidth - delta;
return;
}
if (this.IsInResizeLocation(e.Location))
{
this.ElementTree.Control.Cursor = Cursors.VSplit;
}
else
{
this.ElementTree.Control.Cursor = Cursors.Default;
}
base.OnMouseMove(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
if (this.isResizing)
{
this.isResizing = false;
this.Capture = false;
}
base.OnMouseUp(e);
}
}
To reproduce: use the following code snippet and refer to the attached gif file:
public Form1()
{
InitializeComponent();
this.radPropertyGrid1.SelectedObject = this;
}
private void radButton1_Click(object sender, EventArgs e)
{
ThemeResolutionService.ApplicationThemeName = "VisualStudio2012Dark";
}
private void radButton2_Click(object sender, EventArgs e)
{
ThemeResolutionService.ApplicationThemeName = "ControlDefault";
}
Workaround:
private void radPropertyGrid1_EditorInitialized(object sender, PropertyGridItemEditorInitializedEventArgs e)
{
PropertyGridTextBoxEditor editor = e.Editor as PropertyGridTextBoxEditor;
if (editor != null)
{
BaseTextBoxEditorElement el = editor.EditorElement as BaseTextBoxEditorElement;
if (el != null)
{
el.BackColor = Color.Black;
}
}
}
The PropertyChanged event does not fire when the ValueColumnWidth property is changed.
Workaround: set the RadPropertyGrid.SelectedGridItem property to null before changing the SelectedObject.
To reproduce:
1. Add a RadPropertyGrid and populate it with all of of items by using the following code:
this.radPropertyGrid1.SelectedObject = this;
2. Expand two items.
3. Scroll to the bottom.
4. Scroll to the top.
5. If you scroll to the bottom again, you will see a lot of empty space. The attached gif file illustrates better the problem.
Workaround:
public Form1()
{
InitializeComponent();
this.radPropertyGrid1.SelectedObject = this;
this.radPropertyGrid1.PropertyGridElement.PropertyTableElement.Scroller.Traverser =
new CustomPropertyGridTraverser(this.radPropertyGrid1.PropertyGridElement.PropertyTableElement);
}
public class CustomPropertyGridTraverser : PropertyGridTraverser
{
public CustomPropertyGridTraverser(PropertyGridTableElement propertyGridElement) : base(propertyGridElement)
{
}
public int Index
{
get
{
FieldInfo fi = typeof(PropertyGridTraverser).GetField("index", BindingFlags.NonPublic | BindingFlags.Instance);
return int.Parse(fi.GetValue(this).ToString());
}
set
{
FieldInfo fi = typeof(PropertyGridTraverser).GetField("index", BindingFlags.NonPublic | BindingFlags.Instance);
fi.SetValue(this, value);
}
}
public int GroupIndex
{
get
{
FieldInfo fi = typeof(PropertyGridTraverser).GetField("groupIndex", BindingFlags.NonPublic | BindingFlags.Instance);
return int.Parse(fi.GetValue(this).ToString());
}
}
public PropertyGridTableElement Element
{
get
{
FieldInfo fi = typeof(PropertyGridTraverser).GetField("propertyGridElement", BindingFlags.NonPublic | BindingFlags.Instance);
return fi.GetValue(this) as PropertyGridTableElement ;
}
}
public PropertyGridItemBase Item
{
get
{
FieldInfo fi = typeof(PropertyGridTraverser).GetField("item", BindingFlags.NonPublic | BindingFlags.Instance);
return fi.GetValue(this) as PropertyGridItemBase ;
}
set
{
FieldInfo fi = typeof(PropertyGridTraverser).GetField("item", BindingFlags.NonPublic | BindingFlags.Instance);
fi.SetValue(this, value);
}
}
protected override bool MovePreviousFromDataItem(PropertyGridItemBase currentItem)
{
if (currentItem.Parent != null && currentItem.Parent.GridItems.Count > 0)
{
if (this.Index > 0)
{
PropertyGridItemBase prevItem = currentItem.Parent.GridItems[--this.Index];
if (prevItem.Expandable && (prevItem.Expanded || this.TraverseHirarchy))
{
prevItem = this.GetLastChild(prevItem);
}
this.Item = prevItem;
return true;
}
this.Item = currentItem.Parent;
this.Index = -1;
if (currentItem.Parent.Parent != null)
{
this.Index = currentItem.Parent.Parent.GridItems.IndexOf(currentItem.Parent as PropertyGridItem);
}
else
{
if (this.Element.CollectionView.Groups.Count == 0)
{
this.Index = this.Element.CollectionView.IndexOf(currentItem.Parent as PropertyGridItem);
}
else
{
this.Index = this.Element.CollectionView.Groups[this.GroupIndex].IndexOf(currentItem.Parent as PropertyGridItem);
}
}
return true;
}
return base.MovePreviousFromDataItem(currentItem);
}
}
Currently when expanding an item in RadPropertyGrid the order of the sub items changes on every start.
To reproduce: radPropertyGrid1.SelectedObject = new RadDropDownList(); The Items collection does not have a setter, but one still should be able to add items via the respective editor.
To reproduce:
private void PopulateGrid()
{
this.radPropertyGrid1.SortOrder = System.Windows.Forms.SortOrder.Ascending;
PropertyStoreItem myRadProp;
RadPropertyStore myRadProperties = new RadPropertyStore();
for (int i = 0; i <= 6000; i++)
{
myRadProp = new PropertyStoreItem(typeof(string), "PropName" + i, "Value" + i, "Help" + i, "same");
myRadProperties.Add(myRadProp);
}
radPropertyGrid1.SelectedObject = myRadProperties;
}
Workaround:
public Form1()
{
InitializeComponent();
radPropertyGrid1.PropertyGridElement.PropertyTableElement.ListSource.CollectionView.GroupFactory =
new MyPropertyGridGroupFactory(this.radPropertyGrid1.PropertyGridElement.PropertyTableElement);
this.radPropertyGrid1.SortOrder = System.Windows.Forms.SortOrder.Ascending;
PropertyStoreItem myRadProp;
RadPropertyStore myRadProperties = new RadPropertyStore();
for (int i = 0; i <= 6000; i++)
{
myRadProp = new PropertyStoreItem(typeof(string), "PropName" + i, "Value" + i, "Help" + i, "same");
myRadProperties.Add(myRadProp);
}
this.radPropertyGrid1.PropertyGridElement.PropertyTableElement.ListSource.BeginUpdate();
radPropertyGrid1.SelectedObject = myRadProperties;
this.radPropertyGrid1.PropertyGridElement.PropertyTableElement.ListSource.EndUpdate();
}
public class MyPropertyGridGroupFactory : IGroupFactory<PropertyGridItem>
{
private PropertyGridTableElement propertyGridElement;
public MyPropertyGridGroupFactory(PropertyGridTableElement propertyGridElement)
{
this.propertyGridElement = propertyGridElement;
}
public GroupCollection<PropertyGridItem> CreateCollection(IList<Group<PropertyGridItem>> list)
{
return new PropertyGridGroupCollection(list);
}
public Group<PropertyGridItem> CreateGroup(object key, Group<PropertyGridItem> parent, params object[] metaData)
{
return new MyPropertyGridGroup(key, parent, this.propertyGridElement);
}
}
public class MyPropertyGridGroup : PropertyGridGroup
{
private List<PropertyGridItem> items;
public MyPropertyGridGroup(object key, Group<PropertyGridItem> parent,
PropertyGridTableElement propertyGridElement) : base(key, parent, propertyGridElement)
{
}
protected override IList<PropertyGridItem> Items
{
get
{
if (this.items == null)
{
this.items = new List<PropertyGridItem>();
}
return this.items;
}
}
}
To reproduce:
public Form1()
{
InitializeComponent();
this.radPropertyGrid1.SelectedObject = new Item(123, "SampleName");
}
public class Item
{
public int Id { get; set; }
public string Name { get; set; }
public Item(int id, string name)
{
this.Id = id;
this.Name = name;
}
}
Activate the editor for the "Id" property and click the up arrow of the PropertyGridSpinEditor. Then click on the text box.
Workaround:
private void radPropertyGrid1_EditorInitialized(object sender, PropertyGridItemEditorInitializedEventArgs e)
{
PropertyGridSpinEditor spinEditor = e.Editor as PropertyGridSpinEditor ;
if (spinEditor != null)
{
BaseSpinEditorElement spinEditorElement = spinEditor.EditorElement as BaseSpinEditorElement;
var eventInfo = spinEditorElement.TextBoxItem.HostedControl.GetType().GetEvent("MouseEnter");
var methodInfo = spinEditorElement.TextBoxItem.GetType().GetMethod("TextBoxControl_MouseEnter",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
Delegate handler = Delegate.CreateDelegate(typeof(EventHandler), spinEditorElement.TextBoxItem, methodInfo);
eventInfo.RemoveEventHandler(spinEditorElement.TextBoxItem.HostedControl, handler);
var eventInfo2 = spinEditorElement.TextBoxItem.HostedControl.GetType().GetEvent("MouseLeave");
var methodInfo2 = spinEditorElement.TextBoxItem.GetType().GetMethod("textBoxItem_MouseLeave",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
Delegate handler2 = Delegate.CreateDelegate(typeof(EventHandler), spinEditorElement.TextBoxItem, methodInfo2);
eventInfo2.RemoveEventHandler(spinEditorElement.TextBoxItem.HostedControl, handler2);
}
}
To reproduce: - Add a property grid to a blank form set the selected object the the same form. - Group the items and click several times to expand collapse the groups or items. - you will notice that some of expanded/collapsed groups icon is in wrong state (same applies for the +/- sign of the properties).
Here is the code that should be used for custom sub-properties
private void OnCreateItem(object sender, CreatePropertyGridItemEventArgs e)
{
e.Item = new MyPropertyGridItem((PropertyGridTableElement)sender, e.Parent);
}
As visual studio would be happy of you only provide a constructor with one argument when you inherit from PropertyGridItem you have no way of knowing that you need a second one where the parent should be the second argument.
The proper way to handle this would be to set the parent internally.
To reproduce: use the following code:
Items _items = new Items();
public Form1()
{
InitializeComponent();
radPropertyGrid1.SelectedObject = _items;
}
class Items
{
[Description("A Bool Property")]
public bool IsEnabled
{
get
{
return _isEnabled;
}
set
{
_isEnabled = value;
RadMessageBox.Show("IsEnabled");
}
}
private bool _isEnabled;
[Description("A String Property")]
public string Name
{
get { return _name; }
set
{
_name = value;
RadMessageBox.Show("Name");
}
}
private string _name ;
}
Steps to perform:
1. Change the "Name" property and leave the editor active.
2. Click on the check box to change the "IsEnabled" property.
As a result you will notice that the "IsEnabled" setter is called before the "Name" setter.
Workaround:
Items _items = new Items();
public Form1()
{
InitializeComponent();
this.radPropertyGrid1.CreateItemElement += radPropertyGrid1_CreateItemElement;
radPropertyGrid1.SelectedObject = _items;
}
private void radPropertyGrid1_CreateItemElement(object sender, CreatePropertyGridItemElementEventArgs e)
{
if (e.ItemElementType == typeof(PropertyGridCheckBoxItemElement))
{
e.ItemElementType = typeof(CustomPropertyGridCheckBoxItemElement);
}
}
public class CustomPropertyGridCheckBoxItemElement : PropertyGridCheckBoxItemElement
{
public CustomPropertyGridCheckBoxItemElement()
{
this.CheckBoxElement.ToggleStateChanging += CheckBoxElement_ToggleStateChanging;
}
private void CheckBoxElement_ToggleStateChanging(object sender, StateChangingEventArgs args)
{
if (this.PropertyTableElement.ActiveEditor != null && this.PropertyTableElement.SelectedGridItem != this.Data)
{
this.PropertyTableElement.EndEdit();
}
}
protected override void DisposeManagedResources()
{
this.CheckBoxElement.ToggleStateChanging -= CheckBoxElement_ToggleStateChanging;
base.DisposeManagedResources();
}
}
To reproduce: - Add 20 document windows that contain property grid to a dock (use RadTextBox for he grid's selected object). - Close and dispose the windows. - The memory is not properly released.
To reproduce:
Set the following object as selected of RadPropertyGrid:
class TestClass : System.ComponentModel.INotifyPropertyChanged
{
private string loggingType = "0";
[TypeConverter(typeof(CustomListConverter)), CategoryAttribute("Logging Settings")]
public string LoggingType
{
get { return loggingType; }
set
{
loggingType = value;
OnPropertyChanged("LoggingType");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And use the following converter:
public class CustomListConverter : System.ComponentModel.StringConverter
{
ArrayList _AllStringValues = new ArrayList();
Hashtable _Map = new Hashtable();
public CustomListConverter()
{
}
public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(ITypeDescriptorContext conte
{
FillValuesFromContext();
StandardValuesCollection standardValues = new StandardValuesCollection(_AllStringValues);
return standardValues;
}
private void FillValuesFromContext()
{
if (_AllStringValues.Count > 0)
return;
_AllStringValues.Clear();
_Map.Clear();
string str = "-5,Fatal|-4,Error|-3,Warning|-2, Info|-1,Debug|0,Trace";
string[] strMapVals = str.Split('|');
for (int i = 0; i < strMapVals.Length; i++)
{
string[] strVals = strMapVals[i].Split(',');
if (strVals.Length != 2)
continue;
_AllStringValues.Add(strVals[0].Trim());
_Map.Add(strVals[0].Trim(), strVals[1].Trim());
}
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
FillValuesFromContext();
string displayString = "";
IEnumerator enumTable = _Map.Keys.GetEnumerator();
while (enumTable.MoveNext())
{
if (_Map[enumTable.Current] as string == value as string)
{
displayString = enumTable.Current as string;
break;
}
}
return displayString;
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
FillValuesFromContext();
if (null != value && _Map.ContainsKey(value.ToString()))
{
return _Map[value];
}
return value;
}
public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
{
return true;
}
public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
{
return true;
}
}
When you start the application you will see that the value of the property is set to -1, while it should be converted to 'Debug'
Workaround:
Create a custom PropertyGridItem:
public class MyPropertyGridItem : PropertyGridItem
{
public MyPropertyGridItem(PropertyGridTableElement element)
: base(element)
{
}
public override string FormattedValue
{
get
{
string baseFormattedValue = base.FormattedValue;
TypeConverter converter = TypeConverter;
if (converter != null && converter.CanConvertTo(this, typeof(string)))
{
baseFormattedValue = converter.ConvertToString(this, System.Threading.Thread.CurrentThread.CurrentCulture, this.Value);
}
return baseFormattedValue;
}
set
{
base.FormattedValue = value;
}
}
}
Use it by subscribing to the CreateItem event:
void radPropertyGrid1_CreateItem(object sender, Telerik.WinControls.UI.CreatePropertyGridItemEventArgs e)
{
e.Item = new MyPropertyGridItem(this.radPropertyGrid1.PropertyGridElement.PropertyTableElement);
}
Workaround:
class MyRadPropertyGrid : RadPropertyGrid
{
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case 0x7b:
Point point;
int x = Telerik.WinControls.NativeMethods.Util.SignedLOWORD(m.LParam);
int y = Telerik.WinControls.NativeMethods.Util.SignedHIWORD(m.LParam);
if (((int)((long)m.LParam)) == -1)
{
point = new Point(this.Width / 2, this.Height / 2);
}
else
{
point = this.PointToClient(new Point(x, y));
}
this.PropertyGridElement.PropertyTableElement.ProcessContextMenu(point);
return;
}
base.WndProc(ref m);
}
}
If one sets an empty RadPropertyStore to the SelectedObject property of a RadPropertyGrid and then adds items with a RadSortOrder attribute the items will still be sorted by their name. WORKAROUNDS: 1. Populate the store before you set it to the property grid. 2. Add a dummy item to the store before you set it to the property grid and then remove it: RadPropertyStore store = new RadPropertyStore(); store.Add(typeof(bool), "dummy", false); this.radPropertyGrid1.SelectedObject = store; store.RemoveAt(0); 3. After you add the first item to the store call the following method: this.radPropertyGrid1.PropertyGridElement.PropertyTableElement.ListSource.CollectionView.EnsureDescriptors();