I am converting a WPF to UWP and it uses a number of IMultiValueConverters. I can't find any reference for IMultiValueConverter. Is it available in UWP? If not, if there an alternative?
There is no multiple converter in UWP.
Without Multiple Converter, you can bind to a single property in the ViewModel.
1 That single property should take into account multiple properties from the view model.
Let's call them source properties
2 If any any change is made to a source property, it should raise a PropertyChanged event on the single property.
Regards
There is a very good alternative for this. In the Cimbalino toolkit ( open source on github ) there is a MultiBind behaviour that has been ported to UWP
Details: https://www.pedrolamas.com/2013/05/17/cimbalino-windows-phone-toolkit-multibindingbehavior/
Toolkit code: https://github.com/Cimbalino/Cimbalino-Toolkit
It's also available as nuget, I personally create every UWP app with it, because it has so many great features
A very simple method for binding to multiple values is to use x:Bind with a function. For example,
<TextBlock Text="{x:Bind local:Helper.FormatName(Id, Name)}" />
or
<TextBlock Text="{x:Bind local:Helper.FormatTotal(ViewModel.SelectedWidget.Cost, ViewModel.Quantity, ViewModel.Bundles)}" />
In both of these examples the function is a static function, but the function can also be defined in the code behind or as part of the view model.
<TextBlock Text="{x:Bind CalculateResult(ViewModel.Widget, ViewModel.Foo)}" />
<TextBlock Text="{x:Bind ViewModel.FormatResult(ViewModel.Widget, ViewModel.Foo)}" />
Rather than passing individual values, you can pass a single object that contains multiple properties.
<TextBlock Text="{x:Bind local:Helper.FormatResult(ViewModel.Widget)}" />
Function binding is very flexible and can often be used in place of a converter, creating simpler, more readable code.
<TextBlock Background="{x:Bind local:Helper.GetColor(ViewModel.Widget)}" />
Function binding can also be used with two way bindings by adding a BindBack function.
<TextBox Text="{x:Bind ViewModel.FormatWidget(ViewModel.Widget), BindBack=ViewModel.UpdateWidget}" />
Related
I am learning on to the concepts of WPF such as data binding, commands, resources, element bindings, styles etc, which use markup extensions extensively, and i am having problem understanding the meaning behind the Markup classes, as they are being used beyond what i understand they should be used for. So here are a few points i need to clear:
(all code snippets are from Pro WPF in C# 2010 book)
What is the meaning and use of Static extension? It can be used to
declare static resources, which can be declared in as
, but this xaml confuses me:
<Button ... Foreground="{x:Static SystemColors.ActiveCaptionBrush}" >
In {x:Static SystemColors.ActiveCaptionBrush}, what is the role
of static here, and what will change if i use x:Dynamic here? The
book says this xaml is equivalent to this codebehind:
cmdAnswer.Foreground = SystemColors.ActiveCaptionBrush;
This means that if i have a class with static properties, i should
be able to use something like this:
<Button ... Foreground="{x:Static MyClass.SomeStaticProperty}" >
But it didn't work, despite i had created a class, i tried using
local:Static (referring to the local namespace) but VisualStudio
didn't allow me to use it. What is the proper method of achieving
this?
What is the meaning of Binding (beyond obvious meaning, what is
happening when i am binding)? It is used for resource binding, or
data or element binding. I was able to understand element binding,
but binding to objects that are not elements caused problems. For
example:
<TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily},
Path=Source}"></TextBlock>
Here it is binding to the text to the SystemFonts.IconFontFamily
property, what is the use of x:static in this case, and how to bind
it to a property in a class that i have created? Also how to update
the text property of the textfield if the value of the binding
target changes? Due to binding, it should update by itself, is this
the case?
All the examples in the book make use of SystemFonts.IconFontFamily,
none that i have seen explains the use of such binding, and how to
do it for the classes that i create? Some help in this regard is
needed. I want to ask more about binding, but i will do so in a
separate question about binding only.
Finally, recommend a book or resource that explains what is
happening, instead of how to do this and that?
Answers....
1)
You said ...
... This means that if i have a class with static properties, i should be
able to use something like this:
<Button ... Foreground="{x:Static MyClass.SomeStaticProperty}" >
But it didn't work, despite i had created a class, i tried using
local:Static (referring to the local namespace) but VisualStudio
didn't allow me to use it. What is the proper method of achieving
this?
Well your trial attempt was correct but it was incorrect to what term you have applied that namespace token to.... local namespace token applies to the class that is declared under it so...
<Button ... Foreground="{x:Static local:MyClass.SomeStaticProperty}" >
Should work just fine provided that SomeStaticProperty is a valid Brush.
In this example, the whole markup was internally equivalent to Binding as ...
Binding.Source = {x:Type local:MyClass}
Binding.Path = SomeStaticProperty.
2)
You had an example...
<TextBlock Text="{Binding Source={x:Static SystemFonts.IconFontFamily},
Path=Source}">
</TextBlock>
So use the same equivalence from example 1 and apply it to this example...
<TextBlock Text="{Binding Source={x:Type SystemFonts},
Path=IconFontFamily.Source}">
</TextBlock>
3)
I learned this whole thing from MSDN... I dont think we can have any other legitimate source than that.
I have an ObservableCollection of addresses that I am binding to a ListBox. Then in the ItemTemplate I am Binding to the current address record using {Binding .}. This results in my addresses displaying using their ToString method which I have setup to format the address. All is good, except if I update properties on an individual address record the list in the UI does not update. Adds/Deletes to the list do update the UI (using the ObservableCollection behavior). If I bind directly to properties on the address the UI does update (using the INotifyPropertyChanged behavior of the Address object).
My question is, is there a way to notify the UI of the change to the object as a whole so that I can still use this syntax or do I need to punt and put a DisplayText property on my address type that calls the ToString method and bind to that? FYI, this is an MVVM architecture so I don't have the luxury of calling Refresh on the ListBox directly.
Thanks for any help/ideas.
<ListBox x:Name="AddressList" ItemsSource="{Binding Addresses}" Background="Transparent" BorderBrush="Transparent"
Width="200" HorizontalAlignment="Left">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding .}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
When you bind to the Address object itself, the object itself -- that is, its identity -- doesn't change, even though its properties do. WPF therefore doesn't know to refresh the binding in this case.
So yes, you need to bind to a notifying property (or properties) rather than the whole object. As you say, one way to do this is to create a DisplayText property, and raise the PropertyChanged event for that property whenever something that affects the display text changes. Another is to use multiple TextBlocks in a horizontally oriented StackPanel, each bound to a particular property e.g.
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding HouseNumber}" />
<TextBlock Text=", " />
<TextBlock Text="{Binding Street}" />
<TextBlock Text=", " />
<TextBlock Text="{Binding City}" />
</StackPanel>
The advantage of the second approach is that it gives you flexibility in the UI to change how addresses are displayed, e.g. multiple lines, formatting, etc.; the downside is that it gets complicated if you have conditional logic e.g. an optional flat number or second address line.
I tried to reproduce the problem and succeeded.
I activated the step-into-.NET debugging options, and saw that WPF does not listen to INotifyPropertyChanged if the path in the binding is empty.
What worked to get a change to be reflected in the list box is to replace the whole object in the ObservableCollection. This triggers the INotifyCollectionChanged, with the Replace action.
But this may not be acceptable in your case. And it could be seen more like a hack than a solid solution.
I'd seriously consider having a DataTemplate for Address. There you should bind to the exact properties you need (which would create the listener for INotifyPropertyChanged). It is more flexible than ToString() and you may encounter cases where you have a need for ToString() to do something for non-UI stuff, which would create a conflict. And honestly, ToString is not really meant for UI stuff.
I haven't used WPF that much so the solution to this is probably pretty easy.
In the ide I'm developing it will have multiple controls(text editor) each being hosted in a tab, much like VS does for each source file. When the user clicks new the "host" creates a new EditorWindow(a usercontrol), creates a new tab, and tells the tab to display the EditorWindow it created, and then updates a property called currentWindow (of type EditorWindow) with the one that's currently active. Inside the EditorWindow is the text editor whose name is textEditor(also a property). What I'm trying to do is take this code from the quick start source of the text editor control I'm using
<StackPanel>
<CheckBox Checked="EditiorOptionsChecked" IsChecked="{Binding ElementName=Control, Path=currentWindow.textEditor.IsIndicatorMarginVisible}" Content="Indicator margin visible" />
<CheckBox Checked="EditiorOptionsChecked" IsChecked="{Binding ElementName=Control, Path=currentWindow.textEditor.IsLineNumberMarginVisible}" Content="Line number margin visible" />
<CheckBox Checked="EditiorOptionsChecked" IsChecked="{Binding ElementName=Control, Path=currentWindow.textEditor.IsRulerMarginVisible}" Content="Ruler margin visible (useful for fixed-width fonts only)" />
<CheckBox Checked="EditiorOptionsChecked" IsChecked="{Binding ElementName=Control, Path=currentWindow.textEditor.IsSelectionMarginVisible}" Content="Selection margin visible" />
</StackPanel>
put that in the host controls xaml, and bind the checkboxes to the syntax editor. I've tried a couple different things to no avail. Control is the name of the window hosting all the tabs, and path is obviously supposed to be the property that the checkboxes are bound too. I'm pretty sure the problem is that at initial run-time currentWindow isn't initialized so therefore my bindings aren't ever getting updated, but I'm at a loss as to how to fix this issue. Thanks!
Since you are new to WPF, you may not know that properties have to implement some sort of change notifications in order for bindings to work. For instance, if any of the properties in the the path "currentWindow.textEditor.IsIndicatorMarginVisible" change, you need to inform the binding engine that it has changed. If you implement these properties as DependencyPropertys, the change tracking comes for free. Otherwise, you should implement INotifyPropertyChanged.
I've found that the Snoop utility is the easiest way to do quick binding debugging, you should try using it and see if it tells you anything useful on the bound properties.
One of the things I really like with WPF is the extent to which my views can be built declaratively, ie. using XAML rather than code-behind.
Now I'm really stumped by InputBindings, because their CommandParameters don't accept bindings. I imagine my case is pretty generic and straightforward, but I cannot see how I can do it without resorting to code-behind. Consider:
<ListBox Name="casingsListBox" ItemsSource="{Binding Path=Casings}" SelectedValuePath="Id">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Title}"/>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.InputBindings>
<!-- Doesn't work: -->
<MouseBinding Gesture="LeftDoubleClick"
Command="ApplicationCommands.Open"
CommandParameter="{Binding RelativeSource={RelativeSource Self} Path=SelectedValue}"/>
</ListBox.InputBindings>
</ListBox>
This will not work, since the binding expression for the MouseBinding's CommandParameter is illegal.
I ask myself: what's the point of adding a mouse-click gesture to a listbox if I cannot get to the selected value?
This can be easily solved using a code-behind event handler, of course, or by having the command consumer extract the id from the command source, but there are several reasons why this is undesirable. Apart from the fact that droves of code-behind code defeats (some of) the purpose of WPF in the first place, it makes the UI designers working in Expression Blend less empowered. And dammit, my command parameter shall be an id, not some UI element!!
Subjective: Having browsed SO for a while, I'm struck by the amount of code I see in the WPF-related questions. I get the feeling we developers stick to our old habits and happily hack away in the code-behind file rather than trying to utilize the fresh take on UI building that WPF is supposed to represent. What do you think?
But most importantly: can anyone show me a code-free workaround for this seemingly trivial problem? Preferably without terrible hacks like this one.
I wrote a markup extension that allows an InputBinding's Command to be databound :
<KeyBinding Modifiers="Control" Key="E" Command="{input:CommandBinding EditCommand}"/>
Your situation is slightly different since you want to bind the CommandParameter, but you can probably adapt my code to fit your case. Note that this code uses private reflection, which only works in full-trust, and can be broken in later versions of WPF (actually it is broken in WPF 4.0... I can post a modified version if you need it).
Another option is to use the CommandReference class that can be found in the MVVM toolkit :
<Window.Resources>
<c:CommandReference x:Key="EditCommandReference" Command="{Binding EditCommand}"/>
</Window.Resources>
...
<KeyBinding Modifiers="Control" Key="E" Command="{StaticResource EditCommandReference}"/>
Again, this is for binding the Command property, but can probably be adapted to bind the CommandParameter...
The new way to solve this problem is by using Expression Triggers / Actions which allow you to set up keyboard shortcuts on arbitrary controls that do custom actions (like firing a Command).
I'm running into a problem that seems to have no sensible / tractable solution that I am happy with. Silverlight and Internationalisation - a quagmire in the making.
I have a set of resource files that contain translated strings.
In the simple case, I can declare the language resource etc and bind the content / text of a value within the strongly typed resource. Thats fine - plenty of examples of that posted around.
A less simple case is that the strings can have a parameter, so I need to inject a value into the string for the display of it, which in itself can be a data binding.
The more complex scenario is a string with multiple parameters.
You can argue that the VM should provide this ability, but I am unhappy at that since it breaks the divide between the UX Designer and the Developer, requiring the developer to implement a property / method on the VM to support every string the UI requires.
For example : The designer decides after user feedback to add a custom tooltip with more information on it. The tooltip is done declaratively and combines a better explanation with values from the datacontext. The explanation is stored in the resources and the values used come from either an existing data context or an element to element binding.
If I have to run every string through the VM then the addition of something like this requires VM changes. Equally if the source of the parameters will be from other elements, then the VM is not necessarily in the position to provide the formatted string.
How have people got round / approached this issue?
A.
This question is more complex than I can answer fully, but if you want bindings to consider a specific localization you should set the Language property of your UI Container (maybe your top level UserControl class). After that all bindings should use your requested localization. Check out this article, which concerns WPF but uses techniques that appear to be applicable to Silverlight: http://www.west-wind.com/weblog/posts/796725.aspx
I don't know exactly what you want to do but you've got several solutions.
Create a new string in your VM and make it INotifyPropertyChanged-able
public string MyTranslatedString
{
get
{
return string.Format("{0} Someone", LocalizedResource.Hello;
}
};
And then listen for localization change events (from your application)
Create several text blocks and bind the localized items:
<TextBlock HorizontalAlignment="Stretch" Foreground="Black" Text="{Binding Path=Resource.CurrentlyLoggedInAs, Source={StaticResource LocalizedStrings }}" VerticalAlignment="Center" Margin="0,0,5,0" />
<TextBlock HorizontalAlignment="Stretch" Foreground="Black" Text="{Binding Path=Username}" VerticalAlignment="Center" Margin="0,0,5,0" />
<Button Commands:Click.Command="{Binding Path=LogOutCommand}" Commands:Click.CommandParameter="" Content="{Binding Path=Resource.LogOut, Source={StaticResource LocalizedStrings }}" />
You need to add, in your main app:
<Application.Resources>
<local:LocalizedStrings
xmlns:local="clr-namespace:Localization.Silverlight;assembly=Localization.Silverlight"
x:Key="LocalizedStrings" />
</Application.Resources>
These are the easiest approaches I came up with but I'm quite happy to have something providing me with something simpler.