Why is IValueConverter ConverterParameter not Bindable? [duplicate] - c#

In Xamarin Forms I'm trying to create a xaml converter with properties.
This is to be used, for example, to show values from a list in different ways, based on a code behind property.
I based my code on this: https://stackoverflow.com/a/29869734.
Converter:
namespace App2.Converters
{
class MyConverter : IValueConverter
{
public int ConvParam { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return $"value: {value} - ConvParam: {ConvParam}";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}
XAML:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:conv="clr-namespace:App2.Converters"
x:Class="App2.MainPage"
x:Name="MainPageXaml">
<ContentPage.Resources>
<conv:MyConverter x:Key="cnv" ConvParam="{Binding Source={Reference MainPageXaml}, Path=PropParam}" />
<!--<conv:MyConverter x:Key="cnv" ConvParam="333" />-->
</ContentPage.Resources>
<StackLayout Orientation="Vertical">
<!-- Place new controls here -->
<Label Text="{Binding Source={Reference MainPageXaml}, Path=PropVal}" />
<Label Text="{Binding Source={Reference MainPageXaml}, Path=PropParam}" />
<Label Text="{Binding Source={Reference MainPageXaml}, Path=PropVal, Converter={StaticResource cnv}}" />
</StackLayout>
Code behind:
public partial class MainPage : ContentPage
{
public int PropVal { get; set; } = 111;
public int PropParam { get; set; } = 222;
public MainPage()
{
InitializeComponent();
}
}
The goal is to bind ConvParam of my converter to PropParam in code behind.
But if I use:
<conv:MyConverter x:Key="cnv" ConvParam="{Binding Source={Reference MainPageXaml}, Path=PropParam}" />
the error Position 10:39. No property, bindable property, or event found for 'ConvParam', or mismatching type between value and property is shown and the app doesn't compile.
The property ConvParam itself is recognized inside xaml: if I replace the above line with
<conv:MyConverter x:Key="cnv" ConvParam="333" />
everything works.
The binding expression I used ({Binding Source={Reference MainPageXaml}, Path=PropParam}) actually works, if used as source for the text property of a label:
<Label Text="{Binding Source={Reference MainPageXaml}, Path=PropParam}" />
But if I use it in Resources, It doesn't work.

Thanks to Julipan I could make it work!
As he pointed out, ConvParam must be a BindableProperty, so I modified my converter to inherit from BindableObject and defined ConvParam as BindableProperty.
Converter:
namespace App2.Converters
{
class MyConverter : BindableObject, IValueConverter
{
public static readonly BindableProperty ConvParamProperty = BindableProperty.Create(nameof(ConvParam), typeof(int), typeof(MyConverter));
public int ConvParam
{
get { return (int)GetValue(ConvParamProperty); }
set { SetValue(ConvParamProperty, value); }
}
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return $"value: {value} - ConvParam: {ConvParam}";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

Related

How to split a string and insert it into Binding in the DataTemplate

I have a DataTemplate of an acollectionView, inside I have a Label with the Text in Binding property.
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid BackgroundColor="Gray" Opacity="0.8" RowSpacing="0.1">
<Label TextColor="White" Text="{Binding Data}"/>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
I would need to be able to split that string into multiple strings, and I was able to find this code
public class DelimiterConverter : IValueConverter
{
public object Convert(Object value, Type targetType, object parameter, CultureInfo culture)
{
string[] values = ((string)value).Split(' ');
int index = int.Parse((string)parameter);
return values[index];
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return "";
}
}
HumorDiary[] note = JsonConvert.DeserializeObject<HumorDiary[]>(textt);
DelimiterConverter conv = new DelimiterConverter();
foreach (HumorDiary hd in note)
{
conv.Convert(hd.Data, typeof(string), " ", CultureInfo.CurrentCulture);
}
I don't know if I have entered everything right, but I would not know how to obtain the various strings divided into several parts, in the DataTemplate
It could be used like this:
Add the namespace where your converter is to you page
xmlns:converters="clr-namespace:MyApp.Converters"
Add the converter to your page's resources
<ContentPage.Resources>
<converters:StringSplitConverter x:Key="StringSplitConverter" />
</ContentPage.Resources>
Edit the CollectionView's ItemTemplate to display all the separated strings in separate labels (using BindableLayout)
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid BackgroundColor="Gray" Opacity="0.8" RowSpacing="0.1">
<!-- Bind the ItemsSource to the Data using the converter -->
<StackLayout BindableLayout.ItemsSource="{Binding Data, Converter={StaticResource StringSplitConverter}}">
<BindableLayout.ItemTemplate>
<DataTemplate>
<!-- Display the separated word -->
<Label Text="{Binding .}" />
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
This is the converter. Takes the string and splits it by spaces
public class StringSplitConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is string stringValue)
{
return stringValue.Split(' ');
}
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}

Bind value to a function with parametr in xamarin

Is it possible to bind value that function returns?
Something like that:
<Label Text="{Binding function(param)}"></label>
public string function(string param){
return param;
}
Except Ryan's code,you said that the value is fixed value, you can pass parameter in Converter.
<ContentPage
x:Class="demo3.simplecontrol.Page10"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:converter="clr-namespace:demo3"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<ContentPage.Resources>
<converter:Converter1 x:Key="converter1" />
</ContentPage.Resources>
<ContentPage.Content>
<StackLayout>
<Label
HorizontalOptions="CenterAndExpand"
Text="{Binding str,Converter={StaticResource converter1},ConverterParameter=255}"
VerticalOptions="CenterAndExpand" />
</StackLayout>
</ContentPage.Content>
public partial class Page10 : ContentPage, INotifyPropertyChanged
{
private string _str;
public string str
{
get { return _str; }
set
{
_str = value;
RaisePropertyChanged("str");
}
}
public Page10()
{
InitializeComponent();
str = "this is test";
this.BindingContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
The Converter.cs:
public class Converter1 : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
string str = (string)value;
string p = (string)parameter;
return str+ parameter;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Use a converter:
Create new class for converter:
public class LabelConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value.ToString();
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Add reference to converter in xaml:
<ContentPage xmlns:converters="clr-namespace:JTJ.Converters">
<ContentPage.Resources >
<ResourceDictionary>
<converters:LabelConverter x:Key="labelConverter"/>
</ResourceDictionary>
</ContentPage.Resources>
Bind value to converter
<Label Text="{Binding LabelText, Converter={StaticResource labelConverter}}"

Access Entry from ValueConverter

I need access to the entry component that the converter is attached to, so I can change the cursor position with Entry.CursorPosition.
I have a BindableObject that is also an IValueConverter, how can I get to the Entry
public class MaskConverter : BindableObject, IValueConverter
{
...
}
In XAML name the Entry and use x:Reference:
Converter with MyEntry property (ConvertBack for Mode=TwoWay not shown):
public class MyConverter : BindableObject, IValueConverter
{
public Entry MyEntry { get; set; }
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var entry = MyEntry;
Debug.WriteLine($"convert:pos:{entry?.CursorPosition}:");
return (string)value;
}
...
}
XAML using MyEntry property (MyText is a property on the viewmodel, not shown):
<ContentPage.Resources>
<local:MyConverter x:Key="myConverter" MyEntry="{x:Reference myEntry}" />
...
</ContentPage.Resources>
...
<Entry x:Name="myEntry" Text="{Binding MyText,
Converter={StaticResource myConverter}}">
Without the MyEntry property the named Entry might be passed to the converter using ConverterParameter:
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var param = parameter as Entry;
Debug.WriteLine($"convert:pos:{param?.CursorPosition}:");
return (string)value;
}
XAML when passing the named Entry in ConverterParameter:
<ContentPage.Resources>
<local:MyConverter x:Key="myConverter" />
...
</ContentPage.Resources>
...
<Entry x:Name="myEntry" Text="{Binding MyText,
Converter={StaticResource myConverter},
ConverterParameter={x:Reference myEntry}}">

Image Converter not working in Xamarin.Forms

Im using the converter to change the image but image is not loading here can somebody please help me here what I'm done wrong? I had checked the converter by debugging it is not getting hit.
Converter Code:
public class DownloadIconConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return ((bool)value) ? "progressicon.png" : "cloud_download.png";
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
In Xaml
<ContentPage.Resources>
<converter:DownloadIconConverter x:Key="downloadiconconverter" />
</ContentPage.Resources>
<ImageButton
HeightRequest="40"
IsVisible="True"
Source="{Binding DownloadIcon,Converter={StaticResource downloadiconconverter}}"
WidthRequest="35" Command ="{Binding BindingContext.Download, Source={x:Reference listview}}" CommandParameter="{Binding .}" />
In Code behind file
public bool DownloadIcon
{
get { return downloadicon; }
set { SetProperty(ref downloadicon, value); }
}
The Binding in my knowledge is not working because the boolean is in the ViewModel and not accessible to the listview data template
Try using this:
Source="{Binding BindingContext.DownloadIcon, Converter={StaticResource downloadiconconverter},Source={x:Reference listview}}"

Binding to last array element

So far, i have an ObservableCollection<T> for objects.
I always want to display the last inserted element into a TextBlock. I implemented two solutions in XAML, but both not working:
<TextBlock Text="{Binding Path=entries.Last().message, FallbackValue=...}" />
<TextBlock Text="{Binding Path=entries[entries.Length-1].message, FallbackValue=...}" />
This one works, but references the first entry:
<TextBlock Text="{Binding Path=entries[0].message, FallbackValue=...}" />
Am i missing something? Is it possible to do with pure XAML?
Solution 1 :
You can use a custom converter to achieve this :
Converter class :
class LastItemConverter : IValueConverter
{
public object Convert(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
IEnumerable<object> items = value as IEnumerable<object>;
if (items != null)
{
return items.LastOrDefault();
}
else return Binding.DoNothing;
}
public object ConvertBack(object value, System.Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new System.NotImplementedException();
}
}
Xaml :
<Application.Resources>
<local:LastItemConverter x:Key="LastItemConverter" />
</Application.Resources>
<TextBlock Text="{Binding Path=entries, Converter={StaticResource LastItemConverter}}" />
Solution 2 :
The other way is to create a new property in your model that returns the entry :
public Object LastEntry => entries.LastOrDefault();
Xaml :
<TextBlock Text="{Binding Path=LastEntry, ... " />

Categories

Resources