Xamarin : Reverse the bool value in xamarin listview viewcell - xamarin

I am new with xamarin, I am facing an issue in my xamarin forms project.
I have two images inside listview-viewcell, one is for showing like and other for unlike.
If the value of isUserLiked is true show the second image and otherwise show the first one. I try with !, but not working.
Here is my code:
<StackLayout HeightRequest="500">
<ListView x:Name="ListView1" RowHeight="500" HorizontalOptions="CenterAndExpand">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout
Orientation="Vertical">
<Image
Source="unlike.png"
IsVisible="{Binding !isUserLiked}">
</Image>
<Image
Source="like.png"
IsVisible="{Binding isUserLiked}">
</Image>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
How to reverse the binding value inside listview-viewcell.
Anybody please suggest a solution with working code.
Thanks in advance.

You can't use operator in binding.
Best way to use trigger for that like this :
<Image Source="unlike.png">
<Image.Triggers>
<DataTrigger TargetType="Image" Binding="{Binding isUserLiked}" Value="True">
<Setter Property="Source" Value="like.png" />
</DataTrigger>
</Image.Triggers>
</Image>
Or
Another way to use converter like this:
Converter
public class InverseBoolConverter : IValueConverter, IMarkupExtension
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return !((bool)value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
//throw new NotImplementedException();
}
public object ProvideValue(IServiceProvider serviceProvider)
{
return this;
}
}
Now you can use in your Xaml code like this :
<Image Source="unlike.png" IsVisible="{Binding isUserLiked, Converter={Helpers:InverseBoolConverter}}" />
Don't forgot to add assembly in xaml header :
xmlns:Helpers="clr-namespace:MyNameSpace.Helpers"

You could either work around it by creating another property in your ViewModel which gives you the inverse value.
A better way would be to use a ValueConverter, I have written a blog about it here. In the converter you could inverse the boolean value.
However there are also libraries that do that for you, they basically implement this converter for you.

Related

Xamarin.Forms setting Image Source in shared project

I have a Xamarin.Forms solution with Android and iOS projects.
I want the same images to be in both applications.
I know I can add the same image to both projects code seperately, but that doesn't sound very elegant.
I want to have only one image referred to by both projects, from the code in the shared project.
My images are in the shared project, and are Embedded Resource
SharedProject
|
- Resources
|
- icon192.png
My XAML (shared project)
<ContentPage xmlns:myExtensions="clr-namespace:LibraryApp.Extensions;assembly=App">
<Image x:Name="image2"
VerticalOptions="Center" HeightRequest="50" Aspect="AspectFit" />
<Image Source="{myExtensions:ImageResourceExtension Resources.icon192.png}"
VerticalOptions="Center" HeightRequest="50" Aspect="AspectFit" />
</ContentPage>
My code-behind (shared project)
public partial class AboutPage : ContentPage
{
public AboutPage()
{
InitializeComponent();
image2.Source = ImageSource.FromResource("LibraryApp.AndroidClient.Resources.icon192.png");
}
}
The first Image, image2 works perfectly when set from code-behind.
But I don't want to have to deal with images in code-behind, because they are a presentation detail, not implementation.
So my extension method to try and solve this
namespace LibraryApp.Extensions
{
[Preserve(AllMembers = true)]
[ContentProperty(nameof(Source))]
public class ImageResourceExtension : IMarkupExtension
{
public string Source { get; set; }
public object ProvideValue(IServiceProvider serviceProvider)
{
if (Source == null) return null;
// do some work to get the name of the correct assembly/project, here
// var stream = Assembly.GetCallingAssembly().GetManifestResourceStream(Source);
// just for testing, return something we know that works
return ImageSource.FromResource("LibraryApp.AndroidClient.Resources.icon192.png");
}
}
}
This extension method does get called from the other image (with the correct Source parameter).
Even though the code is effectively exactly the same, I cannot get an image using the extension method.
I get a FileImageSourceHandler: Could not find image or image file was invalid.
How would you solve this problem?
Typical, after spending literally a day on this, I figure it out 20m after asking the question...
One solution is to add an IValueConverter
public class ResourceConverter : IValueConverter
{
public Object Convert(Object value, Type targetType, Object parameter, System.Globalization.CultureInfo culture)
{
if (value is not String filename) throw new ArgumentNullException(nameof(value));
var nameOfAssembly = Assembly.GetExecutingAssembly().GetName().Name;
return ImageSource.FromResource($"{nameOfAssembly}.{filename}");
}
public Object ConvertBack(Object value, Type targetType, Object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
And then in the XAML
<ContentPage xmlns:converter="clr-namespace:GPS4Flight.Converter">
<ContentPage.Resources>
<converter:ResourceConverter x:Key="ResourceConverter" />
</ContentPage.Resources>
<Image Source="{Binding Source=Resources.icon192.png, Converter={StaticResource ResourceConverter}}"
VerticalOptions="Center" HeightRequest="50" Aspect="AspectFit" />
</ContentPage>
Notice I needed {Binding Source=[nameOfFile] for it to work.

How can I add a TextAlignment binding to a template with Xamarin Forms?

I have this XAML code:
<?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:t="clr-namespace:Templates"
x:Class="Japanese..EntryGrid"
x:Name="this" >
<t:HeaderTemplate Text="ABC" />
<Entry HorizontalOptions="FillAndExpand"
VerticalTextAlignment="{Binding EntryTextAlignment, Converter={StaticResource stringToTextAligmentConverter}, Source={x:Reference this}}"
VerticalOptions="FillAndExpand"
Text="{Binding EntryText, Source={x:Reference this}}" />
</StackLayout>
and this C# code:
public partial class EntryGrid : StackLayout
{
public EntryGrid()
{
InitializeComponent();
}
public static readonly BindableProperty
EntryTextAlignmentProperty =
BindableProperty.Create(nameof(EntryText),
typeof(TextAlignment),
typeof(EntryGrid),
TextAlignment.Center,
BindingMode.TwoWay);
public string EntryTextAlignment {
get => (string)GetValue(EntryTextAlignmentProperty);
set => SetValue(EntryTextAlignmentProperty, value); }
}
There's is more code in the template but I just included what was being used for this question.
What I am trying to do is to set the value of the EntryTextAlignment to "Center" like this:
<t:EntryGrid EntryTextAlignment="Start"
HeightRequest="150" />
But it's not taking my request and the text is vertically aligned in the Center.
Does anyone have any idea what I might be doing wrong?
TextAligment Property is Enum, TextAlignment. To bind it correctly you will need to change it from String to TextAlignment. You can also use string, but I think you will have to use converter to handle it. Something like this:
class StringToTextAligmentConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var aligment = (string)value;
switch (aligment)
{
case "Start":
return TextAlignment.Start;
case "Center":
return TextAlignment.Center;
case "End":
return TextAlignment.End;
default:
return TextAlignment.Start;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return TextAlignment.Start;
}
}
xmlns:local="clr-namespace:YourApp.YourNameSpace.Converters"
In your ContentPage declarations in Xaml, then:
<ContentPage.Resources>
<ResourceDictionary>
<local:StringToTextAligmentConverterx:Key="stringToTextAligmentConverter" />
</ResourceDictionary>
</ContentPage.Resources>
Then call it in your Xaml like this:
<Entry VerticalTextAlignment="{Binding EntryTextAlignment, Converter={StaticResource stringToTextAligmentConverter}}"/>

Can I disable a Button in my Xamarin ContentView custom control using a BindableProperty?

I want to be able to disable a button in a custom header, a ContentView, in my Xamarin.Forms app so that I can warn users that they have unsaved data before navigating away from the ContentPage which contains it.
I have created a BindableProperty for the HeaderView which my containing page binds its view model. I was inspired by this article: Add Custom Controls with Binding Properties to Your Xamarin.Forms App
The XAML for the ContentView is here
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
x:Class="HeaderView"
x:Name="HeaderRoot">
<Grid RowSpacing="0">
<Grid.RowDefinitions>
<RowDefinition Height="80"/>
<RowDefinition Height="5"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Padding="20,20,20,0" ColumnSpacing="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="1.3*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackLayout Grid.Column="0">
<FlexLayout HorizontalOptions="CenterAndExpand" VerticalOptions="Center">
<ImageButton x:Name="BackButton" TranslationY="-20" TranslationX="-20" BackgroundColor="Black" HeightRequest="80" WidthRequest="80" Source="angleleft.png" Padding="0,0,0,0" Margin="0,0,0,0" Clicked="OnBackClicked" IsEnabled="{Binding Source={x:Reference HeaderRoot}, Path=BindingContext.IsEnabled}" />
</FlexLayout>
</StackLayout>
<FlexLayout Grid.Column="1" JustifyContent="Center" AlignItems="Center">
<FlexLayout.TranslationY>
<OnPlatform x:TypeArguments="x:Double">
<On Platform="Android" Value="-15"/>
<On Platform="iOS" Value="0"/>
</OnPlatform>
</FlexLayout.TranslationY>
<Label x:Name="TitleLabel" FontFamily="{StaticResource TitleFont}" TextColor="White" FontSize="50" />
</FlexLayout>
</Grid>
</Grid>
</ContentView>
The related code behind for the ContentView is here
public partial class HeaderView : ContentView
{
public HeaderView()
{
InitializeComponent();
if (DesignMode.IsDesignModeEnabled)
return; // Avoid rendering exception in the editor.
//Argument is null because the Header does not need navigation information.
BindingContext = new HeaderViewModel(null);
}
public static BindableProperty CanNavigateProperty =
BindableProperty.Create(
propertyName: "CanNavigate",
returnType: typeof(bool),
declaringType: typeof(HeaderView),
defaultValue: true,
defaultBindingMode: BindingMode.TwoWay,
propertyChanged: HandleCanNavigatePropertyChanged);
private static void HandleCanNavigatePropertyChanged(
BindableObject bindable,
object oldValue,
object newValue)
{
HeaderView targetView = (HeaderView)bindable;
if (targetView != null)
targetView.BackButton.IsEnabled = (bool)newValue;
}
//...
public bool CanNavigate
{
get
{
return (bool)base.GetValue(CanNavigateProperty);
}
set
{
if(this.CanNavigate != value)
{
base.SetValue(CanNavigateProperty, value);
}
}
}
protected void OnBackClicked(object sender, EventArgs e)
{
if (CanNavigate)
{
this.Navigation.PopAsync();
}
}
}
Edit .
The view model is quite simple.
public class HeaderViewModel : ViewModelBase
{
public HeaderViewModel(INavigationService navigationService)
: base(navigationService)
{
UserDTO dto = (Prism.PrismApplicationBase.Current as App).CurrentUser;
UserName = string.Format("{0} {1}", dto.FirstName, dto.LastName);
}
private string userName;
public string UserName
{
get
{
return userName;
}
set
{
SetProperty<string>(ref userName, value);
}
}
}
I then have the following markup in the containing page
<mdaViews:HeaderView CanNavigate="{Binding IsSaved}" Title="COLLECTIONS" Grid.Row="0" />
I have verified that the property, IsSaved, does change its value. The binding works when used in the Text of a Label.
When I change the value of the IsSaved property, the Label will change from "true" to "false" as expected. However this same binding does not appear to change the CanNavigate value in my custom header. While debugging, the OnBackClicked handler always shows a value of CanNavigate == true. The propertyChanged event,HandleCanNavigatePropertyChanged, is never entered. If I am supposed to explicitly call this, I don't know how. If I am missing something or using the wrong approach, I would like to know.
Edit Some of the postings in SO seem to suggest that having a BindingContext set to a view model, might be part of the problem.
Here is such an example:
BindableProperty in ContentView not working. I'm not sure what I should do about the view model, as it is working as expected.
I found the answer in the comment, "The page using the ContentView sets its BindingContext which will then be used for all child elements, including the custom ContentView. If you set the context in the content view again, you will overwrite it." by #Krumelur, who was responding to this answer https://stackoverflow.com/a/39989721/117995.
When I set the BindingContext in my ContentView, I overwrote all that was available from the parent page. To fix this problem I moved all ViewModel processing to the code behind and removed the code where I set the BindingContext.

Xamarin Forms: Prism -- Issue with using unity IoC?

In my xamarin forms app using Prism MVVM Framework, How should I use IoC containers?
There are no
Container.Resolve<Interface,Implentation>
I think we need to resolve the IoC and initialize in app.xaml.cs. But I am not able to find proper way to do it.
Can anyone help me out ?
UPDATE:
PAGE:
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="CoC.Core.Views.MenuPage">
<ContentPage.Content>
<ListView x:Name="info"
SeparatorVisibility="None"
SeparatorColor="Transparent"
HasUnevenRows="false"
RowHeight="50" BackgroundColor="#485366" >
<ListView.ItemTemplate>
<DataTemplate>
<!--<ImageCell Text="{Binding Title}" ImageSource="{Binding IconSource}" /> -->
<ViewCell>
<!--<Label Text="{Binding Title}" />-->
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>
ViewModel:
using Prism.Commands;
using Prism.Mvvm;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections.ObjectModel;
using Prism.Navigation;
namespace CoC.Core.ViewModels
{
public class MenuPageViewModel : BindableBase , INavigationAware
{
IMenuList _menuList;
public ObservableCollection<MenuItem> ConductList { get; set; }
public void OnNavigatedFrom(NavigationParameters parameters)
{
//throw new NotImplementedException();
}
public void OnNavigatedTo(NavigationParameters parameters)
{
//throw new NotImplementedException();
}
public MenuPageViewModel(IMenuList MenuList)
{
_menuList = MenuList;
ConductList = _menuList.GetMenuList();
}
}
}
APP:
using Prism.Unity;
using CoC.Core.Views;
namespace CoC.Core
{
public partial class App : PrismApplication
{
protected override void OnInitialized()
{
InitializeComponent();
//NavigationService.NavigateAsync("MainPage?title=Hello%20from%20Xamarin.Forms");
NavigationService.NavigateAsync("MenuPage");
}
protected override void RegisterTypes()
{
Container.RegisterTypeForNavigation<MainPage>();
Container.RegisterTypeForNavigation<RootPage>();
Container.RegisterTypeForNavigation<MenuPage>();
}
}
}
When I run this code, I am getting error in Main.cs of IOS pro
Error:
Foundation.MonoTouchException
NSInternalLnconistencyException Reason: Application window are expected to have a root view controller at the end of the application.
What you are looking for is an extension method in Unity. You need to add the using Microsoft.Practices.Unity namespace to your code file. The you can register your services using Container.Register<IService,MyService>().

How to customise wp7 toolkit ToggleSwitch

I have the WP7 toolkit and am using the toggle switch.
At the moment it displays On or Off.
I know you can customise it using the the content template and the sample code supplied with the tookit shows just that, but it I can't find a way of changine On/Off to something else.
I want to display Yes and No.
I created my own value converter that was bound to the same boolean property on my view model as IsChecked. So on the view it looked like:
<toolkit:ToggleSwtich IsChecked="{Binding Completed}" Content="{Binding Completed, Converter={StaticResource YesNoConverter}" />
Hum since the "On" et "Off" strings come from a converter set in a private method in the source code, I don't see a lot of alternative : http://silverlight.codeplex.com/SourceControl/changeset/view/55144#1325068
Change the source code to have something more flexible ?
There is a much easier way, set the content to No and then create a event handler for each toggle to make it say Yes and then No:
private void ToggleSwitch_Checked(object sender, RoutedEventArgs e)
{
togButton.Content = "Yes";
}
private void ToggleSwitch_Unchecked(object sender, RoutedEventArgs e)
{
togButton.Content = "No";
}
I know that the question is quite old, but I think that this answer may be useful because there's no need to recompile the control's code.
We can bind the Content to the IsChecked property and use a Converter that returns our custom string.
This is what I did for my project:
<toolkit:ToggleSwitch SwitchForeground="{StaticResource PhoneAccentBrush}"
Grid.Row="3" Grid.Column="1"
Header="{Binding Path=LocalizedResources.MyLabel, Source={StaticResource LocalizedStrings}}"
Content="{Binding IsChecked, Converter={StaticResource SwitchOnOffConverter}, RelativeSource={RelativeSource Self}}"/>
where the SwitchOnOffConverter is this one:
public class SwitchOnOffConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return ((bool) value) ? AppResources.YesText : AppResources.NoText;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}

Resources