Xamarin Binding fuction as ImageSource inside a Listview with Databinding - image

I'm trying to visualize the state of an object, called MinRepresentation, in the ListView below.
I want to bind the function ImageSource stateImage(MinRepresentationState state), where i handover the state and get back the Imagesource.
My Problem is, accsessing the function and passing the parameter via xaml.
Visible Orders is an Collection of MinRepresentation each includes State
<ListView HasUnevenRows="True" SelectionMode="Single" ItemsSource="{Binding VisibleOrders}" ItemSelected="OnListViewItemSelected" ItemTapped="OnListViewItemTapped">
***OTHER STUFF***
<StackLayout Orientation="Horizontal" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="3" Padding= "0,5,0,5" BackgroundColor="#CC3F6E3F">
<Image Source="{Binding stateImage(State)" Margin="10,0,0,0" />
<Label Text="{Binding Id, StringFormat='ID: [{0}]'}" FontSize="Small" Margin="5,0,0,0" FontAttributes="Bold" TextColor="#FFFFFF"/>
</StackLayout>
***OTHER STUFF***
</ListView>
public ImageSource stateImage(MinRepresentationState state)
{
switch (state)
{
case MinRepresentationState.Assigned:
return ImageSource.FromResource("state_assigned.png");
}
}

You can use IValueConverter to achieve that. Create an IValueConverter class and place your logic code there:
public class StateToImageConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
MinRepresentationState representationState = (MinRepresentationState)value;
if (representationState == MinRepresentationState.Assigned)
{
return "state_assigned.png";
}
return "otherImage.png";
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then consume it in XAML like:
<!--Define it in resources-->
<ContentPage.Resources>
<local:StateToImageConverter x:Key="StateToImageConverter"/>
</ContentPage.Resources>
<!--Use converter-->
<Image Source="{Binding State, Converter={StaticResource StateToImageConverter}}" Margin="10,0,0,0"/>

You can only bind to public properties. Create a StateImage property on your model
public string StateImage
{
get {
switch (State)
{
case MinRepresentationState.Assigned:
return "state_assigned.png";
}
}
}

You could also use a DataTrigger to set the image in your XAML
<Image Source="state_default.png" Margin="10,0,0,0">
<Image.Triggers>
<DataTrigger TargetType="Image" Binding="{Binding State}" Value="Assigned">
<Setter Property="Source" Value="state_assigned.png" />
</DataTrigger>
</Image.Triggers>
</Image>
Reference: https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/triggers#data

Related

Create custom frame in xamarin forms

I have to create a frame like in the attached photo ,the thing is that it has to be adaptable as it will contain labels/texts , also it should have elevation and shadow .
All that is easily doable with a frame , the problem is the little speech thing , i can't find a way to make it .
maybe someone has an idea to start with .
thanks
You can binding the Height and Width of the frame with its child element (like label).
in code behind
public class SizeConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((double)value + 20.0); // you can set the logic of height and width here
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return 0;
}
}
in xaml
<ContentPage.Resources>
<ResourceDictionary>
<local:SizeConverter x:Key="SizeConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<Frame CornerRadius="10" WidthRequest="{Binding Source={x:Reference contentLabel}, Path=Width,Converter={StaticResource SizeConverter} ,Mode=OneWay}" MinimumHeightRequest="100" HeightRequest="{Binding Source={x:Reference contentLabel}, Path=Height,Converter={StaticResource SizeConverter} ,Mode=OneWay}" BackgroundColor="LightBlue" Padding="10" >
<!-- Place new controls here -->
<Label x:Name="contentLabel" BackgroundColor="LightGray" Text="xxx"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand" />
</Frame>
</StackLayout>

How can I make a Xamarin Template customizable by passing in a type parameter?

Here is the method that I have come up with so far. However it seems like not such a clean solution.
Does anyone have any suggestions how I could come up with a better solution for doing this?
<?xml version="1.0" encoding="utf-8"?>
<StackLayout xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Japanese;assembly=Japanese"
x:Class="Japanese.Templates.HeaderTemplate"
x:Name="this" HorizontalOptions="FillAndExpand" Orientation="Vertical" Spacing="0" Margin="0">
<StackLayout IsVisible="{Binding HeaderType, Converter={StaticResource HeaderType1BoolConverter}, Source={x:Reference this} }" >
<!-- code -->
</StackLayout>
<StackLayout IsVisible="{Binding HeaderType, Converter={StaticResource HeaderType2BoolConverter}, Source={x:Reference this} }" >
<!-- code -->
</StackLayout>
</StackLayout>
In my bank end CS:
using System;
using System.Collections.Generic;
using Xamarin.Forms;
namespace Japanese.Templates
{
public partial class HeaderTemplate : StackLayout
{
public HeaderTemplate()
{
InitializeComponent();
}
public static readonly BindableProperty HeaderTypeProperty =
BindableProperty.Create(
nameof(HeaderType),
typeof(string),
typeof(DataViewCellTemplate),
default(string));
public string HeaderType
{
get { return (string)GetValue(HeaderTypeProperty); }
set { SetValue(HeaderTypeProperty, value); }
}
}
}
Converter Code:
public class HeaderType1BoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return object.Equals(value, "1");
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
public class HeaderType2BoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return object.Equals(value, "2");
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
In the calling code:
<template:HeaderTemplate Header="Application" HeaderType="1" />
Using triggers is an option.
For e.g.: You can add property-triggers to check the value on HeaderType and accordingly update the Content (or layout) in your custom control HeaderView.
Please note in this case we are extending from ContentView not StackLayout on the assumption that only one layout/control is visible at a time.
XAML
<ContentView
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:SampleApp"
x:Class="SampleApp.HeaderView">
<ContentView.Triggers>
<!-- if header type is 1, use header1 layout -->
<Trigger TargetType="local:HeaderView" Property="HeaderType" Value="1">
<Setter Property="Content">
<Setter.Value>
<Label Text="Header1" />
</Setter.Value>
</Setter>
</Trigger>
<!-- if header type is 2, use header2 layout -->
<Trigger TargetType="local:HeaderView" Property="HeaderType" Value="2">
<Setter Property="Content">
<Setter.Value>
<StackLayout>
<Label Text="Header2" />
<BoxView HeightRequest="1" BackgroundColor="Gray" />
</StackLayout>
</Setter.Value>
</Setter>
</Trigger>
<!-- you can add more layouts here if you need -->
</ContentView.Triggers>
<!-- add default content that can be displayed in case of no match -->
<StackLayout>
<Label Text="DefaultHeader" />
<BoxView HeightRequest="1" BackgroundColor="Gray" />
</StackLayout>
</ContentView>
Code-behind
public partial class HeaderView : ContentView
{
public HeaderView()
{
InitializeComponent();
}
public static readonly BindableProperty HeaderTypeProperty =
BindableProperty.Create(
nameof(HeaderType), typeof(string), typeof(HeaderView),
defaultValue: default(string));
public string HeaderType
{
get { return (string)GetValue(HeaderTypeProperty); }
set { SetValue(HeaderTypeProperty, value); }
}
}
Usage
<local:HeaderView HeaderType="1" />
I believe that what you are looking is a ControlTemplate here is an implementation.
App.xaml
<Application xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="SimpleTheme.App">
<Application.Resources>
<ResourceDictionary>
<ControlTemplate x:Key="Header1Template">
<StackLayout>
<!-- code -->
</StackLayout>
</ControlTemplate>
<ControlTemplate x:Key="Header2Template">
<StackLayout>
<!-- code -->
</StackLayout>
</ControlTemplate>
</ResourceDictionary>
</Application.Resources>
</Application>
Usage (This will make header1 your default, you can pick any of the both)
<ContentView x:Name="contentView" ControlTemplate="{StaticResource Header1Template}">
Now there are many ways to change the header, either by defining as the default code above or with an action which can be a button click like the example given on the link or with a propertyChange event, if you want to depend on a property to choose the header like:
public int HeaderNumber { get; set; }
protected override void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
base.OnPropertyChanged(propertyName);
if (propertyName == "HeaderNumber")
{
contentView.ControlTemplate = (HeaderNumber == 1) ? Header1Template : Header2Template;
}
}
Note that i never implemented a ControlTemplate but i think this solves your problem.

How can I link a switch and a visible property by binding with a ViewModel?

I have a switch and a label on my XAML page:
<Switch x:Name="CpSwitch" Toggled="CpSwitch" />
<ViewCell x:Name="aBtn" Tapped="openPicker">
<Grid VerticalOptions="CenterAndExpand" Padding="20, 0">
<Label Text="Don't Know" />
<Picker x:Name="aBtnPicker" SelectedIndexChanged="aBtnPickerSelectedIndexChanged" ItemsSource="{Binding Points}">
</Picker>
<Label Text="{Binding ABtnLabel}"/>
</Grid>
</ViewCell>
My ViewModel
public class SettingsViewModel: ObservableProperty
{
public SettingsViewModel()
{
}
What I would like to do is to use a ViewModel and in that view model have the switched state either make the ViewCell visible or not.
Can someone give me some suggestions on how I can bind the switch and the visible property for the ViewCell using a ViewModel.
Basically you bind them both to the same Boolean variable in your view model.
<Switch IsToggled="{Binding ShowViewCell}" />
<ViewCell IsVisible="{Binding ShowViewCell}">
...
</ViewCell>
And in your view model:
public class SettingsViewModel
{
public bool ShowViewCell {get;set;}
}
This ensures that when the Switch is turned ON the ViewCell becomes visible. If you want to achieve the opposite you could write a custom converter:
public class InverseBoolConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return !(bool)value;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return !(bool)value;
}
}
Add it to your App.xaml resource dictionary:
<Application.Resources>
<ResourceDictionary>
<converters:InverseBoolConverter x:Key="InverseBoolConverter" />
</ResourceDictionary>
</Application.Resources>
And apply that to the ViewCell:
<Switch IsToggled="{Binding ShowViewCell, Converter={StaticResource InverseBoolConverter}}" />

How can I add string to binding value of image source?

Hi everyone I'm developing a xamarin app and I have a problem.
I have the next code in xaml:
<ListView x:Name="listName">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical">
<Image Source="{Binding imageName}"></Image>
<Label Text="{Binding name}" TextColor="Black"></Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
I need to change the image source property adding a string. For example:
<Image Source="{Binding imageName} + mystring.png"></Image>
<Image Source="{Binding imageName + mystring.png}"></Image>
Can I do this in xaml?
any idea? is possible?
You could use a converter to do this.
public class ImagePostfixValueConverter
: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var path = value as string;
var postfix = parameter as string;
if (string.IsNullOrEmpty(postfix) || string.IsNullOrEmpty(path))
return value;
return path + postfix;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
Then on your page add:
<ContentPage.Resources>
<ResourceDictionary>
<local:ImagePostfixValueConverter x:Key="PostfixConverter"/>
</ResourceDictionary>
</ContentPage.Resources>
And then in your binding:
<Image Source="{Binding imageName, Converter={StaticResouce PostfixConverter}, ConverterParameter=mystring.png}"></Image>
You could do this in a much simpler way:
<Image Source="{Binding imageName, StringFormat='{0}mystring.png'}"></Image>

How to access ToggleButton content in a storyboard animation

I have a toggle button in UI and would like to perform some action when it is clicked.
xaml for this button:
<ToggleButton x:Name="markToggle" Click="markToggle_Click" Style="{StaticResource StyleMarkToggle}" >
<ToggleButton.Content>
<StackPanel x:Name="markToggle_Button" Orientation="Horizontal">
<Image x:Name="Icon" Source="{Binding MarkToggleIcon}" Stretch="None" />
<TextBlock x:Name="Counter" TextWrapping="Wrap" Text="{Binding ButtonText}" Margin="6,0,0,0" />
</StackPanel>
</ToggleButton.Content>
</ToggleButton>
When this toggleButton is clicked, I want to change the Foreground color of textblock (name: Counter).
How do I access this textbox element from Storyboard.Target (or TargetName)?
Here is the xaml code for 'StyleMarkToggle' style.
(Only visual state 'checked' is shown in the below code.)
<VisualState x:Name="Checked">
<Storyboard>
<ColorAnimation Duration="0" To="White" Storyboard.TargetName="{Binding ElementName=Counter}" Storyboard.TargetProperty="(Control.Foreground).(SolidColorBrush.Color)" />
</Storyboard>
It shows this runtime error:
MS.Internal.WrappedException: Cannot resolve TargetName System.Windows.Controls.TextBlock. ---> System.InvalidOperationException: Cannot resolve TargetName System.Windows.Controls.TextBlock.
at MS.Internal.XcpImports.VisualStateManager_GoToStateInternal(Control reference, FrameworkElement root, VisualStateGroup group, VisualState state, Boolean useTransitions, Boolean& refreshInheritanceContext)
at System.Windows.VisualStateManager.RetryGoToStateAfterRefreshingInheritanceContext(Control control, FrameworkElement templateRoot, VisualStateGroup group, VisualState state, Boolean useTransitions)
at System.Windows.VisualStateManager.GoToState(Control control, String stateName, Boolean useTransitions)
at System.Windows.Controls.Primitives.ToggleButton.ChangeVisualState(Boolean useTransitions)
at System.Windows.Controls.Primitives.ToggleButton.OnApplyTemplate()
at System.Windows.FrameworkElement.OnApplyTemplate(IntPtr nativeTarget)
Instead of using a Storyboard, you could just bind the Foreground property of the TextBlock element to the IsChecked property of the ToggleButton element and use a Converter to convert from bool to SolidColorBrush.
Here is the modified XAML:
<ToggleButton x:Name="markToggle" Click="markToggle_Click" Style="{StaticResource StyleMarkToggle}" >
<ToggleButton.Content>
<StackPanel x:Name="markToggle_Button" Orientation="Horizontal">
<Image x:Name="Icon" Source="{Binding MarkToggleIcon}" Stretch="None" />
<TextBlock Foreground="{Binding ElementName=markToggle, Path=IsChecked, Converter={StaticResource BoolToColorConverter}}" x:Name="Counter" TextWrapping="Wrap" Text="{Binding ButtonText}" Margin="6,0,0,0" />
</StackPanel>
</ToggleButton.Content>
</ToggleButton>
And the code for the converter:
public class BoolToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if ((bool)value)
return new SolidColorBrush(Colors.White);
else
return new SolidColorBrush(Colors.Black);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}

Resources