I have a single property that consists of three letters and two numbers, such as "ABC 12". The requirements for my project ask that the UI divide this into a ComboBox for the three letter combinations and a TextBox for the numerical. I can do this pretty easily using a converters to parse out the part of the string that I need.
My question is whether there is a way to implement the "ConvertBack" logic in my converter such that I can reset the property based upon the values in the two different controls.
My xaml:
<ComboBox IsEnabled="{Binding EditMode}" ItemsSource="{Binding AbbrevsList}"
DisplayMemberPath="SelectedAbbrev" SelectedValuePath="Abbrev"
SelectedValue="{Binding Row.Code, Converter=CodeAlphaConverter,
UpdateSourceTrigger=PropertyChanged}" />
<TextBox TextAlignment="Left">
<TextBox.Text>
<Binding Path="Row.Code" Converter="CodeNumericConverter"
UpdateSourceTrigger="LostFocus">
</Binding>
</TextBox.Text>
</TextBox>
Thanks.
Since WPF does not allow you to bind converter parameters, you may have to switch your approach. Instead of using converters, other answers on SO point to changing your View Model to have the logic built into two properties and binding to those or, instead of passing in the individual value to the converter, you pass in the entire object.
Related
I have a datagrid where the ItemsSource is set in code-behind for example:
var grid = grdEmploy as DataGrid;
grid.ItemsSource = employments; // list of objects
In this grid, I have several drop downs being used when editing the row. The options are currently held in a local CollectionViewSource, for example:
<CollectionViewSource x:Key="StatusList" CollectionViewType="ListCollectionView"/>
And set when the window is loaded like so:
var statusList= Functions.GetStatuses(); // returns a List<>
CollectionViewSource itemCollectionViewSource;
itemCollectionViewSource = (CollectionViewSource)(FindResource("StatusList"));
itemCollectionViewSource.Source = statusList;
Then the binding of the column for the grid would look like so:
<DataGridTemplateColumn Header="Employment Status" HeaderStyle="{StaticResource WrappedColumnHeaderStyle}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock>
<TextBlock.Text>
<MultiBinding>
<MultiBinding.Converter>
<local:AimTypeConverter />
</MultiBinding.Converter>
<Binding Path="EmpStat" />
<Binding Path="SourceCollection" Source="{StaticResource StatusList}" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox SelectedValue="{Binding EmpStat}" SelectedValuePath="Value" DisplayMemberPath="Text" ItemsSource="{Binding Source={StaticResource StatusList}}"></ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
This all works great however I have hit a snag whereby one of the columns needs to show different options based on another column. For example if Column A is "1" show Options 2,3, if "2", show Options 3,4 etc.
My thoughts were to load all options into the local list and somehow filter them but I'm not sure how best to do this, any help on this would be appreciated.
The way to solve this using the MVVM pattern would be to define a collection property in the Employee class, or whatever you call it, and then return an already filtered collection from this property based on the value of the property bound to "Column A".
I am afraid it doesn't make much sense to define a single source collection in the code-behind if you want to bind to several source collections, filtered or not, in the DataGrid. I would recommend you to put your filtering logic in the view model.
I'm using C# in a WPF application with MVVM (with Caliburn Micro framework). I'm trying to bind 2 elements (one TextBlock and one TextBox) to the same property, that resides in my model view. My property is called FirstName.
I have two options to do the binding: Binding Path=FirstName or x:Name=FirstName. When I edit the textbox, I see the changes in the textblock only if I bind in a certain way (see code). Any idea of why the other way does not work? (when I type in the textbox I don't see my textblock updates)
I've tried different mode options (two ways, one way, etc). The NotifyOfPropertyChange seems to be working.
<!-- This works -->
<TextBlock Text="{Binding Path=FirstName}"/>
<TextBox x:Name="FirstName"/>
<!-- This does not work -->
<TextBlock x:Name="FirstName"/>
<TextBox Text="{Binding Path=FirstName, Mode=TwoWay}"/>
With your second example, you need to specify UpdateSourceTrigger=PropertyChanged:
<TextBlock x:Name="FirstName"/>
<TextBox Text="{Binding Path=FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
Otherwise, the source is only updated when the TextBox loses focus.
<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'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.
Consider the following scenario:
I have a ListView that is bound to an ObservableCollection using the DataContext:
<ListView ItemsSource="{Binding}">
The class containing the string data uses the DependencyProperty mechanism to keep the displayed content synced with the data collection.
The ListView has one column that is editable (I followed the tutorial here to achieve this); the ListViewItem is then either a TextBlock or a TextBox. This is done using a DataTemplate and two Style resources.
I'd like to format the string displayed in the TextBlock based on a search string. Specifically, I'd like to format the items of the ListView to become bold as the user types in their search query if there is a match (only the characters that match in sequence should be made bold). This only needs to be displayed for the text currently being rendered using the TextBlock (that is, text not currently being edited).
I've considered using an IMultiValueConverter that takes in a reference to the TextBlock that renders the data so that I can format the text appropriately. However, this will destroy the binding that I've set up:
<TextBlock.Text>
<MultiBinding Converter="{StaticResource searchFormatter}" ConverterParameter="{x:Reference Name=txtSearch}">
<MultiBinding.Bindings>
<Binding Path="NameOfBoundDependencyProperty"/>
<Binding RelativeSource="{RelativeSource Self}"/>
</MultiBinding.Bindings>
</MultiBinding>
</TextBlock.Text>
searchFormatter being the IMultiValueConverter and txtSearch being the TextBox containing the search query.
I'm still learning WPF so I'm not familiar with the best approach or to what's possible. Is there a way to keep the data bound (so that edits reflect in the collection and the ListView) and still represent the data differently to the user (so that search matches may be bold)? Perhaps it would be cleaner if I manage the binding manually?
I decided to use a Control that supports HTML so that I could use an IValueConverter to update the value of the displayed text on the fly without affecting any active bindings. I used the code from here and modified it so that it looked like a TextBlock within my ListView:
BorderBrush = Brushes.Transparent;
SelectionBrush = Brushes.Transparent;
Cursor = Cursors.Arrow;
BorderThickness = new Thickness(0);
Background = Brushes.Transparent;
However, I still needed to trigger the IValueConverter so that the display is updated as the user types in their search query (code from here):
ICollectionView view = CollectionViewSource.GetDefaultView(ItemsSource);
view.Refresh();
I didn't want to slow down the search process so I only forced this refresh if there was actually a match (or if the state of having a match moves to no match). My IValueConvertor simply inserted the bold tags to match the search query:
<RichTextBox Text="{Binding Path=DisplayItem, Converter={StaticResource searchFormatter}, ConverterParameter={x:Reference txtSearch}}"/>
Where searchFormatter this time is an IValueConvertor.