Hi,
I've am attempting to use the propertygrid in a new application so I am fairly new on using it. My application allows the user to dynamically add controls (button, label, image, rectangle, ellipse, etc) to a canvas and move and resize them. I wish to use the propertygrid to allow the user to also change properties of the control that were added.
In the process of testing I noticed that when adding a primitive control (rectangle, ellipse) and setting the SelectedObject to the newly created object the property grid gets an exception "at Xceed.Wpf.Toolkit.PropertyGrid.Editors.PrimitiveTypeCollectionEditor.ResolveValueBinding(PropertyItem propertyItem)
at Xceed.Wpf.Toolkit.PropertyGrid.Editors.TypeEditor`1.ResolveEditor(PropertyItem propertyItem)
at Xceed.Wpf.Toolkit.PropertyGrid.ObjectContainerHelperBase.GenerateChildrenEditorElement(PropertyItem propertyItem)
at Xceed.Wpf.Toolkit.PropertyGrid.ObjectContainerHelperBase.PrepareChildrenPropertyItem(PropertyItemBase propertyItem, Object item)
at Xceed.Wpf.Toolkit.PropertyGrid.PropertyGrid.OnPreparePropertyItemInternal(Object sender, PropertyItemEventArgs args)"
The inner exception was "System.IndexOutOfRange" exception. After researching the issue further by debugging "PrimitiveTypeCollectionEditor" I believe I've found the root of the problem:
* The problem in ResolveValueBinding is that "Editor.ItemType = type.GetGenericArguments()[0]" is attempting to access index 0 when for this type no generic parameters exist. Inspecting the code with debug I found the type to be {Name = "DoubleCollection" FullName = "System.Windows.Media.DoubleCollection"}
I patched the code to the see if I could get around the issue. The code patch was as follows:
// DEK Check to see if type contains generic parameters before referencing index 0
if (type.ContainsGenericParameters)
{
Editor.ItemType = type.GetGenericArguments()[0];
}
With additional testing I found the same issue occurred again in code "PrimitiveTypeCollectionControl". Again I patched the code as follows:
// DEK
if (newValue.GetType().ContainsGenericParameters)
{
if (ItemType == null)
ItemType = newValue.GetType().GetGenericArguments()[0];
}
SetText(newValue);
I've retested and my application is working as expected now with both the Ellipse and Rectangle controls. Attached are the two modules with the new modifications.
____________________________________________________________________________________________________________________________
The second issue I've encountered was while attempting to use the XamlDesignerSerializationManager(XMLWriter) to save the XAML definition of my modified form so that I could restore it later. I am able to make this work when I save reference to my Canvas that I've added my controls to but when attempting to save the XAML when referencing the Grid containing the AvalonDockManager my application abends with a stack overflow exception. I understand what causes this and have to tried to resolve it by removing any binding with child elements that reference higher parent elements - thinking this is forcing recursive calls. I have not been able to resolve this at all. I am not terribly concerned though since I am able to save my Canvas, containing all of my newly created controls, and restore it later.
While I have a workaround I wanted to let you know as this may indicate an unexpected recursive call scenario in your code. Microsoft documentation indicates that only 1000 stack entries are allowed and I'm not sure why this would be exceeded when propagating through a very simple AvalonDock layout.
____________________________________________________________________________________________________________________________
Now for a question on using PropertyDefinitions. When I add a control to my form I am able to set the SelectedObject to the new control (standard or primitive) and get all of the control properties populated into the propertygrid. My preference would be to only expose specific control properties for the user to change. I've experimented with setting the SelectedObject to the the control I want to reference and then setting the AutoGenerateProperties to False. I am attempting to inform my propertygrid which properties to show by using PropertyDefinitions but haven't had much success. I am unable to set these values in the XAML since these controls don't exist at runtime. Also I've been unable to find any examples that show how to do this properly in code. My assumption, and maybe incorrect, was that if I set propertydefinitions to existing properties that the propertygrid would handle referencing the properties and attach the necessary editordefinitions as well to handle them.
** Is it possible to add propertydefinitions in code and can you provide me with an example?
Again my workaround would be to handle the sets and gets of each property in my own code but would prefer to minimize my coding as much as possible and leverage existing propertygrid functionality.
Please let me know what you find or if you need additional information to properly respond to my findings or request for information.
Regards,
Don
Comments: ** Comment from web user: BoucherS **
Hi,
1) System.IndexOutOfRange exception.
Thanks, the fix will be included in v2.3
2) XamlDesignerSerializationManager with AvalonDock
Issue https://wpftoolkit.codeplex.com/workitem/20767 has been created.
3) add propertydefinitions in code-behind
There is a bug preventing the propertyGrid from refreshing after the PropertyGrid.PropertyDefinitions have changed. It will be fix in v2.3. In the meantime, go in file :
-Xceed.Wpf.Toolkit/PropertyGrid/PropertyGrid.cs
In method :
OnPropertyDefinitionsCollectionChanged
At the end, add :
```
this.UpdateContainerHelper();
```
Here's a sample for modifying the PropertyDefinitions in code-behind:
```
<StackPanel>
<xctk:PropertyGrid x:Name="_propertyGrid"
AutoGenerateProperties="False">
</xctk:PropertyGrid>
<Button Content="Change Properties"
Click="Button_Click_1" />
</StackPanel>
public partial class MainWindow : Window
{
public MainWindow()
{
Xceed.Wpf.Toolkit.Licenser.LicenseKey = "XXXXX-XXXXX-XXXXX-XXXX";
InitializeComponent();
_propertyGrid.SelectedObject = new MyData() { ID = 12, FirstName = "Tom", LastName = "Sawyer" };
}
private void Button_Click_1( object sender, RoutedEventArgs e )
{
List<string> propertyList = new List<string>() { "FirstName" };
_propertyGrid.PropertyDefinitions.Add( new PropertyDefinition()
{
TargetProperties = propertyList
} );
}
}
public class MyData
{
public int ID
{
get;
set;
}
public string FirstName
{
get;
set;
}
public string LastName
{
get;
set;
}
}
```