Two way Multiconverter or Converter with property - wpf

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.

Related

wpf binding a TreeView to a SortedDictionary<string, List<Class>>

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.

Current binding value

I'm writing markup extension. I have XAML like this
<TextBlock Text="{ui:Test SomeInfo}" />
and TestExtension with constructor taking one string argument. I'm getting "SomeInfo" string so everything is find. Now I want to nest extensions and write something like
<TextBlock Text="{ui:Test {Binding PropName}}" />
and it does not work as is. I had to add constructor which takes one argument of System.Windows.Data.Binding type.
Now I need to know
How should I retrieve a current value from the Binding object?
When should I do this? Should I subscribe to changes some way or ask for that value every time in ProvideValue method?
Update1 PropName should be resolved against DataContext of TextBlock.
Update2 Just found related question: How do I resolve the value of a databinding?
Bindings like this will not work because your MarkupExtension has no DataContext and it does not appear in the visual tree and i do not think you are supposed to interact with binding objects directly. Do you really need this extension? Maybe you could make do with the binding alone and a converter?
If not you could create a dedicated class which has bindable properties (by inheriting from DependencyObject), this however would still not give you a DataContext or namescope needed for ElementName or a visual tree needed for RelativeSource, so the only way to make a binding work in that situation is by using a Source (e.g. set it to a StaticResource). This is hardly ideal.
Also note that if you do not directly set a binding the ProvideValue method will only be called once, this means that even if you have a binding in your extension it may not prove very useful (with some exceptions, e.g. when returning complex content, like e.g. an ItemsControl which uses the binding, but you set the extension on TextBlock.Text which is just a string), so i really doubt that you want to use a MarkupExtension like this if the value should change dynamically based on the binding. As noted earlier: Consider converters or MultiBindings for various values instead.

When to use Path in WPF Binding?

I've seen a lot of WPF Binding examples and have used the feature in a lot of different places in learning MVVM, but something that has seemed quite inconsistent to me is when you specify "Path=" in the binding string as appose to simply typing in the property you want to bind to. For example, what's the functional difference between the following XAML attributes:
DataMemberBinding="{Binding SomeProperty}"
DataMemberBinding="{Binding Path=SomeProperty}"
There is no functional difference. The default property of the Binding object is Path, this means if you don't specify which property you are setting then you will set Path.
This is because the Binding object has two constructors, one default and one that takes in a single string parameter. When you pass in a value without labeling it that property will be forwarded onto the matching constructor, in the case of Binding this sets the path. It is very similar in concept to the way attributes work, a call to the constructor followed by optional parameters, for example:
[AttributeUsage(AttributeTargets.Class), AllowMultiple = false, Inherited = false ]
Probably way beyond what you're actually asking the question for, but I've noticed that there is a slight (and probably inconsequential) difference between the two. Since I can't explain it myself I've started a new question about it here.

Bind TextBox to large string in WPF using MVVM

I am having a performance problem binding a large string to a a TextBox in WPF.
In the view I am binding a TextBox's Text property to the view model's Output property which is a StringBuilder.
View:
<TextBox Text="{Binding Output, Mode=OneWay}" IsReadOnly="True"/>
ViewModel:
public StringBuilder Output
{
get { return _output; }
}
As the text in the StringBuilder gets larger the performance of the binding degrades.
What's a better way to do this using MVVM?
One possible way of getting around delays in databinding is to use asynchrnous binding. You can do this by setting IsAsync property of your binding object :
This will of course not solve the problem of the binding taking a long time but will stop the UI from freezing whilst it does the binding.
You can also use priority binding to show a cut down version of the text (which is quick to load) whilst the larger text item is loaded. Priority binding is described on msdn - >http://msdn.microsoft.com/en-us/library/ms753174.aspx.
I can't really imagine why the performance of the binding would be slow because it is simply displaying what's in the StringBuilder. However, the first thing that comes to my mind is how you're adding to the StringBuilder. Appending, removing, replacing, or inserting characters into the StringBuilder may be what's giving you performance issues.
I don't really know what kind of string you're building or what the requirements are, but you may need to use a different structure.

WPF Binding Converters

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".

Resources