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
|
I am using the Alloy example to style my propertygrid. It looks great except for the properties that are collections. I want to style them using the ButtonEffect. I've tried a few things without success. Can anyone provide some assistance? |
|
|
Hi Mark, You can override the collection UI by providing your own DataTemplates with the keys of PropertyGrid.CollectionDisplayKey and PropertyGrid.CollectionElementEditorKey. The bad news is that by doing this you take on complete responsibility for the contents of the right-hand column; we don't currently provide a way to style just the collection add/remove buttons. And this means you need to know something about how the WPF Property Grid wires up collections. It's probably easiest to explain with an example. For this we're just going to reproduce the default Windows Forms-style appearance, but we're going to use hyperlinks instead of buttons for the Add and Remove UI. All of the following goes into the Window.Resources section. First, we're going to need some plumbing to help our replacement UI fit snugly into the space allocated for it: <ms:MarginInversionConverter x:Key="MarginInversionConverter" /> Now we'll build our replacement UI for the collection row: <DataTemplate x:Key="{x:Static ms:PropertyGrid.CollectionDisplayKey}"> What's going on here? The DataTemplate key causes this style to "hijack" the built-in style. The outer Border is just adjusting the positioning for the WinForms-style template. You will want to replace this with your own Alloy-style design. The DockPanel contains three TextBlocks, representing the "n items" text (second and third) and "Add" command UI (first). In this example, the command UI is a hyperlink which sends the CollectionAddCommand, with a parameter of the collection itself ({Binding Value} resolves to the collection object). In your case, you will want to use a suitably styled button instead of the textblock and hyperlink. Now we need to provide a similar template for the collection elements. <DataTemplate x:Key="{x:Static ms:PropertyGrid.CollectionElementEditorKey}"> Again, let's step through this to see how it works. As before, the DataTemplate has a key which hijacks the normal collection item display; and again we have a DockPanel to contain the removal UI and the actual editor. This time however we need a bit more trickery because we need to display the right type of editor, but we don't know what type of editor that is. This is where the EditorDecorator class comes in. The grid displays collection items within an EditorDecorator, and we can consult this to find out what we're referring to. So our editor area is a ContentControl whose content is the value to edit (Content="{Binding}") and whose ContentTemplate is the DecoratedTemplate property of the enclosing EditorDecorator (which the grid sets to the right thing). And our remove UI, in this case a hyperlink but in your case a customised button, sends the CollectionRemoveCommand with a parameter of the Node associated with the enclosing EditorDecorator (CommandParameter="{Binding DecoratedNode, ..}"). Note, by the way, that you don't need to explicitly point the grid at these templates the way you do with custom editors. Because the templates hijack the keys that are built into the grid, the grid finds them automatically. I hope this makes sense. This wasn't really something we were planning to expose in v1.0 which is why the documentation doesn't explain how all these bitty little classes fit together. In future versions we are likely to open up the decorator extensibility model in the same way as the editor extensibility model, which will enable you to do this in a simpler and more consistent way; for the time being though this should serve as a workaround. |
|
|
Ivan, I am looking for a way to replace the collections editor items name column (i.e., the column that reads Item[0] Item[1] ... ) with something more meaningful. I found this thread that shows one way to do it, but the last paragraph alludes to an upcoming improved way to do it (In future versions we are likely to open up the decorator extensibility model in the same way as the editor extensibility model). Is that available yet?
Thanks Chris |
|
|
Hello Chris, No, I'm afraid we've not yet been able to add those extensibility hooks. So for now, the templating approach described in the previous messages in this thread is still the only option. |
|
|
Ivan,
Ok, I am trying to use the above example (replacing the data template CollectionElementEditorKey) to change the Item[0], Item[1], text. I have the example working, but it is not clear how to change the Item[n] text. Can you show me how to do it in the XAML?
Thanks |
|
|
Oops, sorry, I misread the example when I skimmed it. This is just showing how to override the collection editing UI (the stuff in the right hand column of the grid). To override the Item[n] captions you need to use the PropertyGrid.PropertyNameTemplate: <ms:PropertyGrid PropertyNameTemplate="{StaticResource CustomNameTemplate}" /> What does that custom template have to look like? Well, the first issue is that it has to be responsible for *everything* on the left hand side, including the expand-collapse button, the indenting, etc., so there's a good bit of boilerplate required to get started: <Style x:Key="EmptyStyle"/> <DataTemplate x:Key="CustomNameTemplate"> This is not actually as bad as it looks -- most of it is just triggers for when to show the expansion button, tooltip support, etc. The bit that is displaying the actual name is the TextBlock highlighted in the above XAML (near the top), and in particular its Text binding to Node.HumanName. That's the only bit we're going to need to change -- everything else can just be left as it is and you shouldn't need to worry about it again. So what do we need to replace that binding with? Well, in general Node.HumanName is going to be fine; we only want to replace it if Node is a CollectionElement. We could do this with a suitable DataTrigger but I'm going to be a bit lazy here and use a converter instead. This has some downsides (it won't update if the data you want to display may change and you want the caption to update accordingly) so it may not be suitable for production use but should give you the idea. Here's the converter: public class SmartNodeNameConverter : IValueConverter { Obviously, the caption I'm returning for collection elements here is just for proof of concept. You can drill into the Node.Value to pick out more meaningful data or into the Node.Property to get at metadata such as attributes, or CollectionElement.IndexedPropertyArguments to get at the index into the collection. Now we just change our binding to use this: <local:SmartNodeNameConverter x:Key="Snnc" /> ... in the DataTemplate ... Hope this is enough to get you started and apologies again for not spotting that the previous answer wasn't actually appropriate to your question. |
|
|
Ivan, Thanks for this. When I get time I will try it out. Of course, it would be nice if there were an easy way to replace just the property name text and leave all the other behavior/look as it was. In the Windows Forms property grid, overloading the ToString() of a complex class can be used to change the property name displayed in the grid. |
|
|
Ivan, This solution is working but I noticed that none of the provided styles (I'm using Alloy which works well with the rest of my application) uses the PropertyNameTemplate from the actual property grid control. The generic.xaml of the property grid has the following:
<GridViewColumn CellTemplate="{Binding PropertyNameTemplate, RelativeSource={RelativeSource AncestorType={x:Type local:PropertyGrid}}}" Whereas Alloy just uses: {Binding Node.HumanName} in the FancyNodeTemplate style.
This means that setting the PropertyNameTemplate property the following way: Alloy alloyStyle = new Alloy(); has no effect.
Is this by design, or is this a bug?
Thanks, Pablo
|
|
|
It's a limitation. Because the custom styles effectively provide their own property name templates, we didn't consider it a priority to support user-supplied templates. We'll look at adding this support, but I can't make any promises on timescales. However, we provide the source XAML to the custom styles (in all editions of the grid) so you can always create your own adaptation of the custom style which does respect PropertyNameTemplate. If you decide to do this and are able to share your changes with the community then that would be great! |
|
|
Ivan, For now I just set our own version of the alloy style to bind to the PropertyTameTemplate in both the ItemTemplate of the TreeListView, and the CellTemplate of the GridViewColumn in the property grid style (Instead of statically referencing the FancyNodeTemplate). I then adapted our own version based on the FancyNodeTemplate and set it as the PropertyNameTemplate when initializing the property grid. Thanks, Pablo |
|