Specify signature when using x:Bind - xaml

UWP allows you to bind to a static method. I'm trying to get a time string by using
<TextBlock Text={x:Bind MyDateTime.ToString(MyPatternString)} />
where MyPatternString is "h:mm tt".
The problem is that DateTime's ToString() method has several different signatures. The first one receives an IFormatProvider. Because of this, I get a build error:
Function parameter 'provider' is invalid or missmatched
Is there any way to tell it that I wish to use the signature which accepts a string? I'd have thought it would automatically know this.

You can just add a method to your ViewModel and use that instead!
That way your binding expression can be changed to this:
<TextBlock Text={x:Bind FormatDateToString(MyDateTime)} />
Be advised that this works with the Windows 10 Anniversary Update only! More info on this matter here!

Found your question after searching for the answer myself; didn't find much help posted anywhere but did figure it out after some trial and error.
Function parameter 'provider' is invalid or mismatched
The reason for this that in XAML, a specific overload is being called which is DateTimeProperty.ToString(string, IFormatProvider).
In my case, any value I display is within a User Control so for each I added a CultureInfo dependency property and bound it to common source on my view model.
If C#, add:
using System.Globalization;
Then
public static readonly DependencyProperty CultureInfoProperty = DependencyProperty.Register(
"CultureInfo", typeof(CultureInfo), typeof(XyzReadoutView), new PropertyMetadata(default(CultureInfo)));
public CultureInfo CultureInfo
{
get { return (CultureInfo) GetValue(CultureInfoProperty); }
set { SetValue(CultureInfoProperty, value); }
}
This creates a local instance required for x:Bind, compile errors occur if using a static property.
And XAML:
<TextBlock Text={x:Bind MyDateTime.ToString('h:mm tt', CultureInfo)} />
Notice the format is surrounded with ' and not ".
Also, this will only update once as the mode for x:Bind has a default of Mode=OneTime; if you want changes on the DateTime or CultureInfo to propagate, the mode must be changed to Mode=OneWay.
<TextBlock Text={x:Bind MyDateTime.ToString('h:mm tt', CultureInfo), Mode=OneWay} />
If the format is user changeable, I would create a dependency property for it as well for updates and easy control binding back to a view model but that's just my personal preference.
public static readonly DependencyProperty DateTimeFormatProperty = DependencyProperty.Register(
"DateTimeFormat", typeof(string), typeof(XyzReadoutView), new PropertyMetadata(default(string)));
public string DateTimeFormat
{
get { return (string) GetValue(DateTimeFormatProperty); }
set { SetValue(DateTimeFormatProperty, value); }
}
And XAML:
<TextBlock Text={x:Bind MyDateTime.ToString(DateTimeFormat, CultureInfo), Mode=OneWay} />

You need to use an IValueConverter to format the text for display. Create a class which inherits from IValueConverter.
public class DateFormatter : IValueConverter
{
// This converts the DateTime object to the string to display.
public object Convert(object value, Type targetType,
object parameter, string language)
{
// Retrieve the format string and use it to format the value.
string formatString = parameter as string;
if (!string.IsNullOrEmpty(formatString))
{
return string.Format(
new CultureInfo(language), formatString, value);
}
// If the format string is null or empty, simply call ToString()
// on the value.
return value.ToString();
}
// No need to implement converting back on a one-way binding
public object ConvertBack(object value, Type targetType,
object parameter, string language)
{
throw new NotImplementedException();
}
}
Register it as resource on the Page and then you can specify the converter in the binding
<TextBlock Text={x:Bind MyDateTime, Converter={StaticResource DateFormatConverter}}, ConverterParameter="mm/dd/yyyy"}"/>
https://learn.microsoft.com/en-us/uwp/api/Windows.UI.Xaml.Data.IValueConverter

Related

Binding to Element height Plus a value

I wanna bind my element height to another element height plus 20 pixel...
Is it possible to do such a thing?
<ScrollViewer Height="{Binding Height, ElementName=AnotherElement}">
Yes, by using a value converter
public class IncreaseByValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var passedInValue = System.Convert.ToDouble(value);
var increaseByValue = System.Convert.ToDouble(parameter);
return passedInValue + increaseByValue;
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
defined as a static resource somewhere in your resources:
<local:IncreaseByValueConverter x:Key="IncreaseByValueConverter" />
used like this:
<ScrollViewer Height="{Binding Height, ElementName=AnotherElement, Converter={StaticResource IncreaseByValueConverter}", ConverterParameter="20">
You'll probably want to handle the converter parameter better by using some try/catch code in the IncreaseByValueConverter.Convert method. For example, make the parameter optional, so if it's not passed in, use a default value, or something like that...

Converter does not work after calling OnPropertyChanged

I have been trying to make this work for a while with no success.
I have a converter to pretty print dates on the page. Convert function is as follows;
class DateConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
Nullable<int> _date = value as Nullable<int>;
if (!_date.HasValue)
return DependencyProperty.UnsetValue;
if (!String.IsNullOrEmpty(parameter as String))
{
if (parameter.Equals("EDITED"))
return "edited " + UtilityFunctions.formatUnixTime(_date.Value);
}
return UtilityFunctions.formatUnixTime(_date.Value);
}
}
In XAML part I am using this converter by passing a property of a property to it.
<TextBlock Text="{Binding Wiki.EditDate, Converter={StaticResource DateConverter}, ConverterParameter=EDITED}" />
My ModelView extends BindableBase and whenever I update Wiki property, I call SetProperty which in return calls OnPropertyChanged for Wiki property.
If I try to show the date without a converter, it works fine.
<TextBlock Text="{Binding Wiki.EditDate}" />
I am using this converter in other parts of my projects so I believe it is not the cause of the problem.
What might be the cause of this problem?
Thanks for any helps...
As it turned out in discussion and debugging - the problem was that the value passed to converter was wrong - "2014-06-03 00:56:21".
Such value can't be coverted to int, and because you are using as:
Nullable<int> _date = value as Nullable<int>;
there won't be exception thrown. Instead of exception you have null (as doesn't throw InvalidCastException).
If you would use cast:
Nullable<int> _date = (Nullable<int>)value;
then you would have an exception. Some more information you will surely find on SO - for example this answer.

Resource from DLL in other assembly

I'm creating an application that will be build in modules (DLL's). One of these DLL's provides the Strings for the application in different languages.
The DLL has following structure:
Languages (This is the project.)
Properties
References
Language.cs (Here I'm able to set the desired language from the application and has a GetText function that returns the String based on the set Culture.) Please let me know is this is a good approach.
Language.en-US.resx
Language.nl-BE.resx
So, from Languages.cs I'm able to get the desired String in C# from the resource files based on the set Culture.
Here is an example:
I set the culture to Dutch: Languages.Language.SetCulture("nl-BE");
I request the Dutch word for the resource Name: Language.GetText("Name");
It will return the String Naam.
That was C#. Xaml appears to be a bigger problem to get a Label.Text get the resource Name.
I added xmlns:l="clr-namespace:My.Languages.;assembly=My.Languages" to my xaml file, but this is as far as I got. I can't figure out a way how to have a similar Language.GetText("Name") in Binding.
My only solution at this point would be to bind every Label.Text to a property in my C# code and get the correct String from there. This will generate a lot of "not needed" binding/properties and would be hard to maintain.
Any suggestions?
My approach for Xaml would be to use a Converter that would call your Language.GetText(key);
e.g. XAML
<TextBlock Text={Binding FallbackValue='Naam', ConverterParameter='Name' Converter={StaticResource Translate}}" />
C#
public class TranslateConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (DesignerProperties.GetIsInDesignMode(new DependencyObject()))
return DependencyProperty.UnsetValue;
string localizedValue = string.Empty;
if (parameter is string)
{
localizedValue = Language.GetText(parameter as string);
return localizedValue;
}
else
{
return DependencyProperty.UnsetValue;
}
}
}

StringFormat in XAML

I am trying to format my string to have commas every 3 places, and a decimal if it is not a whole number. I have checked roughly 20 examples, and this is the closest I have come:
<TextBlock x:Name="countTextBlock" Text="{Binding Count, StringFormat={0:n}}" />
But I get a The property 'StringFormat' was not found in type 'Binding'. error.
Any ideas what is wrong here? Windows Phone 8.1 appears to differ from WPF, because all of the WPF resources say that this is how it is done.
(The string is updated constantly, so I need the code to be in the XAML. I also need it to remain binded. Unless of course I cannot have my cake and eat it too.)
It seems that, similar to Binding in WinRT, Binding in Windows Phone Universal Apps doesn't have StringFormat property. One possible way to work around this limitation is using Converter as explained in this blog post,
To summarize the post, you can create an IValueConverter implmentation that accept string format as parameter :
public sealed class StringFormatConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
if (value == null)
return null;
if (parameter == null)
return value;
return string.Format((string)parameter, value);
}
public object ConvertBack(object value, Type targetType, object parameter,
string language)
{
throw new NotImplementedException();
}
}
Create a resource of above converter in your XAML, then you can use it like this for example :
<TextBlock x:Name="countTextBlock"
Text="{Binding Count,
Converter={StaticResource StringFormatConverter},
ConverterParameter='{}{0:n}'}" />

Reference to current class xaml Windows Phone

I got the following code:
namespace SomeApp
{
public partial class MyClass : PhoneApplicationPage, IValueConverter
{
SOME METHODS...
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return true;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return true;
}
}
}
I would like to bind this class to a ValueConverter of a RadioButton. Is there any way to reference to the current class I'm working with? For example:
<phone:PhoneApplicationPage
x:Class="SomeApp.MyClass"
xmlns:local="clr-namespace:SomeApp">
<phone:PhoneApplicationPage.Resources>
<local:MyClass x:Key="myClass"/>
</phone:PhoneApplicationPage.Resources>
<RadioButton IsChecked="{Binding Converter={StaticResource myClass}}"/>
Thanks in advance =)
First using your page as a converter don't seem like a good idea, it's better practice to separate the converter fonctionality in a separate class. Particularly it would be a very bad idea to create a StaticResources of a converter created that way as it will use a lot of unessessary memory to create the whole page.
The only thing a converter can be binded in xaml is a StaticResource so you will not be able to do it in xaml but if you really want to do it you could do it by creating the binding from code behind (for example in the constructor of the page):
Binding binding=new Binding();
binding.Converter = this;
myRadioButton.SetBinding(CheckBox.IsCheckedProperty, binding);