I am currently stuck at a dead end with the following situation:
I have a List<Category> collection with each Category containing an Id, Name and a List<string[]> property called Subcategory (where array contains the Name and Id of that subcagtegory - I didn't feel like creating another class).
I have a TreeView with HierarchicalDataTemplate bound properly to categories and subcategories. I also have check boxes next to the tree node and I bind IsChecked to a MultiBinding of the Id of the sub/category and the an overall list of sub/categories that should be checked-off.
<CheckBox.IsChecked>
<MultiBinding Converter="{StaticResource IsCategoryChecked}">
<Binding Path="Id"/>
<Binding Path="myDataSet.Tables[Categories]/cat_subcat"/>
</MultiBinding>
</CheckBox.IsChecked>
The converter I use simply checks if the name of the sub/category I provide is part of the list (both are values I'm binding to). This works fine and overall I am quite happy with the result.
The problem comes with converting the check/uncheck back. When user changes the IsChecked value I must either add the Id of the sub/category to the list of all checked-off categories or remove that Id from it. Unfortunately when I've attempted to implement the ConvertBack() method for IMultiValueConverter, I only have access to the ACTUAL value that changed (the true or false value of IsChecked) and have NO access to the Id of the sub/category that THAT SPECIFIC CHECKBOX is bound to.
SOLUTIONS I'VE TRIED but failed miserably:
Saving the sub/category name when I do the initial conversion is not an option since I'm using the same Converter for ALL the sub/categories and thus I'd only save the last sub/category I've tried to convert.
Using a MouseUp/KeyUp event is useless because (for whatever reason) they fail to be triggered (a bug?).
I am contemplating to ignore the ConvertBack() and use Checked/Unchecked events instead but feel like that is a rather "dirty" solution because I might just as well ignore the bindings altogether! Is this the only way to go? I've ran out of options but still hope for a "good" solution, if it's out there!
You have a couple of options, and I've used both in production:
Overload Checked/Unchecked, as you've said, which gives you the most control over the situation
Add an IsCategoryChecked property to your strongly typed table in the code behind which handles the changes required behind the scenes, and you bind to the IsCategoryChecked property rather than using the converter
The second one works fairly well thanks to the partial classing with strongly typed datasets. However, it still isn't "seamless".
Related
OK, once again, my Google-Fu isn't up to par and I would really appreciate a little guidance here.
I have a WPF app with multiple pages, one of which contains a TabControl, of which one of the tabs contains a grid, in which one of the columns contains a StackPanel with just two items on it: a Label and a TreeView. I have no need to update the TreeView once the content is obtained. The app uses MVVMLight (great toolkit!) to expose the data and said data is exposed in an mvvm (observable) property as it should be. I have checked and the data itself is available in correct form once I get to the point of setting the ItemsSource property so I know it's not the lack of data or the structure of the data itself. I have looked at all the entries on the web (at least the first 4 pages worth) matching the search terms "wpf treeview dictionary" and all articles come close, but don't get me to where I need to be. I'm missing something and what's worse, IntelliSense is even "helping" by providing the correct values for the xaml settings. So I know I'm close, but after two days of frustration, I'm throwing in the towel and asking for an assist.
So...to the meat of the problem: The data that the TreeView needs to display is in the form of SortedDictionary<string, List<ServerEntityNameMsSqlSvr>>. A ServerEntityNameMsSqlSvr class contains multiple properties, one of which is FullName. What I want the TreeView to display is the dictionary Key as the parent node and the FullName from each of the items in the List<ServerEntityNameMsSqlSvr>. You'd think that'd be simple, no? Not for me, the closest I can get is to display the Key of the dictionary, but either nothing for the children (best case) or throw an exception that stops the app with a null exception (worst case).
Here is the xaml I'm using (worst case):
<TreeView Grid.Column="0" ItemsSource="{Binding TableHierarchy}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Path=Value}">
<TextBlock Text="{Binding Path=Key}"/>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate DataType="awe:ServerEntityNameMsSqlSvr">
<TextBlock Text="{Binding FullName}"/>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
"TableHierarchy" is the MVVM property that exposes the data, it is declared as SortedDictionary<string, List<ServerEntityNameMsSqlSvr>>. The DataType "awe:ServerEntityNameMsSqlSvr" is a simple class with a few methods and properties, nothing special. One layer of inheritance, no interfaces. Of the properties that are available, I want to expose just the FullName, which is declared as public string FullName => _FullName(); Yep, it calls an overloaded method to build the full name but the result is a simple string (and the method call happens when the data is built, not a display time, iow, the values are already there, at least debugging to the setter shows that the content is correct.
Some of the solutions that I have researched suggest that the data type be changed to something other than a dictionary. In this case, that's not possible and given that the lists are, on occasion, quite large, I don't want to rebuild it into something else. This needs to work with the sorted dictionary as declared.
The xaml shown above is indeed correct, however, the gadget that supports the data (the methods in the ServerEntityNameMsSqlServer class) all need to not throw exceptions under any circumstances. In this case, one of the methods not directly involved with the author's code but used somewhere else in the framework (an overloaded call to "Equals" that was constructed to check individual property equality to determine the result) was throwing a null exception because a property wasn't filled in for the particular use case.
Difficult to find, but that was the cause.
While using a converter, I want to bind to the entire object and send it to the converter to use, rather than binding to each of the individual properties, so I can do this:
Order thisOrder = (Order)values[0];
(note, its a multibinding, other values are being sent in besides the thisOrder object. Now the Order class implements the INotifyPropertyChanged interface, and raised the appropriate events when any of its properties are changed. The converter needs access to multiple properties of the Order, although it actually only needs to rerun on changes to one of the properties. Is there any way to either bind to the entire object so that if any of the properties change, the converter is evaluated again, or bind to only one property, but have the rest of the object also available as a parameter for the converter to work on?
Example Details:
Order has following fields:
ID
MarketPrice
Name
When MarketPrice changes, the converter needs to recalculate based on
ID and MarketPrice.
Presently, the binding is applied on the DataRecordCellArea of a xamDataGrid, as follows:
<MultiBinding Converter="{StaticResource MarketPositionToBrushConverter}">
<Binding RelativeSource="{RelativeSource Self}" Path="Record.DataItem"/>
...
</MultiBinding>
As far as i know there currently is no clean solution to this. I would suggest you add two bindings, one to the Order object so you have access to all the values and another to the MarketPrice so that the binding updates if that property changes. You then have access to the object and all properties via the first binding and you get the respective updates.
The is another method which is quite a hack, you can subscribe to PropertyChanged in the class owning the Order instance and then fire a change notification for the Order instance property whenever the MarketPrice in the instance changes. I would avoid doing this but it would allow you to get rid of the second binding.
I need to convert a two part value into a string and back again for example:
{Value = 12.0, Units = DimensionUnits.Inches}
Converts to
"12 in"
This is pretty simple using a multivalue converter to convert from source but becomes impossible to convert back if the user doesn't provide the unit type in the string so a Multivalue converter doesn't look like the solution I need.
A direct converter parameter won't work because the unit type needs to be bound so I researched how to create a bindable parameter. Creating a bindable parameter is actually pretty easy - deriving from DependencyObject - but then you have the problem of your converter not living in the visual tree - and thus not being able to bind to anything - to which there are 2 solutions:
http://tomlev2.wordpress.com/2011/03/21/wpf-how-to-bind-to-data-when-the-datacontext-is-not-inherited/
http://shujaatsiddiqi.blogspot.com/2011/02/wpf-binding-converter-parameter.html
The first method derives your Converter from Freezable instead of DependencyObject to allow DependencyProperties. This works and allows you to bind within the Whatever.Resources section but it has extremely odd behavior like only listening to the binding the first time it is used in your entire application.
The second method doesn't seem to work at all. The dependency property is never updated when the source changes.
<pf:BindingReflector Target="{Binding Source={StaticResource DistanceConverter}, Path=Units, Mode=TwoWay}"
Source="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=g:TestWindow, AncestorLevel=1}, Path=Units, Mode=TwoWay}"/>
Does anyone know of a solution to this problem or is this a big limitation of WPF?
Personally, I would actually suggest rethinking this a bit, and doing either:
Keep the final string one-way using a IMultiValueConverter, and have this be input as two separate items. This seems like a reasonable approach, as the units appear to be an enum with a specific set of options. A combo box for units and textbox for amount seems appropriate, and the total display can be done with a one-way converter.
Handle this conversion explicitly in the ViewModel. This has the advantage of allowing much better validation handling, which is probably going to be required as entering two separate values (amount + units) in one control is likely to not validate correctly. By moving the logic directly into your ViewModel instead of binding to the properties, you can correctly handle errors in a clean way.
I am trying to develop a filtering functionality for WPF DataGrid (from the WPF Toolkit). I want a user to right-click any cell and select Filter from its CcontextMenu, and then the grid should be filtered by the cell's value.
I am trying the M-V-VM pattern. My windows's datacontext is MainWindowViewModel which has a property Transactions. This property returns ObservableCollection<TransactionViewModel>, and the data grid uses this collection as its items source. So basically each row is bounded to TransactionViewModel (as you can guess, this grid lists transactions). MainWindowsViewModel has ICollectionView which is used for filtering and tracking the currently selected row. The DataGrid has its property IsSynchronizedWithCurrentItem set to "true", so myCollectionView.CurrentItem gives me the currently selected TransactionViewModel.
The only thing I still need to know is by which column I need to filter. This depends on where the user clicked the context menu. So I am trying to pass this information using CommandProperty of the context menu item. And here I have a real problem. I tried this:
CommandParameter="{Binding Column.Binding.Path.Path,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type tk:DataGridCell}}}" />
This is really ugly, but this works for DataGridTextColumns. Unfortunately, I have also DataGridTemplateColumns, and they don't work (the path is different there, because I need to reach the actual cell template)...
So how can I implement this functionality? Perhaps the whole way is wrong? I didn't find any valuable example on that. The only thing I found is the WPF DataGrid autofilter implementation on the Codeproject which doesn't work at all for some reason...
Thank you.
I'm not 100% sure if this would help but...
DataGrid has CurrentCell so you could bind it in TwoWay mode in your MainWindowViewModel.
Then every "row" could point to DoFilter command defined in MainWindowViewModel. It's not a beauty solution (because viewmodel has to know DataGrid Cell type) but it should work.
Why not just pass the cell as a parameter like this:
CommandParameter=
"{Binding RelativeSource={RelativeSource FindAncestor,tk:DataGridCell,1}}" />
and let your command's Executed event handle all the hard part of finding the actual column name? That way you can write all the special-case code you need.
Is there an easy way to tell a Two-way WPF data binding to wait a few milliseconds after the last change before updating the Source with the new property value?
I'm implementing a filter feature for a ListBox where I have a textbox, and I want to filter the content of the ListBox according to what I type. I'm using data binding to connect the pieces together. Filtering the list can be quite time consuming, so I don't want to do it after every character that is typed: hence my request.
I have been using Paul Stovell's DelayBinding Extension (his site's down at the moment, so I can't link to it). However, I suspect that it is the cause of a memory leak in my applicataion (caused by it not removing event handlers).
Does anybody else have any other ideas?
I'm also a few years late, but if you're using WPF 4.5+ there is now an property exactly for this purpose, it's called Delay.
Description
The amount of time, in milliseconds, to wait before updating the
binding source.
Example usage
<TextBlock Text="{Binding Name, Delay=500}"/>
A little late to the question here (just a few years :) but for anyone who's interested I had a similar requirement in a project so I created two markup extensions called DelayBindingExtension and DelayMultiBindingExtension.
They work like normal Bindings with the addition that you can specify UpdateSourceDelay and/or UpdateTargetDelay, both of which are TimeSpan properties. Also, I've verified that it's leak-free (it makes use of the propertychanged callback of a dependency property binding through inheritance context rather than the DependencyPropertyDescriptor).
Example usage for a DelayBinding
<TextBox Text="{db:DelayBinding Path=TextProperty,
UpdateSourceTrigger=PropertyChanged,
UpdateSourceDelay='00:00:01'}"/>
And for a DelayMultiBinding
<cs:ColorSelector.SelectedColor>
<db:DelayMultiBinding Mode="TwoWay"
Converter="{StaticResource ColorConverter}"
UpdateSourceDelay="00:00:02"
UpdateTargetDelay="00:00:01">
<Binding Path="Red" />
<Binding Path="Green" />
<Binding Path="Blue" />
</db:DelayMultiBinding>
</cs:ColorSelector.SelectedColor>
Source code and sample usage for DelayBinding and DelayMultiBinding can be downloaded here.
If you're interested in the implementation details, you can check out my blog post about it here: DelayBinding and DelayMultiBinding with Source and Target delay
First, to answer your question, I would add the UpdateSourceTrigger binding extension which will let you control when the binding updates. Try LostFocus first but it sounds like you might want to go for Explicit.
Second, if your filtering takes a long time I would look into using CollectionViewSource on your ListBox. Bea Stollnitz has a good primer on it here and I used this blog post to show me how to filter. When I changed over I noticed a huge speed difference over my other implementation even though they use the same filtering functions. Also CollectionViewSource will automatically handle updating filtered items if the list you're bound to changes, even on the item level if you're binding to an ObservableCollection (this is the original reason I changed to CollectionViewSource).
HTH