This project has moved. For the latest updates, please go here.

PropertyGrid ExpandableObjectConverter and PropertyBag

Sep 30, 2011 at 8:54 AM
Edited Sep 30, 2011 at 10:27 AM

Would be possible to have ExpandableObjectConverter working into your PropertyGrid? 

For example if i have Vector3 property bound to Property grid, i should see 3 separate PropertyItem's rappresentingX, Y, Z.

What about dynamically bound properties, for example PropertyBag (http://www.codeproject.com/KB/recipes/propertybag.aspx) ?

 

I'll try to implement something for myself and see what i can get.

Anyway, good work your doing.

Thanks,

Amer

Sep 30, 2011 at 10:44 AM

PropertyBag is perfectly working now so no need for additional coding.

As for multi-component properties, I think WPF-style way is to implement your own editors.

Sep 30, 2011 at 1:48 PM

Nested properties will eventally make it into the control.

Oct 7, 2011 at 2:18 AM

Nested properties are now supported.  I decided not to implement the old WinForm way of doing it with the ExpandableObjectConverer.  Instead you simply decorate your property the ExpandableObjectAttribute.

Oct 10, 2011 at 9:16 AM
Edited Oct 10, 2011 at 9:16 AM

Hi,

I've just tried out the new solution you've added, by using the ExpandableObject, this does not fit well, what i was requesting, would be better if you use the ExpandableObjectConverter as i've written before, instead the problem is that even using new way i can property set the value, but when i for example change the value and when getting back value the values does not get changes, so probablty ExpandableObjectConverter would fit better.

Oct 10, 2011 at 1:50 PM

The ExpandableObjectConverter does not work the same in WPF as it does in WinForms and the majority of the methods are not called, so it has no purpose with this WPF property grid.  As for your issue, could you explain  what you mean.  I don't really understand.  As long as the object that contains the nested properties is set to an instance of an object, it should work.  I have had no issues setting values of nested objects.  If the object is not set to an instance then any changes to properties will not work, because there isn't an instance to data bind against.

Are talking about setting the value in code and having the property grid show the updated values?

If I misunderstood you issue, please clarify.

Oct 10, 2011 at 2:54 PM
Edited Oct 10, 2011 at 2:55 PM
public struct Vector3
{
[Description("X")]
public float X { get; set; }
[Description("Y")]
public float Y { get; set; }

}

public class MyInstance

{


[ExpandableObject]
public Vector3 Position { get; set; }

}

If i bind MyInstance to property grid and modify X value of Position, it does not get changed property, try it and you'll 
see what i mean, anyway would be fine i could specify the ExpandableObject once (by settings it on Vector3) and then no 
need to set it on Vector3 Position.

Hope this clarify,

Thanks

 

Oct 10, 2011 at 3:01 PM

I see what you mean.  This is very strange and only occurs on a struct.  When you initially change the value, the setter of the property is called correctly with the corrct value, but when the getter is called directly after the setter, the value reverts back to 0.  Very interesting indeed.  I will look into this.

Oct 10, 2011 at 3:16 PM

for now you can change it from a struct to a class and it will work correctly.

Oct 10, 2011 at 3:31 PM
Edited Oct 10, 2011 at 3:31 PM

Alright, so I looked into this and have found out why this is occuring.  This problem occurs because structs are passed by value -- that is, the PropertyGrid receives a *copy* of the Vector3 structure in your class, rather than a reference. Consequently, the data binding expressions end up binding to (and modifying) that copy rather than the original values.

I will do some digging to see if I can find a work around for this.  Until then, you will need to change it from a struct to a class if you want it to work.

Oct 10, 2011 at 3:45 PM

Another option would be to create your own custom editor to edit the struct properties.

Oct 10, 2011 at 3:49 PM
Edited Oct 10, 2011 at 3:50 PM

Ok,

Thats fine, anyway, Winform property grid retrieve public fields from struct too, probably would be fine if you bind Fields to Property too, for example if my Vector3 i need to declare X,Y as properties, but in most scenario (like Xna) they are fields, so probably binding for fields need to be done too (see example, X and Y need to be bound to PropertyGrid too):

 

public struct Vector3
{
[Description("X")]
public float X;
[Description("Y")]
public float Y;
[Description("Z")]
public float Z;
}

public class MyInstance

{


[ExpandableObject]
public Vector3 Position { get; set; }

}
Oct 10, 2011 at 3:52 PM

You can't databind to fields in WPF.  They have to be properties.

Oct 10, 2011 at 4:16 PM

Here is a workaround:

    public struct Vector3
    {
        [Description("X")]
        public float X;
        [Description("Y")]
        public float Y;

        public Vector3(float x, float y)
        {
            this.X = x;
            this.Y = y;
        }

    }
    public class Vector3Converter : IValueConverter
    {
        static Vector3 _originalValue; // the static struct that stores original value at the start of editing

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            _originalValue = ((Vector3)value);

            if (parameter.ToString() == "X")
                return ((Vector3)value).X;
            if (parameter.ToString() == "Y")
                return ((Vector3)value).Y;

            return _originalValue;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (parameter.ToString() == "X")
                _originalValue = new Vector3(float.Parse(value.ToString()), _originalValue.Y);
            if (parameter.ToString() == "Y")
                _originalValue = new Vector3(_originalValue.X, float.Parse(value.ToString()));

            return _originalValue;

        }
    }
            <extToolkit:PropertyGrid x:Name="_propertyGrid" Width="450" Margin="10"  >
                <extToolkit:PropertyGrid.EditorDefinitions>
                    <extToolkit:EditorDefinition TargetType="{x:Type local:Vector3}">
                        <extToolkit:EditorDefinition.EditorTemplate>
                            <DataTemplate>
                                <StackPanel Orientation="Horizontal" >
                                    <TextBox Text="{Binding Path=Value, Converter={StaticResource Vector3Converter},ConverterParameter=X}" Width="65" />
                                    <TextBox Text="{Binding Path=Value, Converter={StaticResource Vector3Converter},ConverterParameter=Y}" Width="65"/>
                                </StackPanel>
                            </DataTemplate>
                        </extToolkit:EditorDefinition.EditorTemplate>
                    </extToolkit:EditorDefinition>
                </extToolkit:PropertyGrid.EditorDefinitions>
            </extToolkit:PropertyGrid>

This will allow you to editor your Vector3 property and removed the need for properties and instead allow you to use fields.

Oct 10, 2011 at 4:16 PM

Forgot to mention you no longer need the ExpandableObjectAttribute on the property, so it can be removed.

Oct 11, 2011 at 9:45 AM

 

I think this is too complex and to anoying to specify the Converter, what i propose is to apply automatic theconverter to all Vector3 binding, in my project i have lot of classes that uses the Vector3,

and when i bind them to property grid, would be fine to have the IValueConvertedautomatically applied to all classes that have Vector3 property inside.

Oct 11, 2011 at 1:40 PM

How is this complex?  You are simply writing your converter as an IValueConverter instead of a TypeConverter and using it on a DataTemplate instead of your Struct, which is actually decoupling the presentation of the data from the actual Struct itself.  This DataTemplate will be applied to every class that has a property of type Vector3.  SO it doesn't matter how many classes you have that contain a Vector3 property.  You can even place the DataTemplate in a ResourceDictionary so if can be used on mulitple PropertyGrid controls.

As it stands this is the only way to accomplish your task.

Jul 24, 2012 at 9:57 AM
Edited Jul 24, 2012 at 10:02 AM

Hi! I've tried to add Vector3Converter in my project, but I got a run-time error "the resource Vector3Converter cannot be resolved" in XAML.

Did I do something wrong?

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Windows;
using System.Windows.Data;

namespace Xceed.Wpf.Toolkit.Core.Converters
{
    public class Vector3Converter : IValueConverter
    {
        static Vector3 _originalValue; // the static struct that stores original value at the start of editing

        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            _originalValue = ((Vector3)value);

            if (parameter.ToString() == "X")
                return ((Vector3)value).X;
            if (parameter.ToString() == "Y")
                return ((Vector3)value).Y;
            if (parameter.ToString() == "Z")
                return ((Vector3)value).Z;

            return _originalValue;
        }

        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            if (parameter.ToString() == "X")
                _originalValue = new Vector3(float.Parse(value.ToString()), _originalValue.Y, _originalValue.Z);
            if (parameter.ToString() == "Y")
                _originalValue = new Vector3(_originalValue.X, float.Parse(value.ToString()), _originalValue.Z);
            if (parameter.ToString() == "Z")
                _originalValue = new Vector3(_originalValue.X, _originalValue.Y, float.Parse(value.ToString()));

            return _originalValue;

        }

    }
}

Jul 25, 2012 at 9:32 AM
Edited Jul 25, 2012 at 9:33 AM

After I add the codes in XAML, the error is gone.

 

    <Window.Resources>
        <xctk:Vector2Converter x:Key="Vector2Converter"/>
        <xctk:Vector3Converter x:Key="Vector3Converter"/>
        <xctk:Vector4Converter x:Key="Vector4Converter"/>
    </Window.Resources>

But now I find another problem. When I modify the X,Y,Z value in propertyGrid, it cannot fire the setter.
Any idea?
Jul 30, 2012 at 8:39 PM

I must have missed something...

One of the posts at the beginning of this thread indicated (or at least led me to beleive) that the PropertyGrid control could be bound to a PropertyBag (CodeProject) object.

How can I do this?  What did I miss?  By the way, I am still fairly *new* to WPF so sometimes I still have a hard time wrapping my mind around the databinding thing but it does look promising for what I am trying to do.