This thread looks to be a little on the old side and therefore may no longer be relevant. Please see if there is a newer thread on the subject and ensure you're using the most recent build of any software if your question regards a particular product.
This thread has been locked and is no longer accepting new posts, if you have a question regarding this topic please email us at support@mindscape.co.nz
|
Hello, I'm trying to add my property editor in runtime. Everything works fine, except the DataContext of the editor is set to null. Here the sample code :
var dt = new DataTemplate();
var factory = new FrameworkElementFactory(typeof(MyUserControl));
dt.DataType = typeof(MyDataType);
dt.VisualTree = factory;
var editor = new TypeEditor()
{ EditedType = typeof(MyDataType),
};
EditorTemplate = dt };
propertyGrid.Editors.Add(editor);
so the code is very simple, I create a UserControl called 'MyUserControl' to use as a DataTemplate. I then create the DataTempte based on this usercontrol, create my TypeEditor and Add the new Editor to the propertyGrid Editors collection.
When I browse MyDataType in the property Grid I can see the UserControl as I expected, but...sigh no data is displayed (the UserControl use relative Data Binding). I've checked and the Datacontext is null, I checked also all the parent up and DataContext is set only in the propertyGrid (propertyGridRow object and not MyDataObject of course). if I use a ResourceDataTemplate (like in your example) the Data context is set correctly. what's wrong ? sigh Please help
|
|
|
This looks okay. Do you see any data binding errors in the Output window? If you don't see any errors in the Output window, could you post a simple but complete project that reproduces the problem? Thanks! |
|
|
Hi Ivan,
I've tried to reproduce the problem starting from your samples sources. Attached you can find the samples source modified. You should look at step 4 page, where there are the Editor template projectis.
I tried first a simple approach adding a template for the same data type (Person) and it did work. Then I tried to simulate more my application type, so I've added a WPF User Control Library project which contains the data type and the usercontrol. With this approach the editor is not used by the propertygrid, it's a different problem from what I see in my project but it may be a starting point for you to debug.
My original problem is in fact that the UserControl.DataContext is null instead of having it set to the my data type object
Thank you
ps : unfortunately there are no Databinding interesting messages in my output window (apart tons of other messages though) |
|
|
There are three problems here. First, your TypeEditor is assigned to edit things of type MyDataType, but the Name property is of type String. Therefore the TypeEditor does not match and your template never gets instantiated. Remember that editors in the property grid apply to *properties* of the SelectedObject, not to the SelectedObject itself. To invoke your DataTemplate, you must set a SelectedObject which has a *property* of type MyDataType. Second, you are performing your assert too early. When the constructor runs, the DataContext has not been assigned. (How could it be? DataContext is a property. WPF has to instantiate the UserControl1, which means finishing running the constructor, before it can set the DataContext.) So during the constructor the DataContext is null and your assert fails. By the time the UserControl1 has been displayed, though, the DataContext *is* set to the MyDataType, as you will be able to confirm from your button logic. Third, your Button.Tag is binding to {Binding Value}, but MyDataType (which is the DataContext) doesn't have a Value property. So this will cause binding errors. If you want the Tag to be bound to the actual MyDataType object itself, you must use {Binding} with no path. (But note this cannot be a two-way binding.) {Binding Value} is used only in property editors, or type editors for value types. You may need to consider the semantics of MyDataType -- is it a value type (like Decimal) or an entity type (like Person). |
|
|
Hi Ivan,
yup, in the rush of preparing the example I've made a mess, you're right. So attached there is the sample that let you see what the problem is. There is a class MyDataTypeContainer which contains 3 MayDataTypes : data1, data2 and data3. data1 and data2 are set to values in the ctor while data3 has been left to a null reference. now, when you run the sample you can see the DataContext correct for data1 and data2 (I've added a button to check for the DataContext). But when you click on data3 the Datacontext is null. You may say that is null since data3 is a null reference but you may agree also that in this case the property data3 should be hidden or a default value should be constructed before been displayed in the propertygrid. or you may disagree both.
Thank you for your support |
|
|
The DataContext is null because the property value is null. Regarding your suggestions for this: * We do not wish to hide the property in this case because the editor's job may be to let the user set it to something other than null. For example, many objects have string properties that are initially null, and we definitely want users to be able to edit those string properties! * We don't want to construct a default value because that's not our job -- if you want a default value rather than null then that should be part of your business logic, not an accident of displaying the object in a property grid. So you need to decide: is null an appropriate value for data3, or should it really be an "empty" MyDataType? If the latter (i.e. you decide to use an empty MyDataType), you just initialise it in your constructor and all is well. If the former, you have another decision to make: should users be allowed to re-initialise MyDataType properties, thus replacing null values with new (non-null) MyDataType isntances, or are they really editing the existing MyDataTypes, in which case the null value has to be presented as read-only because it can't be edited? If the latter (i.e. always editing existing instances and therefore nulls are read-only), then you just need to use a DataTemplateSelector or a trigger to display a read-only template in the null case and you are done. If the former (i.e. you want users to be able to set MyDataType properties to new values, i.e. new instances of MyDataType), things get a little bit trickier, because a WPF Property Grid TypeEditor by default assumes it is editing the properties of an existing instance rather than replacing it with a whole new instance. So we need to override this behaviour so that the DataTemplate can affect the property value itself rather than just the subproperties of the MyDataType stored in the property. To do this, you need to set TemplateBindingMode="WrappedValue" in the TypeEditor declaration. With this setting in place, the TypeEditor will bind the template not to the MyDataType, but instead to an ObjectWrapper<MyDataType>, where the MyDataType (null or otherwise) is available through the Value property. You'll probably be familiar with this behaviour from PropertyEditors, which also allow the templates to modify the property value rather than its subproperties (important because a property of type, say, Int32 doesn't *have* subproperties, and the user wants to set the property to a different Int32). With this change in place, you can will be able to set the property value by having a two-way binding to the Value property: <Button Tag="{Binding Value, Mode=TwoWay}" /> Now setting the Button.Tag will replace the existing MyDataType with the new value. Thus, setting Button.Tag = new MyDataType() will replace an existing value, null or non-null -- of whichever property is being edited, e.g. data3 -- with a new (empty) MyDataType. You can still access members of the existing value via a property path: <TextBlock Text="{Binding Value.Name}" /> though of course this will give binding errors if the property value is null (because Value will then also be null). |
|