I'm evaluating the possibilities to view and edit parameters in a C#/WPF/MVVM application. I'm currently looking into some custom controls from WPF Woolkit Extended, Telerik, DevExpress and Syncfusion. I see two options, a PropertyGrid or a DataGrid-like control.
The problem: My parameter content is in a collection and PropertyGrids don't seem to like those, they like to be bound to objects with properties. On the other hand my parameters have different value types and that is something the Data Grids do not favor, there the columns always have the same value editor (e.g. Check box, Date picker etc).
Does anybody know a reliable control that supports binding to a collection and individual value editors per row?
Other requirements are hierarchical data representation, validation and a search function.
DataGrid lets you use a DataGridTemplateColumn, where you can specify a template that will be applied to each cell in that column. This template could contain a UserControl that you have defined, which analyzes its DataContext and shows the appropriate control. (This will be quite a bit of work, though, so if there exist out-of-the-box solutions for this, you should use that instead.)
After a while of investigation I found out that all frameworks have the ability to apply individual cell editors in columns.
This is usually done in the column definition. A grid has a Columns collection. Each column can set a CellTemplateSelector which was the key to my initial problem.
Here is a little snippet for the Telerik grid view, but down to the CellTemplateSelector they all behave the same, as far as I can judge. All the vendor of 3rd party UI libs seems to have copied the behviour of the WPF DataGrid, of course.
<telerik:RadGridView x:Name="radGridView"
AutoGenerateColumns="False">
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn DataMemberBinding="{Binding Name}" Header="Parameter" />
<telerik:GridViewDataColumn DataMemberBinding="{Binding Value}" Header="Value">
<telerik:GridViewDataColumn.CellTemplateSelector>
<telerik:ConditionalDataTemplateSelector>
<telerik:DataTemplateRule Condition="PropertyId < 1">
<DataTemplate>
<TextBox Text="{Binding Value, StringFormat=c}"/>
</DataTemplate>
</telerik:DataTemplateRule>
<telerik:DataTemplateRule Condition="PropertyId > 0">
<DataTemplate>
<CheckBox IsChecked="{Binding Value}" />
</DataTemplate>
</telerik:DataTemplateRule>
</telerik:ConditionalDataTemplateSelector>
</telerik:GridViewDataColumn.CellTemplateSelector>
</telerik:GridViewDataColumn>
</telerik:RadGridView.Columns>
In this example the ItmesSource of the grid is bound to my view model that contains Name, Value and PropertyId properties. Based on the PropertyId a control is selected. The thing I like about the Telerik control is that I don't need code-behind since the ConditionalDataTemplateSelector is available in that framework. Better for MVVM to keep your view clean of code behind, I guess.
UPDATE:
Just received an answer from Syncfusion, they say:
We can load different cell editors in one column. For that we have to Handle dataGrid.Model.QueryCellInfo even and we have to change the CellType based on data. We can't avoid code behind. But this can be achieved using Behaviors.
Related
<press_limits value="1055" label="Press Limits" type="single 317" format="object">
<projected_area value="0.052944637336319995" label="Projected area of part" type="real(m*m)"/>
<press_tonnage value="500.0" label="Press tonnage" type="real(g)" units="0Ton"/>
<within_press_limit value="1" label="within limits of press" type="boolean"/>
From XML like the above, the XAML below will generate a form that displays the values, with appropriate controls and value formats. But I can't get the stuff converted back. I fail to see a simple change that will meet the requirements of Binding. Perhaps fixing this requires an architecture change. How can I do this differently?
The magic starts here. Bind this ItemsControl to an XmlElement, and it builds a ControlChooser for each subelement.
<ItemsControl ItemsSource="{Binding XPath=*}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate><WrapPanel/></ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<W3V:ControlChooser Content="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
The ControlChooser triggers on the format attribute:
<Style.Triggers>
<DataTrigger Binding="{Binding XPath=#format}" Value="spin">
<Setter Property="ContentTemplate" Value="{StaticResource combo}" />
</DataTrigger>
to pick a DataTemplate:
<DataTemplate x:Key="combo" > <W3V:ComboView /> </DataTemplate>
which instatiates the following control:
<ComboBox Style="{StaticResource ComboButtonStyle}" Width="200"
Text="{Binding Path=., ## PROBLEM, BUT HOW TO AVOID?
Converter={StaticResource valueFormattingConverter },
IsEditable="True" />
valueFormattingConverter uses the #units, #type, and #value attributes to produce properly formatted text. The trouble is, this doesn't convert back. I asked here: TextBox ConvertBack event doesn't fire for XML element, and learned that it is impossible for Path=. to be used as '.' is an object but not a dependency property.
So then, I need a way to provide a DependencyProperty for Binding. So ComboView needs to receive an object that has a property which is or has the XmlElement I want. I think this means I really need to change things around but haven't the slightest idea how. Maybe there's an MVVM approach to doing this??? Any insights will be appreciated.
An upcoming issue is a need to validate the data typed in and process the information.
The MVVM method: pull the data out of the XML file, and put it a class which is the ViewModel. Each property in the ViewModel corresponds to an item of data in the XML file. Then use a custom DataTemplate to render the contents of the ViewModel to the screen.
We can extend this to render a list of items. Each item in the list is a ViewModel. A DataTemplate always renders based on the type of the property it is attached to. So you can have a list of objects, and a custom DataTemplate for each item in the list. Of course, all of the items in the list would have to inherit from the same type.
This means you can have a list of items, and each item can render differently depending on the type of data in the XML file. This means that each item in the list can have a custom look and feel with different number of decimal places, colors, etc.
I've used this technique before, and it works well.
Update
For examples, see:
http://www.wpftutorial.net/datatemplates.html
http://www.stackoverflow.com/questions/3400532/display-multiple-types-from-a-single-list-in-a-wpf-listbox.
If I was solving this, I would look at generating C# on the fly. Here is how I would do it:
All XAML compiles to a series of C# commands.
I would set the XAML to format things the way I like it.
I would find the equivalent C# code.
I would then insert the appropriate if/else statements to alter the C# to suit.
Another method that I would try:
Its possible to render custom XAML into an area on the screen.
I would edit the XAML, based on the XML, then display this custom XAML on the screen.
I'd be curious to know if either of these methods work in practice, or if there is a better method that would work.
I had the following XAML looking just as I want it to look. It's basically a dropdown menu with checkboxes and some text to them.
<ComboBox x:Name="comboBox">
<ComboBoxItem><CheckBox>WeeHee</CheckBox></ComboBoxItem>
<ComboBoxItem><CheckBox>BuuHuu</CheckBox></ComboBoxItem>
</ComboBox>
Then I learned that the options are possible to be dynamically adapted so I changed the markup and bound the combobox to an array. That works perfectly well functionality-wise but now I get only the names of the options, while the checkboxes are gone.
<ComboBox x:Name="comboBox"
ItemsSource="{x:Static local:MainWindow.AllKinds}">
</ComboBox>
So I'm looking for a way to create a custom item for populating the combobox but still so that the data is dynamically bound. I'm assuming WPF/XAML supports such functionality and I'm just too ignorant to know what to google for.
So my request is a rudimentary example or suggestion on what to google for. (Might be something seemingly obvious to everybody but me.)
So with most of the controls that offer up things from ItemSource there's a nifty built in feature that allows you to specify how each item should look/behave etc. So you can specify each Item in your ComboBox's by setting your own ItemTemplate.
Since a ComboBox just uses an ItemsControl to list its items, you can utilize this via ComboBox.ItemTemplate to make your items however you like, in this case probably something like (in pseudo).
<ComboBox>
<ComboBox.ItemTemplate>
<DataTemplate>
<CheckBox>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Hope this helps, its some pretty handy info to know for when you want to start customizing Item generated lists between wpf, silverlight, wp, win-rt, etc. Cheers!
I'm using a telerik RadGridView which is pretty much the same thing as a normal DataGrid in WPF. In my gridview.columns I have a GridViewDataColumn which then allows me to put a celltemplate then a datatemplate and then allow me to put different controls within a grid. I have a combobox and a textbox(only one shows at a time based on visibility property). The problem I'm having is the tab system is kind of weird and doesn't work right. When I tab to a cell in the column above, my combobox nor my textbox ever gets focus. In fact the cell turns completly white. So I was wondering how (in code behind) can I detect when a user tabs in this particular cell and manually set focus to these child elements inside this cell on the selected row?
<telerik:GridViewDataColumn x:Name="MyDataColumn" Focusable="True" GotFocus="MyDataColumn_GotFocus_1" Header="Header1" Width="250">
<telerik:GridViewColumn.CellTemplate>
<DataTemplate>
<Grid>
<Textbox x:name="MyTextbox" Visibility="{Binding IsTextbox}"/>
<Combobox x:name="MyCombobox" Visibility="{Binding IsCombo}"/>
</Grid>
</DataTemplate>
</telerik:GridViewColumn.CellTemplate>
</telerik:GridViewDataColumn>
*Basically, how can I gain access to one of those child controls inside this GridViewDataColumn in code behind so that I can set focus to it? Thanks so much for any advice.
Probably the most straight forward answer to your question can be found by reading the answer to the Access items inside the DataTemplate in WPF post.
However, it may be worth reading the correct answer in this Access Elements inside a DataTemplate… How to for more than 1 DataTemplate? post also.
We have a WPF application with a standard MVVM pattern, leveraging Cinch (and therefore MefedMVVM) for View -> ViewModel resolution. This works well, and I can bind the relevant controls to properties on the ViewModel.
Within a particular View, we have an Infragistics XamGrid. This grid is bound to an ObservableCollection on the ViewModel, and displays the appropriate rows. However, I then have a specific column on this grid which I am trying to bind a TextBox text value to a property on the parent DataContext, rather than the ObservableCollection. This binding is failing.
We've gone through several options here including:
Using AncestorType to track up the tree and bind to the DataContext of the parent UserControl like so (from the great answer to this question, as well as this one)...
{Binding Path=PathToProperty, RelativeSource={RelativeSource AncestorType={x:Type typeOfAncestor}}}
Specifying the ElementName and trying to target the top level control directly. Have a look here if you'd like to read about using ElementName.
Using a 'proxy' FrameorkElement defined in the resources for the UserControl to try and 'pass in' the context as required. We define the element as below, then reference as a static resource...
<FrameworkElement x:Key="ProxyContext" DataContext="{Binding Path=DataContext, RelativeSource={RelativeSource Self}}"></FrameworkElement>
In this case the binding finds the FrameworkElement, but can not access anything beyond that (when specifying a Path).
Having read around, it looks quite likely that this is caused by the Infragistics XamGrid building columns outside of the tree. However, even if this is the case, at least options 2 or 3 should work.
Our last thoughts are that it is related to the V - VM binding, but even using Snoop we've yet to find what the exact issue is. I'm by no means an expert with WPF binding so any pointers would be appreciated.
EDIT: I have found some templating examples from Infragistics here that I will try.
EDIT 2: As pointed out by #Dtex, templates are the way to go. Here is the relevant snippet for use with a XamGrid:
<ig:GroupColumn Key="CurrentDate">
<ig:GroupColumn.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DataContext.CurrentDateTest, RelativeSource={RelativeSource AncestorType=UserControl}}" />
</DataTemplate>
</ig:GroupColumn.HeaderTemplate>
<ig:GroupColumn.Columns>
I've left the XML open... you'd simply add the columns you wanted, then close off the relevant tags.
I dont know about XamGrid but that's what i'll do with a standard wpf DataGrid:
<DataGrid>
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding DataContext.MyProperty, RelativeSource={RelativeSource AncestorType=MyUserControl}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding DataContext.MyProperty, RelativeSource={RelativeSource AncestorType=MyUserControl}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Since the TextBlock and the TextBox specified in the cell templates will be part of the visual tree, you can walk up and find whatever control you need.
Because of things like this, as a general rule of thumb, I try to avoid as much XAML "trickery" as possible and keep the XAML as dumb and simple as possible and do the rest in the ViewModel (or attached properties or IValueConverters etc. if really necessary).
If possible I would give the ViewModel of the current DataContext a reference (i.e. property) to the relevant parent ViewModel
public class ThisViewModel : ViewModelBase
{
TypeOfAncestorViewModel Parent { get; set; }
}
and bind against that directly instead.
<TextBox Text="{Binding Parent}" />
Here's the problem: I have a data-bound list of items, basically a way for users to map a request to a response. The response is an xml-based file. I'm letting them queue these up, so I've used a combobox for responses. The responses will include the full path, so they get a bit long. I want the displayed text of the combobox to be right-justified so the user can see the file name. For my static controls, I just use ScrollToHorizontalOffset() when a file is loaded and I'm done. For this dynamic list, I'd like to do it in xaml.
The "somewhat ugly" solution would be to store all the ComboBox objects as they load... then I can call ScrollToHorizontalOffset() directly, but I'd really prefer to do it a cleaner way than that! EDIT: (Actually, this may not be reasonable. A quick look at trying to hack around this issue gets into some really awkward situations trying to map my datasource items to the controls)
I've tried HorizontalContentAlignment, that only impacts the "dropped-down" portion of the ComboBox.
I've also tried to hook other various loading events, but haven't found one that works.
Using an Item template you can decide what will be shown.
You can set tooltip. You can then also use converters to add the dots.
<ComboBox x:Name="ConfigurationComboBox" VerticalContentAlignment="Center" ToolTip="saved configuration" SelectionChanged="ConfigurationComboBox_SelectionChanged">
<ComboBox.ItemTemplate>
<DataTemplate >
<StackPanel>
<TextBlock Text="{Binding}" ToolTip="{Binding Path}"></TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
To measure the text, see Measuring text in WPF.