Binding IsVisible property to my VIewModel - xamarin

I have some entry, and on focus of that entry I want to show my cancel button.
Here is the xaml:
<RelativeLayout>
<controls:StandardEntry
x:Name="mainEntry"
BackgroundColor="White"
BorderColor="Gray"
BorderThickness="0"
CornerRadius="15"
Placeholder="Search..."
TextColor="LightGray"
HeightRequest="10"
Padding="35,0"
FontSize="Default"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent, Property=Height,Factor=0,Constant=40}"
RelativeLayout.WidthConstraint="{ConstraintExpression Type=RelativeToParent,Property=Width,Factor=0.7,Constant=0}">
<Entry.Behaviors>
<behavior:EventToCommandBehavior EventName="Focused" Command="{Binding SearchBarFocusedCommand}"/>
<behavior:EventToCommandBehavior EventName="Unfocused" Command="{Binding SearchBarUnfocusedCommand}"/>
</Entry.Behaviors>
</controls:StandardEntry>
<Image
Source="Invest_Search_Icon.png"
VerticalOptions="Center"
RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToView,ElementName=mainEntry, Property=X,Factor=1,Constant=10}"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToView, ElementName=mainEntry, Property=Y,Factor=1,Constant=10}"/>
<Image
Source="Invest_Search_Icon.png"
VerticalOptions="Center"
RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToView,ElementName=mainEntry, Property=Width,Factor=1,Constant=-25}"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToView, ElementName=mainEntry, Property=Y,Factor=1,Constant=10}"/>
<Button
Text="Cancel"
TextColor="Gray"
IsVisible="{Binding CancelButtonIsVisible}"
BackgroundColor="White"
VerticalOptions="Start"
CornerRadius="10"
HeightRequest="40"
Margin="0,0,50,0"
RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToView,ElementName=mainEntry, Property=Width,Factor=1,Constant=20}"
RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToView, ElementName=mainEntry, Property=Y,Factor=1,Constant=0}"/>
</RelativeLayout>
As you see Imusing EventToCommand Behavior, which is work ok (it enters into my command methods). In my VM:
public class InvestViewModel : BaseViewModel, INotifyPropertyChanged
{
public InvestViewModel()
{
SetDefaultContent();
SearchBarFocusedCommand = new Command(() => OnSearchBarFocused());
}
private void OnSearchBarUnfocused()
{
CancelButtonIsVisible = false;
}
private void OnSearchBarFocused()
{
CancelButtonIsVisible = false;
}
private void SetDefaultContent()
{
CancelButtonIsVisible = true;
}
private bool cancelButtonIsVisible;
public bool CancelButtonIsVisible
{
get => cancelButtonIsVisible;
set
{
cancelButtonIsVisible = value;
RaisePropertyChanged(() => CancelButtonIsVisible);
}
}
public ICommand CancelClickCommand { get; set; }
public ICommand SearchBarFocusedCommand { get; set; }
}
So the flow:
On page loading, first SetDefaultContent() => CancelButtonIsVisible = true;
On Entry focused, hide cancel button OnSearchBarFocused() => CancelButtonIsVisible = false;
Apparently, SetDefaultContent is working.
It doesnt work my focus method, when I focused nothing happens, there is still visible cancel button.
Any suggestions?

Option-1
The simpler option would be to bind IsVisible property on 'Cancel' button to IsFocused property on entry (mainEntry) control using named reference.
<Button Text="Cancel"
IsVisible="{Binding IsFocused, Source={x:Reference mainEntry},
Converter={StaticResource NegateBooleanConverter}}" />
You can implement a converter that will negate the boolean value for you. Alternatively you can use triggers as well.
public class NegateBooleanConverter : 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;
}
}
Option-2
Another option is to bind IsFocused with a view-model property.
Create a property with setter in viewmodel
public bool IsEntryFocused
{
set
{
CancelButtonIsVisible = !value;
}
}
and set up the binding in view
<controls:StandardEntry
x:Name="mainEntry"
...
IsFocused="{Binding IsEntryFocused}"

Related

.Net maui: How to reference a color in a binding?

So I have a label and I want to set the text colour from a mvvm variable.
VM
[ObservableProperty]
private string col = "White";
XAML
<Label Text="{Binding Name}"
FontSize="20"
TextColor="{Binding Col}">
So in general TextColor="White" works fine
I've tried using the Color object https://learn.microsoft.com/en-us/dotnet/maui/user-interface/graphics/colors
e.g.
[ObservableProperty]
private Color col = Colors.White;
but I can't get it to work.
I had hoped that a simple string would work...oh for my vain hopes...
Thanks, G.
Edit: I should add that my label is in a CollectionView?
BIG EDIT:
IT WORKS for a standalone label
i.e.
[ObservableProperty]
private Color col = Colors.White;
So the issue is if the label is in a CollectionView. I wonder why?
EDIT: Because the CollectionView is bound to the ItemsSource - doh what a dummy!
If you want to bind color(which type is string) to your view, you can use Binding value converters to achieve this.
I created a demo to achieve this , you can refer to the following code:
MyModel.cs
public class MyModel: INotifyPropertyChanged
{
string name;
public string Name
{
set
{
if (name != value)
{
name = value;
OnPropertyChanged("Name");
}
}
get
{
return name;
}
}
string _value;
public string Value
{
set
{
if (_value != value)
{
_value = value;
OnPropertyChanged("Value");
}
}
get
{
return _value;
}
}
private string _textColor = "Green";
public string TextColor
{
get { return _textColor; }
set
{
_textColor = value;
OnPropertyChanged("TextColor");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
MyViewModel.cs
public class MyViewModel
{
public ObservableCollection<MyModel> dataList { get; set; }
public ICommand ColorChangeCommand { protected set; get; }
public MyViewModel()
{
dataList = new ObservableCollection<MyModel>();
dataList.Add(new MyModel() { Name = "test1", Value = "1" });
dataList.Add(new MyModel() { Name = "test2", Value = "2" });
dataList.Add(new MyModel() { Name = "test3", Value = "3" });
ColorChangeCommand = new Command<MyModel>(async (key) =>
{
key.TextColor = "Red";
});
}
}
StringToColorConverter.cs
public class StringToColorConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var color = value.ToString();
switch (color)
{
case "Green":
return Colors.Green;
case "Red":
return Colors.Red;
default:
return Colors.Yellow;
}
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return null;
}
}
A usage:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MauiApp0706.Tab1"
xmlns:local="clr-namespace:MauiApp0706"
Title="Tab1">
<ContentPage.Resources>
<ResourceDictionary>
<local:StringToColorConverter x:Key="ColorConverter" />
</ResourceDictionary>
</ContentPage.Resources>
<VerticalStackLayout>
<CollectionView
ItemsSource="{Binding dataList}"
x:Name="mylistview"
>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid Column="0">
<Label Text="{Binding Name}" TextColor="{Binding TextColor, Converter = {StaticResource ColorConverter}}"/>
</Grid>
<Grid Column="1">
<Label Text="{Binding Value}" TextColor="{Binding TextColor, Converter = {StaticResource ColorConverter}}"/>
</Grid>
<Grid Column="2">
<Button Text="change" Command="{Binding BindingContext.ColorChangeCommand, Source={x:Reference Name=mylistview} }" CommandParameter="{Binding .}"></Button>
</Grid>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</VerticalStackLayout>
</ContentPage>
Not to detract from the answer already given, but a couple of points to note for people running into this...
"Color col = Colors.White" - Color and Colors aren't the same thing, and within "Color" there is System.Drawing.Color and Microsoft.Maui.Graphics.Color, so be careful you're not accidentally mixing types.
If you do your UI in C# rather than XAML, then you can just bind directly to a Color to begin with and get rid of all the string-converting.

How can I pass a command to a template and have it execute in my back end code and pass the parameter?

I have this template:
<?xml version="1.0" encoding="utf-8"?>
<Grid Padding="20,0" 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.DataGridTemplate"
x:Name="this" HeightRequest="49" Margin="0">
<Grid.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding TapCommand, Source={x:Reference this}}"
CommandParameter="1"
NumberOfTapsRequired="1" />
</Grid.GestureRecognizers>
<Label Grid.Column="0" Text="{Binding Test" />
</Grid>
Behind this I have:
public partial class DataGridTemplate : Grid
{
public DataGridTemplate()
{
InitializeComponent();
}
public static readonly BindableProperty TapCommandProperty =
BindableProperty.Create(
"Command",
typeof(ICommand),
typeof(DataGridTemplate),
null);
public ICommand TapCommand
{
get { return (ICommand)GetValue(TapCommandProperty); }
set { SetValue(TapCommandProperty, value); }
}
}
and I am trying to call the template like this in file: Settings.xaml.cs
<template:DataGridTemplate TapCommand="openCFSPage" />
hoping that it will call my method here in file: Settings.cs
void openCFSPage(object sender, EventArgs e)
{
Navigation.PushAsync(new CFSPage());
}
The code compiles but when I click on the grid it doesn't call the openCFSPage method.
1) Does anyone have an idea what might be wrong?
2) Also is there a way that I can add a parameter to the template and then have that parameter passed to my method in the CS back end code?
Note that I would like to avoid adding a view model if possible. The application is small and I'd like to just have the code I need in the CS code of the page that calls the template.
Please note that the simplest way to implement this would be through MVVM (i.e. a view-model), but if you want to side-step this option (as you mentioned in the question) then you can use one of the following options
Option1 : Wrap delegate into command object
If you look at it from the perspective of a XAML parser, you are technically trying to assign a delegate to a property of type ICommand. One way to avoid the type mismatch would be to wrap the delegate inside a command-property in the page's code-behind.
Code-behind [Settings.xaml.cs]
ICommand _openCFSPageCmd;
public ICommand OpenCFSPageCommand {
get {
return _openCFSPageCmd ?? (_openCFSPageCmd = new Command(OpenCFSPage));
}
}
void OpenCFSPage(object param)
{
Console.WriteLine($"Control was tapped with parameter: {param}");
}
XAML [Settings.xaml]
<!-- assuming that you have added x:Name="_parent" in root tag -->
<local:DataGridView TapCommand="{Binding OpenCFSPageCommand, Source={x:Reference _parent}}" />
Option2 : Custom markup-extension
Another option (a bit less mainstream) is to create a markup-extension that wraps the delegate into a command object.
[ContentProperty("Handler")]
public class ToCommandExtension : IMarkupExtension
{
public string Handler { get; set; }
public object Source { get; set; }
public object ProvideValue(IServiceProvider serviceProvider)
{
if (serviceProvider == null)
throw new ArgumentNullException(nameof(serviceProvider));
var lineInfo = (serviceProvider?.GetService(typeof(IXmlLineInfoProvider)) as IXmlLineInfoProvider)?.XmlLineInfo ?? new XmlLineInfo();
object rootObj = Source;
if (rootObj == null)
{
var rootProvider = serviceProvider.GetService<IRootObjectProvider>();
if (rootProvider != null)
rootObj = rootProvider.RootObject;
}
if(rootObj == null)
{
var valueProvider = serviceProvider.GetService<IProvideValueTarget>();
if (valueProvider == null)
throw new ArgumentException("serviceProvider does not provide an IProvideValueTarget");
//we assume valueProvider also implements IProvideParentValues
var propInfo = valueProvider.GetType()
.GetProperty("Xamarin.Forms.Xaml.IProvideParentValues.ParentObjects",
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
if(propInfo == null)
throw new ArgumentException("valueProvider does not provide an ParentObjects");
var parentObjects = propInfo.GetValue(valueProvider) as IEnumerable<object>;
rootObj = parentObjects?.LastOrDefault();
}
if(rootObj != null)
{
var delegateInfo = rootObj.GetType().GetMethod(Handler,
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
if(delegateInfo != null)
{
var handler = Delegate.CreateDelegate(typeof(Action<object>), rootObj, delegateInfo) as Action<object>;
return new Command((param) => handler(param));
}
}
throw new XamlParseException($"Can not find the delegate referenced by `{Handler}` on `{Source?.GetType()}`", lineInfo);
}
}
Sample usage
<local:DataGridView TapCommand="{local:ToCommand OpenCFSPage}" />
You have 2 options depending on the the use case :
FYI, there's no way to call another method directly from the view (its a bad design pattern to do so)
Using Event Aggregator :
Create interface
public interface IEventAggregator
{
TEventType GetEvent<TEventType>() where TEventType : EventBase, new();
}
All you have to do is call it from you TapCommand
_eventAggregator.GetEvent<ItemSelectedEvent>().Publish(_selectedItem);
Then in your Settings.cs you can Create a method that can receive the data
this.DataContext = new ListViewModel(ApplicationService.Instance.EventAggregator);
Inheritance and Polymorphism / Making openCFSPage a service :
Creating a interface / service that links both models
public interface IOpenCFSPage
{
Task OpenPage();
}
and a method :
public class OpenCFSPage : IOpenCFSPage
{
private INavigationService _navigationService;
public OpenCFSPage(INavigationService navigationService){
_navigationService = navigationService;
}
public async Task OpenPage()
{
await _navigationService.NavigateAsync(new CFSPage());
}
}
Settings.xaml:
<template:DataGridTemplate TapCommand="{Binding OpenCFSPage}" />
<!-- Uncomment below and corresponding parameter property code in DataGridTemplate.xaml.cs to pass parameter from Settings.xaml -->
<!--<template:DataGridTemplate TapCommand="{Binding OpenCFSPage}" CommandParameter="A" />-->
Settings.xaml.cs:
public Settings()
{
InitializeComponent();
OpenCFSPage = new Command(p => OpenCFSPageExecute(p));
BindingContext = this;
}
public ICommand OpenCFSPage { get; private set; }
void OpenCFSPageExecute(object p)
{
var s = p as string;
Debug.WriteLine($"OpenCFSPage:{s}:");
}
DataGridTemplate.xaml:
<?xml version="1.0" encoding="UTF-8"?>
<Grid xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Japanese;assembly=Japanese"
Padding="0,20"
HeightRequest="49" Margin="0"
x:Class="Japanese.DataGridTemplate">
<Grid.GestureRecognizers>
<TapGestureRecognizer
Command="{Binding TapCommand}"
CommandParameter="1"
NumberOfTapsRequired="1" />
</Grid.GestureRecognizers>
<Label Grid.Column="0" Text="Test" />
</Grid>
DataGridTemplate.xaml.cs:
public partial class DataGridTemplate : Grid
{
public DataGridTemplate()
{
InitializeComponent();
}
public static readonly BindableProperty TapCommandProperty =
BindableProperty.Create(
nameof(TapCommand), typeof(ICommand), typeof(DataGridTemplate), null,
propertyChanged: OnCommandPropertyChanged);
public ICommand TapCommand
{
get { return (ICommand)GetValue(TapCommandProperty); }
set { SetValue(TapCommandProperty, value); }
}
//public static readonly BindableProperty CommandParameterProperty = BindableProperty.Create(
// nameof(CommandParameter), typeof(string), typeof(DataGridTemplate), null);
//public string CommandParameter
//{
// get { return (string)GetValue(CommandParameterProperty); }
// set { SetValue(CommandParameterProperty, value); }
//}
static TapGestureRecognizer GetTapGestureRecognizer(DataGridTemplate view)
{
var enumerator = view.GestureRecognizers.GetEnumerator();
while (enumerator.MoveNext())
{
var item = enumerator.Current;
if (item is TapGestureRecognizer) return item as TapGestureRecognizer;
}
return null;
}
static void OnCommandPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
if (bindable is DataGridTemplate view)
{
var tapGestureRecognizer = GetTapGestureRecognizer(view);
if (tapGestureRecognizer != null)
{
tapGestureRecognizer.Command = (ICommand)view.GetValue(TapCommandProperty);
//tapGestureRecognizer.CommandParameter = (string)view.GetValue(CommandParameterProperty);
}
}
}
}
Check this code you help you. Here you have to pass a reference of list view and also you need to bind a command with BindingContext.
<ListView ItemsSource="{Binding Sites}" x:Name="lstSale">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical">
<Label Text="{Binding FriendlyName}" />
<Button Text="{Binding Name}"
HorizontalOptions="Center"
VerticalOptions="Center"
Command="{Binding
Path=BindingContext.RoomClickCommand,
Source={x:Reference lstSale}}"
CommandParameter="{Binding .}" />
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Xamarin forms custom view dropdown is not working

I’m trying to make a custom drop-down in xamarin forms with some bindable properties. I’m having a label and a list view below the label using relative layout so that listview will always below the label and its IsVisible property will be toggled.
I have created a custom view as below:
Dropdown.xaml
<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CustomViewXam.CustomViews.Dropdown">
<StackLayout>
<RelativeLayout>
<Label x:Name="selectedLabel" TextColor="Red" Text="xcx"
BackgroundColor="Silver" FontSize="15"
HeightRequest="50"
RelativeLayout.WidthConstraint =
"{ConstraintExpression
Type=RelativeToParent,
Property=Width,
Factor=0.5,
Constant=0}"/>
<ListView x:Name="listView" BackgroundColor="Black"
RelativeLayout.WidthConstraint =
"{ConstraintExpression
Type=RelativeToView,
ElementName=selectedLabel,
Property=Width,
Factor=1,
Constant=0}"
RelativeLayout.YConstraint=
"{ConstraintExpression
Type=RelativeToView,
ElementName=selectedLabel,
Property=Height,
Factor=1,
Constant=0}"
>
</ListView>
</RelativeLayout>
</StackLayout>
</ContentView>
Dropdown.xaml.cs
public partial class Dropdown : ContentView
{
public Dropdown()
{
InitializeComponent();
BindingContext = this;
}
public string TitleText
{
get { return base.GetValue(TitleTextProperty).ToString(); }
set { base.SetValue(TitleTextProperty, value); }
}
private static BindableProperty TitleTextProperty = BindableProperty.Create(
propertyName: "TitleText",
returnType: typeof(string),
declaringType: typeof(string),
defaultValue: "",
defaultBindingMode: BindingMode.TwoWay,
propertyChanged: TitleTextPropertyChanged);
private static void TitleTextPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
var control = (Dropdown)bindable;
control.selectedLabel.Text = newValue.ToString();
}
public static readonly BindableProperty ItemsSourceProperty =
BindableProperty.Create<Dropdown, IEnumerable<object>>(p => p.ItemsSource,
null, BindingMode.OneWay, null, (bindable, oldValue, newValue) => { ((Dropdown)bindable).LoadItems(newValue); });
public IEnumerable<object> ItemsSource
{
get { return (IEnumerable<object>)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
}
public void LoadItems(IEnumerable<object> tiles)
{
try
{
var list = tiles;
}
catch (Exception e)
{ // can throw exceptions if binding upon disposal
}
}
}
And I'm using this custom view as below in my xaml page
<local:Dropdown TitleText="dssdasd" ItemsSource="{Binding TitleList}" />
Where TitleList is an ObservableCollection in ViewModel
private ObservableCollection<string> _titleList;
public ObservableCollection<string> TitleList
{
get
{
return _titleList;
}
set
{
if (_titleList!= value)
{
_titleList= value;
NotifyPropertyChanged("TitleList");
}
}
}
Problem:
The Text is visible on the UI and text is coming properly, but the listview below is empty and data is not coming. LoadItems method in Custom dropdown is not getting called even TitleList list is updated. Can anyone please guide me whats wrong i'm doing in the above code.
Note: I'm having BindingContext set to my view model in my view.
Note: I'm having BindingContext set to my view model in my view.
Do you mean you use the code in your Dropdown's construction method to set it?
public Dropdown()
{
InitializeComponent();
// Try to remove the code below
BindingContext = this;
}
I recommend you to set the BindingContext in your page xaml and remove this code in the view:
public MainPage()
{
InitializeComponent();
MyViewModel viewModel = new MyViewModel();
BindingContext = viewModel;
}
In this way the LoadItems() will fire at the first time we initialize the Dropdown's ItemsSource. But when you want to update the list, LoadItems() will not be called. because the instance of the list hasn't been changed. You can try to modify the method like:
public void LoadItems(IEnumerable<object> tiles)
{
try
{
var list = tiles;
listView.ItemsSource = tiles;
}
catch (Exception e)
{ // can throw exceptions if binding upon disposal
}
}
Moreover if you want to see the listView's data on the view, you should set its ItemTemplate for instance:
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Label Text="{Binding}"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>

Xamrin Button is not working for mmvm-light Relay Command

In my Xamrin forms code i have configured MVVM light
RelayCommand is not hitting even though I have set the property in xaml page for the command.
XAML
<?xml version="1.0" encoding="utf-8"?><ContentPage BackgroundColor="White" xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ContractorActionSolution.CSA.CSAContentPages.Demo">
<ContentPage.Padding>
<OnPlatform x:TypeArguments="Thickness">
<OnPlatform.iOS>0,20,0,0</OnPlatform.iOS>
</OnPlatform>
</ContentPage.Padding>
<ContentPage.Content>
<StackLayout Padding="10" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<Image x:Name="imgLogo" Source="sisystems_logo.jpg" HorizontalOptions="FillAndExpand" VerticalOptions="Start"/>
<StackLayout VerticalOptions="CenterAndExpand" Padding="20" HorizontalOptions="FillAndExpand">
<Label x:Name="lblmsg" TextColor="#F25B63" Text="Error Message"/>
<Entry x:Name="txtUserName" Placeholder="Email"/>
<Entry x:Name="txtPassword" Placeholder="Password" IsPassword="True"/>
<Button Command="{Binding IncrementCommand}" Text="Continue"/>
<Label Text="Can't Login ?" TextColor="#F25B63" HorizontalOptions="Center"/>
</StackLayout>
</StackLayout>
</ContentPage.Content>
Code
public partial class Demo : ContentPage
{
DemoViewModel _myViewModel;
public Demo ()
{
InitializeComponent ();
}
protected override void OnAppearing()
{
base.OnAppearing();
BindingContext = App.Locator.DemoVM;
_myViewModel = BindingContext as DemoViewModel;
}
protected override void OnDisappearing()
{
base.OnDisappearing();
_myViewModel.CleanUP();
}
}
Model
public class DemoViewModel : BaseViewModel
{
private string _name;
public string Name
{
get { return _name; }
set { Set(() => Name, ref _name, value); }
}
private RelayCommand _incrementCommand;
/// <summary>
/// Gets the IncrementCommand.
/// </summary>
public RelayCommand IncrementCommand
{
get
{
return _incrementCommand ?? (_incrementCommand = new RelayCommand(
() =>
{
}));
}
}
public void CleanUP()
{
Name = string.Empty;
}
}
I have also tried using RelayCommand and RelayAsyncCommand both are not working.
Property bindings are working fine,
I can add binding for entry with a string property, but not able to add RelayCommand with a button.
The problem might be the fact that the handler is empty and optimization actually omits it after compilation. Try to add anything inside, like Debug.WriteLine("Test"); to see if the breakpoint is hit then.

<ListView> of images - How open URL via TapGestureRecognizer?

How can we open a URL (via TapGestureRecognizer) that is bound to a of images?
My ListView's ItemsSource is _partners (type of List).
The Partner class has two properties - WebUrl and ImageUrl.
<ListView x:Name="partnersListView">
<ListView.ItemTemplate>
<DataTemplate>
<!--<ImageCell ImageSource="{Binding ImageUrl}" Text="{Binding WebUrl}" />-->
<ViewCell>
<Image Source="{Binding ImageUrl}">
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="Image_TapGestureRecognizer_Tapped" NumberOfTapsRequired="1" CommandParameter="{Binding .}" />
</Image.GestureRecognizers>
</Image>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
The list is showing images only, and I would like the app to navigate to a web site when the user taps the image. However, the following code - unsurprisingly - doesn't work.
private void Image_TapGestureRecognizer_Tapped(object sender, System.EventArgs e)
{
var tappedMenuItem = sender as MenuItem;
var partner = tappedMenuItem.CommandParameter as Partner;
Device.OpenUri(new Uri(partner.WebUrl));
}
Any suggestions, please?
Please note:
I want to keep the TapGestureRecognizer in XAML - not in the code-behind.
Thank you.
Best way to tackle this problem, is to bind the SelectedItem of the ListView to a property on your page.
So in your View
<ListView SelectedItem="{Binding SelectedPartner}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Image Source="{Binding ImageUrl}" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
And in the Code Behind
public Partner SelectedPartner
{
set
{
if (value != null)
Device.OpenUri(new System.Uri(value.WebUrl));
}
}
The list is showing images only, and I would like the app to navigate to a web site when the user taps the image. However, the following code - unsurprisingly - doesn't work.
CommandParameter is just for Command, you can't get it from a tap event handler. To accomplish your requirement, you can either use TappedCallback together with TappedCallbackParameter or use Command together with CommandParameter:
Method 1(TapppedCallback and TappedCallbackParameter):
Modify your Partner class to hold an Action<View,object>:
public class Partner
{
public Partner(string weburl, string imageurl)
{
this.WebUrl = weburl;
this.ImageUrl = imageurl;
}
public Partner(string weburl, string imageurl, Action<View, object> callback) : this(weburl, imageurl)
{
this.CallBack = callback;
}
public Action<View, object> CallBack { get; set; }
public string WebUrl { get; set; }
public string ImageUrl { get; set; }
}
In your code-behind, define a TappedCallback function like below and initialize the items source with the this function:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
List<Partner> list = new List<Partner>
{
new Partner("http://www.url.com","timg.jpg",TappedCallback),
new Partner("http://www.url.com","timg.jpg",TappedCallback),
new Partner("http://www.url.com","timg.jpg",TappedCallback),
new Partner("http://www.url.com","timg.jpg",TappedCallback),
new Partner("http://www.url.com","timg.jpg",TappedCallback),
new Partner("http://www.url.com","timg.jpg",TappedCallback)
};
partnersListView.ItemsSource = list;
}
private void TappedCallback(View sender,object param)
{
var Partner = param as Partner;
Device.OpenUri(new Uri(Partner.WebUrl));
}
}
Use TappedCallback in Xaml:
<ListView x:Name="partnersListView">
<ListView.ItemTemplate>
<DataTemplate>
<!--<ImageCell ImageSource="{Binding ImageUrl}" Text="{Binding WebUrl}" />-->
<ViewCell>
<Image Source="{Binding ImageUrl}">
<Image.GestureRecognizers>
<TapGestureRecognizer TappedCallback="{Binding CallBack}" TappedCallbackParameter="{Binding .}" NumberOfTapsRequired="1" />
</Image.GestureRecognizers>
</Image>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Method 2(Command and CommandParameter):
Define an ICommand in your Partner class and constructor to accept ICommand:
public class Partner
{
public Partner(string weburl, string imageurl)
{
this.WebUrl = weburl;
this.ImageUrl = imageurl;
}
public Partner(string weburl, string imageurl, ICommand command) : this(weburl, imageurl)
{
this.TapCommand = command;
}
public string WebUrl { get; set; }
public string ImageUrl { get; set; }
public ICommand TapCommand { get; set; }
}
Create a MyCommand class to implement the ICommand interface :
public class MyCommand : ICommand
{
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
var Partner = parameter as Partner;
Device.OpenUri(new Uri(Partner.WebUrl));
}
}
In your code-behind initialize your items source with a new MyCommand object:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
List<Partner> list = new List<Partner>
{
new Partner("http://www.url.com","timg.jpg",new MyCommand()),
new Partner("http://www.url.com","timg.jpg",new MyCommand()),
new Partner("http://www.url.com","timg.jpg",new MyCommand()),
new Partner("http://www.url.com","timg.jpg",new MyCommand()),
new Partner("http://www.url.com","timg.jpg",new MyCommand()),
new Partner("http://www.url.com","timg.jpg",new MyCommand())
};
partnersListView.ItemsSource = list;
}
}
Use Command in Xaml:
<ListView x:Name="partnersListView">
<ListView.ItemTemplate>
<DataTemplate>
<!--<ImageCell ImageSource="{Binding ImageUrl}" Text="{Binding WebUrl}" />-->
<ViewCell>
<Image Source="{Binding ImageUrl}">
<Image.GestureRecognizers>
<TapGestureRecognizer Command="{Binding TapCommand}" CommandParameter="{Binding .}" NumberOfTapsRequired="1" />
</Image.GestureRecognizers>
</Image>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>

Resources