Xamarin Forms Converter being called on page disappearing? - xamarin.forms

<Label Grid.Row="0" Grid.Column="1"
Text="{Binding Date, Converter={StaticResource localTimeConverter}, StringFormat='{0:MMMM dd, yyyy}'}"
LineBreakMode="NoWrap"
FontSize="16"
Style="{DynamicResource FieldLabel}"/>
I have the above Xaml and the below Converter:
public class UtcToLocalDateTimeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((DateTime)value).ToLocalTime();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
However the Converter is not being called when the page starts instead it gets called when navigating away from the page. I add a break point in the convert method and that's when the method is being called. This is confusing!?
Please can someone offer some help?

Whoops ! This was my bad.
I was adding the converter into the SelectedItemTemplate of the ListView instead of the ItemTemplate of the ListView. Thats why the converter never got called. All fixed now.

Related

How to style an Avalonia DataGridRow via data binding to a view model row

I am using a DataGrid control in Avalonia and I want to apply styles to DataGridRow based on data binding to the view model backing a row. How can I do this?
I figure it will be based on classes, so I defined a style for an isRequired class:
<DataGrid Items="{Binding Players}">
<DataGrid.Styles>
<Style Selector="DataGridRow.isRequired">
<Setter Property="Background" Value="LightGreen" />
</Style>
</DataGrid.Styles>
</DataGrid>
But notice the class is supposedly going to be set on a DataGridRow, but how can I set that class on the row based on a bound Player view model?
In WPF I'd often reach for Triggers but I've started to employ more Converters lately.
<DataGrid.Styles>
<Style Selector="DataGridRow">
<Setter Property="Background" Value="{Binding Tag, Converter={StaticResource OrderRowBackgroundConverter}}" />
</Style>
</DataGrid.Styles>
public class OrderRowBackgroundConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value != null)
{
switch (value.ToString())
{
case "":
return new SolidColorBrush(Color.Parse("#FF8D8D"));
case "fulfilled":
return new SolidColorBrush(Color.Parse("#B8FFB8"));
}
}
return new SolidColorBrush(Color.Parse("#FF8D8D"));
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

How pass the current data with converter?

How can i send the current element of my Actors list to my converter? and as well i need pass a parameter with ConverterParameter
<ListView x:Name="Actors" ItemsSource="{Binding Actors}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.ContextActions>
Source="" IsVisible="{Binding ., Converter={StaticResource ListConverter}, ConverterParameter={Binding ActorCurrent}}"}"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
this Actor exist in my view movel, i need to pass as parameter
private Actor _actorCurrent;
public Actor ActorCurrent
{
get => _actorCurrent;
set
{
_actorCurrent = value;
RaisePropertyChanged(() => ActorCurrent);
}
}
i tried with dot, but dont work
IsVisible="{Binding . // in my Converter class i dont nothing
EDIT 1:
i have as well this
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// i want to value = will be {Binding .,
// and parameter = will be ActorCurrent
// then i want to compare value with parameter and return a bool
}
Or can i use triggers?
<DataTrigger TargetType="Image" Binding="{Binding Name}" Value="{Binding ActorCurrent.name}">
i want to value = will be {Binding .}
You can get the value in the Convert method, you have to know the type of your value first. You can add a breakpoint there to get the type of value:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
//if the type of value is Actor:
Actor tempActor = value as Actor;
//if the type of value is string:
string tempStr = value as string
return "123";
}
and parameter = will be ActorCurrent
Actually, you can't set binding to ConverterParameter as ConverterParameter is not a BindableProperty.
There is a discussion about this problem in this thread and you may find some idea there.

Xamarin : Date in string

I am new with xamarin, I am facing an issue in my xamarin forms project.
I have a Label inside listview-viewcell, to show time in UI. The date is received as a number like 1510822596449(Java Timestamp). I want to show the date in strings like "n days ago".How can I achieve this?
<StackLayout>
<ListView>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding createdTime}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
Anybody please suggest a solution with working code.
Thanks in advance.
First, create a class DatetimeToStringConverter and add the following code:
public class DatetimeToStringConverter : IValueConverter
{
#region IValueConverter implementation
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return string.Empty;
return new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)
.AddMilliseconds((long)value) // put your value here
.ToLocalTime().ToString("g");
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
#endregion
}
Then in xaml add the following code at the root of the page:
<ContentPage.Resources>
<ResourceDictionary>
<local:DatetimeToStringConverter x:Key="cnvDateTimeConverter"></local:DatetimeToStringConverter>
</ResourceDictionary>
</ContentPage.Resources>
Then add namespace:
xmlns:local="clr-namespace:Myapp;assembly=Myapp"
Then change the label text like this:
Text="{Binding createdTime, Converter={StaticResource cnvDateTimeConverter}}"
Keep the createTime type as long in model, otherwise you get invalid cast exception.
As suggested in the comments you could do this with a ValueConverter.
Write a converter similar to this, in your shared code.
public class TicksToDateTimeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!Int64.TryParse(value, out long ticks))
return DatTime.Now;
// TODO you can do a ToString and format it you want here but also in XAML
return new DateTime(ticks);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
// Not needed
throw new NotImplementedException();
}
}
Now in your XAML, declare te value convter like this under the root of your page, I'm assuming it's a ContentPage.
<ContentPage.Resources>
<ResourceDictionary>
<local:TicksToDateTimeConverter x:Key="TicksConverter" />
</ResourceDictionary>
</ContentPage.Resources>
And don't forget to declare the local namespace in your page root like: xmlns:local="clr-namespace:YourApp.Namespace", which should be the full namespace, without the class name to your converter class.
To finally use the converter in your layout, do this:
<StackLayout>
<ListView>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Label Text="{Binding createdTime, Converter={StaticResource TicksConverter}}"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
Depending on whether or not you return a string from the converter or a DateTime, in the latter case you can also format it here in XAML, like so:
<Label Text="{Binding createdTime, Converter={StaticResource TicksConverter}, StringFormat='{0:dd-MM-yyyy}'}"/>
Or you could choose to do it differently altogether and convert the value inside the model that you bind to the ViewCell.

Xamarin.Forms ImageCell binding to a local file

I'm using data binding in a ListView to bind a list of ImageCells. The image is a file stored locally on the device as app data.
On Windows, using an absolute or relative path to the file does not work, I have to convert it to a file:// URI. Unfortunately, on Android, the file:// URI doesn't work, and it needs to be a path.
I'm currently working around the issue by using a different value in the view model depending on the target platform. Is there a better solution than this:
if (Device.OS == TargetPlatform.Windows) {
result.uri = new Uri(uri).AbsoluteUri;
}
Xaml:
<ListView.ItemTemplate>
<DataTemplate>
<ImageCell ImageSource="{Binding Uri}"
Text="{Binding Name}">
</ImageCell>
</DataTemplate>
</ListView.ItemTemplate>
The type of Uri is string, do I need to use a UriImageSource instead?
I solved it by creating converter and dependency service.
Xaml
<ContentPage.Content>
<StackLayout VerticalOptions="FillAndExpand" Padding="5,20,5,0" >
<ListView x:Name="list" ItemsSource="{Binding MyList}">
<ListView.ItemTemplate>
<DataTemplate>
<ImageCell Text="{Binding Name}" ImageSource="{Binding ImagePath, Converter={StaticResource AndroidImageInvert}}">
</ImageCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
Converter
public class ByteImageConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
string fileName = value as string;
return ImageSource.FromStream(() => new MemoryStream(DependencyService.Get<IWRDependencyService>().GetImageBytes(fileName)));
}
public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Dependency Service
public byte[] GetImageBytes(string fileName)
{
fileName = fileName.Replace(".jpg", "").Replace(".png", "");
var resId = Forms.Context.Resources.GetIdentifier(
fileName.ToLower(), "drawable", Forms.Context.PackageName);
var icon = BitmapFactory.DecodeResource(Forms.Context.Resources, resId);
var ms = new MemoryStream();
icon.Compress(Bitmap.CompressFormat.Png, 0, ms);
byte[] bitmapData = ms.ToArray();
return bitmapData;
}

Binding errors in Xamarin Forms

Is there a way I can see the binding errors in while developing Xamarin Forms apps? The Application Output tab shows nothing but the binding doesn't work. How can I debug bindings?
I'd like to suggest you to add EmptyConverter:
public class EmptyConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
}
Then create instance of converter on your page:
<ContentPage.Resources>
<ResourceDictionary>
<converters:EmptyConverter x:Key="EmptyConverter"/>
</ResourceDictionary>
</ContentPage.Resources>
Then add converter to label:
<Label Text="{Binding Text, Converter={StaticResource EmptyConverter}}"/>
Put breakpoints in Convert and ConvertBack methods and you'll be able to see all changes of binded values.
Hope this will help you.
You can try to use the Compiled Bindings:
https://learn.microsoft.com/xamarin/xamarin-forms/app-fundamentals/data-binding/compiled-bindings
You will have gain performance and precise error reporting

Resources