Xamarin Prism : Dependency Service Toast Message - xamarin

I want to implement a Toast message on my Android app.
So I created in my shared code the interface :
namespace TravelApp.Renderers
{
public interface IToast
{
void show(string message);
}
}
Then I created on my Android project the interface implementation
[assembly:Dependency(typeof(TravelApp.Droid.Toast))]
namespace TravelApp.Droid
{
public class Toast : IToast
{
public void show(string message)
{
Android.Widget.Toast.MakeText(Android.App.Application.Context, message, ToastLength.Short).Show();
}
}
}
In my XAML file I used a pancakeview, when I tap on this view I want to display my toast Message :
<pancake:PancakeView x:Name="MyPancakecs" HorizontalOptions="EndAndExpand"
VerticalOptions="EndAndExpand"
CornerRadius="60"
HeightRequest="50"
WidthRequest="50"
BackgroundColor="{StaticResource BackgroundColor}"
Margin="0,0,60,0"
Padding="15"
>
<Image Source="TrayPlus"></Image>
<pancake:PancakeView.GestureRecognizers>
<TapGestureRecognizer Command="{Binding ToastMyToaster}"/>
</pancake:PancakeView.GestureRecognizers>
</pancake:PancakeView>
Then I register my container in a PlateformInitializer class in my android Project :
namespace TravelApp.Droid
{
public class PlatformInitializer : IPlatformInitializer
{
public void RegisterTypes(IContainerRegistry containerRegistry)
{
containerRegistry.Register<IToast,Toast>();
}
}
}
I added it in my App constructor in MainActivity.cs :
LoadApplication(new App(new PlatformInitializer())) ;
And then in my ViewModel I add a IToast object in my constructor:
namespace TravelApp.ViewModels
{
public class TravelListViewModel : BindableBase
{
private string _messageToast;
public string MessageToast
{
get { return _messageToast; }
set { SetProperty(ref _messageToast, value); }
}
public DelegateCommand ToastMyToaster;
public TravelListViewModel(INavigationService navigationService, ITravelRepository travelRepository, IToast Toaster)
{
this._navigationService = navigationService;
this._travelRepository = travelRepository;
this._messageToast = "Test Toaster";
this._toaster = Toaster;
this.ToastMyToaster = new DelegateCommand(ToastShow);
}
private void ToastShow()
{
this._toaster.show(MessageToast);
}
}
In my research I used this documentation : https://prismlibrary.com/docs/xamarin-forms/Dependency-Service.html
However when I run the code and tap on my pancakeview, there is no message displayed, I'm not even sure that the command is triggered...
I don't know if I needed to implement the IPlateformInitializer.
Thank you for your help,

Here is how I did it.
Shared Project
public interface IToast
{
void LongAlert(string message);
void ShortAlert(string message);
}
Android Project (Renderer)
You need to install CurrentActivityPlugin
[assembly: Dependency(typeof(AndroidToast))]
namespace yournamespace
{
public class AndroidToast : IToast
{
public void LongAlert(string message)
{
Toast.MakeText(CrossCurrentActivity.Current.Activity, message, ToastLength.Long).Show();
}
public void ShortAlert(string message)
{
Toast.MakeText(CrossCurrentActivity.Current.Activity, message, ToastLength.Short).Show();
}
}
}
Calling toast in my shared project
internal static void Toast(string message, bool isShort=false)
{
var toast = DependencyService.Get<IToast>();
if (toast != null)
{
if (!isShort)
toast.LongAlert(message);
else
toast.ShortAlert(message);
}
}
Somewhere in my viewModel / any where in Shared project
Helper.Toast("message here");

Related

Xamarin Forms Button Command doesn't fire after adding button effect

I have a button like this:
<Button Margin="0,20,0,0" Command="{Binding OnSkip}" BackgroundColor="{StaticResource Primary}" CornerRadius="2"
Text="Terms and Conditions of Use" VerticalOptions="End" TextColor="White">
<Button.Effects>
<effects1:ButtonClickEffect></effects1:ButtonClickEffect>
</Button.Effects>
</Button>
Upon adding the button effect inside the button, the 'OnSkip' command no longer fires and I'm not sure why.
The button click effect code is implemented as follows:
public class AndroidButtonClickEffect : PlatformEffect
{
protected override void OnAttached()
{
this.Control.Touch += this.Control_Touch;
}
private void Control_Touch(object sender, Android.Views.View.TouchEventArgs e)
{
if (e.Event.Action == MotionEventActions.Down)
{
this.SetColor(Android.Graphics.Color.Blue);
}
else if (e.Event.Action == MotionEventActions.Up)
{
this.SetColor(Android.Graphics.Color.LightBlue);
}
}
private void SetColor(Android.Graphics.Color color)
{
this.Control.SetBackgroundColor(color);
}
protected override void OnDetached()
{
this.Control.Touch -= this.Control_Touch;
}
}
Removing the button effect causes the command to fire again. Why does the button effect interfere with the command firing? Is there a way I can get the effect to invoke the desired command (generically so I can reuse the effect)?
Thanks.
In the main project, I added the following class, which binds the Command:
public class ButtonClickEffect : RoutingEffect
{
public ButtonClickEffect() : base("Framework.ButtonClickEffect") { }
public static readonly BindableProperty CommandProperty =
BindableProperty.Create("Command", typeof(ICommand), typeof(ButtonClickEffect));
public static ICommand GetCommand(BindableObject view)
{
return (ICommand)view.GetValue(CommandProperty);
}
public static void SetCommand(BindableObject view, ICommand value)
{
view.SetValue(CommandProperty, value);
}
public static readonly BindableProperty CommandParameterProperty =
BindableProperty.CreateAttached("CommandParameter", typeof(object),
typeof(ButtonClickEffect), (object)null);
public static object GetCommandParameter(BindableObject view)
{
return view.GetValue(CommandParameterProperty);
}
public static void SetCommandParameter(BindableObject view, object value)
{
view.SetValue(CommandParameterProperty, value);
}
}
The Android implementation was implemented as follows:
[assembly:ResolutionGroupName("Framework")]
[assembly:ExportEffect(typeof(AndroidButtonClickEffect), "ButtonClickEffect")]
namespace Framework.Droid.Effects
{
public class AndroidButtonClickEffect : PlatformEffect
{
protected override void OnAttached()
{
Control.Touch += Control_Touch;
}
private void Control_Touch(object sender, Android.Views.View.TouchEventArgs e)
{
if (e.Event.Action == MotionEventActions.Down)
{
SetColor(Color.LightBlue);
}
else if (e.Event.Action == MotionEventActions.Up)
{
SetColor(Color.Blue);
}
var command = ButtonClickEffect.GetCommand(Element);
command?.Execute(ButtonClickEffect.GetCommandParameter(Element));
}
private void SetColor(Color color)
{
Control.SetBackgroundColor(color);
}
protected override void OnDetached()
{
Control.Touch -= Control_Touch;
}
}
}
I then removed the 'Command' property from my button and replaced it as follows:
<Button Margin="0,20,0,0" BackgroundColor="{StaticResource Primary}" CornerRadius="2"
Text="Terms and Conditions of Use" VerticalOptions="End" TextColor="White"
effects1:ButtonClickEffect.Command="{Binding OnSkip}" effects1:ButtonClickEffect.CommandParameter="{Binding .}">
<Button.Effects>
<effects1:ButtonClickEffect></effects1:ButtonClickEffect>
</Button.Effects>
</Button>
In all honesty, the command binding is a lot more awkward now (all this code just to get a simple button effect), but the important thing is that it now works. Now I need to work out how implement for iOS.
Credit from here for the answer and whoever posted this URL as an answer (it got deleted).

My UI did not response for orientation change

I am developing a native mobile app for all platforms. I have created my own theme content page. Then after deployment on android when I make phone landscape it did not respond. what's the reason here.
Here is my base content page.
public abstract class BaseContentPage : ContentPage
{
public readonly BaseViewModel BaseViewModel;
protected bool _isNavigated = false;
public BaseContentPage(BaseViewModel baseViewModel)
{
BaseViewModel = baseViewModel;
}
public abstract void Navigate(SelectedItemChangedEventArgs e);
protected abstract override void OnAppearing();
protected override void OnDisappearing()
{
_isNavigated = true;
}
public BaseContentPage()
{
}
}
here Xaml
<views:BaseContentPage
xmlns:views="clr-namespace:DipsDemoXaml.Views"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:Resource="clr-namespace:DipsDemoXaml.Resources"
x:Class="DipsDemoXaml.Views.WardListPage" Title="{x:Static Resource:AppResources.WardListPageTitle}">
<StackLayout BackgroundColor="{StaticResource DefaultBackgroundColor}" Orientation="Vertical" x:Name="s1">
I even try this also in code behind constructor I call size changed and create a method called Wardpagesizechanged.
public WardListPage(WardListPageViewModel wardListViewModel) : base(wardListViewModel)
{
InitializeComponent();
this.SizeChanged += wardpagesizechanged;
}
Wardpagesizechanged method
private void wardpagesizechanged(object sender, EventArgs e)
{
if(this.Width> this.Height)
{
s1.Orientation = StackOrientation.Horizontal;
}
else
{
s1.Orientation = StackOrientation.Vertical;
}
}
what is the problem here, I am clueless

Xamarin ListView binding is not working

I have been trying to bind my ListView to my View model. The view model successfully retrieves 5 records from the database and the Listview seems to display 5 blank rows, however it is not showing binding for each field within each row.
I have spent a couple of days searching internet but I don't seem to be doing anything different. I was using master detail pages so I thought that it may be the issue so I set my Events page as first navigation page without master/detail scenario but to no avail. Please note that I am using Portable Ninject for my dependencies/IoC.
My App.Xamal.cs is is as follows:
public App (params INinjectModule[] platformModules)
{
InitializeComponent();
var eventsPage = new NavigationPage(new EventsPage());
//Register core services
Kernel = new StandardKernel(new MyAppCoreModule(), new MyAppNavModule(eventsPage.Navigation));
//Register platform specific services
Kernel.Load(platformModules);
//Get the MainViewModel from the IoC
eventsPage.BindingContext = Kernel.Get<EventsViewModel>();
((BaseViewModel)(eventsPage.BindingContext)).Init();
MainPage = eventsPage;
}
My EventsPage.Xaml is provided below:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MyApp.Views.EventsPage"
Title="Events">
<ContentPage.Content>
<ListView x:Name="Events" ItemsSource="{Binding Events}" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding EventID}" BackgroundColor="Red" TextColor="White"
FontAttributes="Bold" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>
My EventsPage.xaml.cs is provided below:
namespace MyApp.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class EventsPage : ContentPage, IBaseViewFor<EventsViewModel>
{
public EventsPage ()
{
InitializeComponent ();
}
EventsViewModel _vm;
public EventsViewModel ViewModel
{
get => _vm;
set
{
_vm = value;
BindingContext = _vm;
}
}
}
}
My EventsViewModel is as follows, it successfully retrieves 5 records and OnPropertyChanged is fired for Events property:
namespace MyApp.ViewModels
{
public class EventsViewModel : BaseViewModel, IBaseViewModel
{
ObservableCollection<Event> _events;
readonly IEventDataService _eventDataService;
public ObservableCollection<Event> Events
{
get { return _events; }
set
{
_events = value;
OnPropertyChanged();
}
}
public EventsViewModel(INavService navService, IEventDataService eventDataService) : base(navService)
{
_eventDataService = eventDataService;
Events = new ObservableCollection<Event>();
}
public override async Task Init()
{
LoadEntries();
}
async void LoadEntries()
{
try
{
var events = await _eventDataService.GetEventsAsync();
Events = new ObservableCollection<Event>(events);
}
finally
{
}
}
}
}
My BaseViewModel is as follows:
namespace MyApp.ViewModels
{
public abstract class BaseViewModel : INotifyPropertyChanged
{
protected INavService NavService { get; private set; }
protected BaseViewModel(INavService navService)
{
NavService = navService;
}
bool _isBusy;
public bool IsBusy
{
get
{
return _isBusy;
}
set
{
_isBusy = value;
OnPropertyChanged();
OnIsBusyChanged();
}
}
protected virtual void OnIsBusyChanged()
{
}
public abstract Task Init();
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
// Secod BaseViewModel abstract base class with a generic type that will be used to pass strongly typed parameters to the Init method
public abstract class BaseViewModel<TParameter> : BaseViewModel
{
protected BaseViewModel(INavService navService) : base(navService)
{
}
public override async Task Init()
{
await Init(default(TParameter));
}
public abstract Task Init(TParameter parameter);
}
}
IBaseViewModel is just a blank interface:
public interface IBaseViewModel
{
}
IBaseViewFor is given below:
namespace MyApp.ViewModels
{
public interface IBaseViewFor
{
}
public interface IBaseViewFor<T> : IBaseViewFor where T : IBaseViewModel
{
T ViewModel { get; set; }
}
}
My Event model is as follows:
namespace MyApp.Models
{
public class Event
{
public int EventID;
}
}
Finally, the image of the output, as you can see that 5 rows are created with red background but EventID is not binding in each row. I have checked the data and EventID is returned. I have even tried to manually add records into Events list but to no avail, see the manual code and image below:
async void LoadEntries()
{
try
{
Events.Add((new Event() { EventID = 1 }));
Events.Add((new Event() { EventID = 2 }));
Events.Add((new Event() { EventID = 3 }));
Events.Add((new Event() { EventID = 4 }));
Events.Add((new Event() { EventID = 5 }));
}
finally
{
}
}
I have spent a lot of time on it but unable to find a reason for this anomaly, can someone please cast a fresh eye and provide help!?
You can only bind to public properties - ie, you need a getter
public class Event
{
public int EventID { get; set; }
}

How to divide properties class and methods class in MVVM

Last time, I submitted a question about using property in MVVM in my Windows Phone 7 app.
I could well done by excellent advices. Please see my previous question.
Can not bind textblock property from another class to UI class using MVVM
Through my coding, MVVM properties are increasing. So I want to divide properties class and methods.
But I couldn't divide it. Please let me know how to divide properties class and methods class in MVVM.
My code is here.
Authentication.cs
public class Authentication : ViewModelBase
{
private string _ErrorStatus;
public string ErrorStatus
{
get
{
return _ErrorStatus;
}
set
{
_ErrorStatus = value;
NotifyPropertyChanged("ErrorStatus");
}
}
void Authenticate()
{
ErrorStatus = "Access Denied";
}
}
I want to divide like this. But "ErrorStatus" is not changed.
Properties.cs
public class Properties : ViewModelBase
{
private string _ErrorStatus;
public string ErrorStatus
{
get
{
return _ErrorStatus;
}
set
{
_ErrorStatus = value;
NotifyPropertyChanged("ErrorStatus");
}
}
}
Authentication.cs
public class Authentication
{
Properties properties = new Properties();
void Authenticate()
{
//not work
properties.ErrorStatus = "Access Denied";
}
}
The following enables Authentication to have access to properties of Properties and shows how it could work.
public class Properties : ViewModelBase
{
private string _ErrorStatus;
public string ErrorStatus
{
get
{
return _ErrorStatus;
}
set
{
_ErrorStatus = value;
RaisePropertyChanged("ErrorStatus");
}
}
}
public class Authentication : Properties
{
public void Authenticate()
{
ErrorStatus = "Access Denied";
}
}
public partial class MainPage : PhoneApplicationPage
{
public MainPage()
{
InitializeComponent();
var test = new Authentication();
test.Authenticate();
MessageBox.Show(test.ErrorStatus); // Displays "Access Denied"
}
}
I assume you have the same old
private Authentication authentication;
public MainPage()
{
InitializeComponent();
this.authentication = new Authentication();
this.DataContext = authentication;
}
void btnAuthenticate_Click(object src, EventArgs e)
{
authentication.Authenticate();
}
and
public class Authentication
{
private string properties = new Properties();
public string Properties
{
get
{
return properties;
}
set
{
properties = value;
NotifyPropertyChanged("Properties");
}
}
void Authenticate()
{
Properties.ErrorStatus = "Access Denied";
}
}
the normal xaml should then be
<TextBlock Text="{Binding Path=Properties.ErrorStatus}" />
but sometimes, you change the Properties instance.
so if it don't work, you can try this xaml
<Border DataContext="{Binding Properties}">
<TextBlock Text="{Binding Path=ErrorStatus}" />
</Border>

NavigationService throws NullReferenceException

Using MVVM Light, I'm trying to develop a rather simple WP7 application. I've run into a problem using the navigation service. I can navigate to a page, but after pressing the back button I can't navigate to the same page again. NavigationService throws a NullReferenceException.
I have implemented my navigation using Messaging from the GalaSoft.MvvmLight.Messaging namespace. All my views inherits from a customized PhoneApplicationPage base class that registrers a listener on "NavigationRequest":
public class PhoneApplicationPage : Microsoft.Phone.Controls.PhoneApplicationPage
{
public PhoneApplicationPage() : base()
{
Messenger.Default.Register<Uri>(this, "NavigationRequest", (uri) => NavigationService.Navigate(uri));
}
}
From my view models I post Uri's to this listener:
SendNavigationRequestMessage(new Uri("/View/AppSettingsView.xaml", UriKind.Relative));
Like i said, this works except when navigating after pressing the Back button.
Why is this and how can I solve it?
Is there a better way to implement navigation using MVVM Light?
I'm using MVVM Light as well. I have a class called PageConductor, which is based on what John Papa (Silverlight MVP) from Microsoft uses. Here's the PageConductor Service I use
public class PageConductor : IPageConductor
{
protected Frame RootFrame { get; set; }
public PageConductor()
{
Messenger.Default.Register<Messages.FrameMessage>(this, OnReceiveFrameMessage);
}
public void DisplayError(string origin, Exception e, string details)
{
string description = string.Format("Error occured in {0}. {1} {2}", origin, details, e.Message);
var error = new Model.Error() { Description = description, Title = "Error Occurred" };
Messenger.Default.Send(new Messages.ErrorMessage() { Error = error });
}
public void DisplayError(string origin, Exception e)
{
DisplayError(origin, e, string.Empty);
}
private void OnReceiveFrameMessage(Messages.FrameMessage msg)
{
RootFrame = msg.RootFrame;
}
private void Go(string path, string sender)
{
RootFrame.Navigate(new Uri(path, UriKind.Relative));
}
public void GoBack()
{
RootFrame.GoBack();
}
}
In my MainPage.xaml.cs constructor, I have this, which creates an instance of my ContentFrame in my PageConductor service.:
Messenger.Default.Send(new Messages.FrameMessage() { RootFrame = ContentFrame });
I then use dependency injection to instantiate an instance of my PageConductor Service into my MainPage ViewModel. Here is my MainViewModel class:
protected Services.IPageConductor PageConductor { get; set; }
public RelayCommand<string> NavigateCommand { get; set; }
public MainViewModel(Services.IPageConductor pageConductor)
{
PageConductor = pageConductor;
RegisterCommands();
}
private void RegisterCommands()
{
NavigateCommand = new RelayCommand<string>(
(source) => OnNavigate(source));
}
private void OnNavigate(string sender)
{
PageConductor.GoToView(sender, "main");
}
Notice the instance of my PageConductorService as a parameter in my MainViewModel constructor method. I pass this in via my ViewModelLocator:
private readonly TSMVVM.Services.ServiceProviderBase _sp;
public ViewModelLocator()
{
_sp = Services.ServiceProviderBase.Instance;
CreateMain(_sp);
}
#region MainPageViewModel
public static MainViewModel MainStatic
{
get
{
Services.ServiceProviderBase SP = Services.ServiceProviderBase.Instance;
if (_main == null)
{
CreateMain(SP);
}
return _main;
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance",
"CA1822:MarkMembersAsStatic",
Justification = "This non-static member is needed for data binding purposes.")]
public MainViewModel Main
{
get
{
return MainStatic;
}
}
public static void ClearMain()
{
_main.Cleanup();
_main = null;
}
public static void CreateMain(Services.ServiceProviderBase SP)
{
if (_main == null)
{
_main = new MainViewModel(SP.PageConductor);
}
}
#endregion
For further reference, my Messages.FrameMessage class is simply:
internal class FrameMessage
{
public Frame RootFrame { get; set; }
}
I've had no issues with forward/back buttons.

Resources