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();
To reproduce: 1. Add a RadPropertyGrid and a RadButton. 2. Create custom PropertyGridItemElement with custom PropertyGridValueElement in order to display permanently RadDropDownListElement 3. Use the following code snippet: public Form1() { InitializeComponent(); this.radPropertyGrid1.CreateItemElement += radPropertyGrid1_CreateItemElement; this.radPropertyGrid1.Editing += radPropertyGrid1_Editing; } private void radPropertyGrid1_Editing(object sender, PropertyGridItemEditingEventArgs e) { if (e.Item.Name == "Direction") { e.Cancel = true; } } private void radPropertyGrid1_CreateItemElement(object sender, CreatePropertyGridItemElementEventArgs e) { PropertyGridItem item = (PropertyGridItem)e.Item; if (e.Item.Name == "Direction") { e.ItemElementType = typeof(CustomItemElement); } } public class CustomItemElement : PropertyGridItemElement { protected override PropertyGridValueElement CreatePropertyGridValueElement() { return new CustomPropertyGridValueElement(); } protected override Type ThemeEffectiveType { get { return typeof(PropertyGridItemElement); } } } public class CustomPropertyGridValueElement : PropertyGridValueElement { RadDropDownListElement dropdown; protected override void CreateChildElements() { base.CreateChildElements(); dropdown = new RadDropDownListElement(); dropdown.DropDownStyle = Telerik.WinControls.RadDropDownStyle.DropDownList; this.Children.Add(dropdown); dropdown.DataSource = Enum.GetValues(typeof(Direction)); dropdown.SelectedIndexChanged += dropdown_SelectedIndexChanged; } private void dropdown_SelectedIndexChanged(object sender, Telerik.WinControls.UI.Data.PositionChangedEventArgs e) { RadDropDownListElement ddle = sender as RadDropDownListElement; if (ddle != null && e.Position > -1) { PropertyGridItem item = this.VisualItem.Data as PropertyGridItem; if (item != null) { item.Value = ddle.Items[e.Position].DataBoundItem; } } } public override void Synchronize() { PropertyGridItem item = this.VisualItem.Data as PropertyGridItem; dropdown.SelectedValue = item.Value; } } public class Item { public int Id { get; set; } public Direction Direction { get; set; } public string Title { get; set; } public Item(int id, string title, Direction direction) { this.Id = id; this.Title = title; this.Direction = direction; } } public enum Direction { Up, Down, Left, Right } private void radButton1_Click(object sender, EventArgs e) { this.radPropertyGrid1.SelectedObject = new Item(123, "Title", Direction.Left); } When you click the button once, the RadPropertyGrid displays the desired data. However, if you click the button once again, ArgumentOutOfRangeException occurs. Workaround:wrap setting the SelectedObject in a SuspendLayout/ResumeLayout block: this.radPropertyGrid1.SuspendLayout(); this.radPropertyGrid1.SelectedObject = new Item(123, "Title", Direction.Left); this.radPropertyGrid1.ResumeLayout();