I have a code written in xaml.cs file, But i want to do it in viewmodel file, but i don't know how can i do it, i tried to do command but this is didn't worked and i'm just a beginner so don't have many ideas about this pattern and commadns
this is my code :
private async void ItemImageButton_Clicked(object sender, EventArgs e)
{
var appoitment = (Appoitment)((ImageButton)sender).CommandParameter;
AppintmentService appintmentService = new AppintmentService();
await appintmentService.DeleteFollowup(appoitment.PatientID, appoitment.ID);
await DisplayAlert("Delted", "The patient Deleteed", "Ok");
await Navigation.PushAsync(new PatientProfileFollowUpPage());
}
Any help please ?
You could binding Command for the ImageButton. A simple code below for your reference.
Xaml:
<ImageButton Command="{Binding ImgButtonCommand}"></ImageButton>
ViewModel:
public class Page1ViewModel
{
public Command ImgButtonCommand { get; set; }
public Page1ViewModel()
{
ImgButtonCommand = new Command(() =>
{
//something you want to do
});
}
}
Binding for the page:
public partial class Page1 : ContentPage
{
public Page1()
{
InitializeComponent();
this.BindingContext=new Page1ViewModel();
}
}
Related
App.xaml.cs
MainPage = new NavigationPage(new Home());
Home.xaml.cs
public partial class Home : CarouselPage
{
public Home()
{
Children.Add(new CarPage1());
Children.Add(new CarPage2());
CurrentPage = new CarPage2();
}
}
CarPage2.xaml.cs
public partial class CarPage2 : ContentPage
{
public CarPage2()
{
InitializeComponent();
}
private void Btn_Clicked(object sender, EventArgs e)
{
//Do something
//I wanna change screen to CarPage1 without use Navigation.PushAsync(new CarPage1());
}
}
How to change current screen to CarPage1 not use Navigation.PushAsync ?
I have done a sample to test and setting the CurrentPage property worked correctly, you can try the following code:
public partial class CarPage2 : ContentPage
{
public CarPage2()
{
InitializeComponent();
}
private void Btn_Clicked(object sender, EventArgs e)
{
var home = this.Parent as CarouselPage;
home.CurrentPage = home.Children[0];
}
}
I followed this example https://medium.com/swlh/xamarin-forms-mvvm-how-to-work-with-sqlite-db-c-xaml-26fcae303edd
I put a breakpoint in my OnRouteSelected event handler, and e.SelectedItem has the selected object, even though the ListView doesn't display it.
Am I missing something glaring below?
Here is my code:
RoutesPage.xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<ContentPage.Content>
<ListView ItemsSource="{Binding Routes}" SelectedItem="{Binding SelectedRoute, Mode=TwoWay}" HasUnevenRows="False" SeparatorColor="Gray" ItemSelected="OnRouteSelected">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Label TextColor="Black" Text="{Binding ROName}"/>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage.Content>
</ContentPage>
RoutesPage.xaml.cs
public partial class RoutesPage : ContentPage
{
public RoutesPage()
{
InitializeComponent();
var routeStore = new RouteStore(DependencyService.Get<ISQLiteDb>());
var pageService = new PageService();
ViewModel = new RoutesPageViewModel(routeStore, pageService);
}
protected override void OnAppearing()
{
base.OnAppearing();
ViewModel.LoadDataCommand.Execute(null);
}
void OnRouteSelected(object sender, SelectedItemChangedEventArgs e)
{
ViewModel.SelectRouteCommand.Execute(e.SelectedItem);
}
public RoutesPageViewModel ViewModel
{
get { return BindingContext as RoutesPageViewModel; }
set { BindingContext = value; }
}
}
RoutesPageViewModel.cs The LoadData() method gets the data and adds it to the Routes Collection successfully.
public class RoutesPageViewModel : BaseViewModel
{
private RouteViewModel _selectedRoute;
private IRouteStore _routeStore;
private IPageService _pageService;
private bool _isDataLoaded;
public ObservableCollection<RouteViewModel> Routes { get; private set; }
= new ObservableCollection<RouteViewModel>();
public RouteViewModel SelectedRoute
{
get { return _selectedRoute; }
set { SetValue(ref _selectedRoute, value); }
}
public ICommand LoadDataCommand { get; private set; }
public ICommand AddRouteCommand { get; private set; }
public ICommand SelectRouteCommand { get; private set; }
public ICommand DeleteRouteCommand { get; private set; }
public ICommand CallRouteCommand { get; private set; }
public RoutesPageViewModel(IRouteStore routeStore, IPageService pageService)
{
_routeStore = routeStore;
_pageService = pageService;
LoadDataCommand = new Command(async () => await LoadData());
AddRouteCommand = new Command(async () => await AddRoute());
SelectRouteCommand = new Command<RouteViewModel>(async c => await SelectRoute(c));
}
private async Task LoadData()
{
if (_isDataLoaded)
return;
_isDataLoaded = true;
var routes = await _routeStore.GetRoutesAsync();
foreach (var route in routes)
Routes.Add(new RouteViewModel(route));
}
private async Task AddRoute()
{
// await _pageService.PushAsync(new RoutesDetailPage(new RouteViewModel()));
}
private async Task SelectRoute(RouteViewModel route)
{
if (route == null)
return;
SelectedRoute = null;
// await _pageService.PushAsync(new RoutesDetailPage(route));
}
}
The property in the viewmodels are being set like this:
private string _roName;
public string ROName
{
get { return _roName; }
set
{
SetValue(ref _roName, value);
OnPropertyChanged(nameof(ROName));
}
}
The constructor:
public RouteViewModel(Route route)
{
//other properties
ROName = route.ROName;
}
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; }
}
i have a toolbar in my content page where there is one item called add , on clicking over add i want to open DisplayActionSheet
i have created ContentPage Toolbar in xaml and attached ICommand to it in view model. Now DisplayActionSheet is accessible only in View hence i am not sure how will i able to access it and render it from view model.
xaml file
<ContentPage.ToolbarItems>
<ToolbarItem Name="" Icon="ic_add.png" Order="Primary" Priority="0" Command="{Binding OnAddContactCommand}"/>
<ToolbarItem Name="" Icon="ic_search.png" Order="Primary" Priority="1" Command="{Binding OnContactSearchCommand}" />
</ContentPage.ToolbarItems>
View model
public ICommand OnContactSearchCommand => new Command(OnContactSearch);
public ICommand OnAddContactCommand => new Command(OnAddContactSearch);
events
private async void OnAddContactSearch()
{
//var action = await DisplayActionSheet(AppResources.select_contact_source, AppResources.cancel, null, AppResources.manual, AppResources.phonebook);
}
private void OnContactSearch()
{
Debug.WriteLine("OnContactSearch");
}
Like #Alessandro said Application.Current.MainPage works fine for action sheets and alerts as well. To hide view specific stuff from view model I created an IMessageBoxService which is injected into the view models' contructors that need it. Note that I am using the Autofac IoC container. For Xamarin's DependencyService you have change the constructors and look up the service in code.
IMessageBoxService.cs
public interface IMessageBoxService
{
void ShowAlert(string title, string message, Action onClosed = null);
// ...
Task<string> ShowActionSheet(string title, string cancel, string destruction, string[] buttons = null);
}
MessageBoxService.cs
public class MessageBoxService : IMessageBoxService
{
private static Page CurrentMainPage { get { return Application.Current.MainPage; } }
public async void ShowAlert(string title, string message, Action onClosed = null)
{
await CurrentMainPage.DisplayAlert(title, message, TextResources.ButtonOK);
onClosed?.Invoke();
}
public async Task<string> ShowActionSheet(string title, string cancel, string destruction = null, string[] buttons = null)
{
var displayButtons = buttons ?? new string[] { };
var action = await CurrentMainPage.DisplayActionSheet(title, cancel, destruction, displayButtons);
return action;
}
}
AppSetup.cs
protected void RegisterDependencies(ContainerBuilder cb)
{
// ...
cb.RegisterType<MessageBoxService>().As<IMessageBoxService>().SingleInstance();
}
Usage
public class EditProductViewModel : AddProductViewModel
{
private IMessageBoxService _messageBoxService;
public ICommand DeleteCommand { get; set; }
public EditProductViewModel(IPageNavigator navigator, IMessenger messenger,
IMessageBoxService messageBoxService, TagDataStore tagDataStore) : base(navigator, messenger, tagDataStore)
{
_messageBoxService = messageBoxService;
DeleteCommand = new Command(DeleteItem);
}
...
private async void DeleteItem()
{
var action = await _messageBoxService.ShowActionSheet(TextResources.MenuTitleDeleteProduct,
TextResources.ButtonCancel, TextResources.ButtonDelete);
if (action == TextResources.ButtonDelete)
{ } // delete
If you are doing viewmodel first navigation (s. Xamarin or Jonathan Yates' blog) you may chose to make this part of the Navigator service. It's a matter of taste
try with
Application.Current.MainPage.DisplayActionSheet();
I'm currently working on Map UI update.
I have a public List<string> PolylineAddressPoints which has a setter where a method is called. The method, which is called, updates public List<GeoPosition> PolylineCoordinates.
When the public List<GeoPosition> PolylineCoordinates has been update, then the renderer is updating the UI of my map.
However, it only works if I edit the list from the MainPage.xaml.cs.
I search during a long moment on the web why my map want not update by itself without find any solution. Does the elements needs to be updated from the UI thread?
My main goal is just to update it directly from the list..
My customMap:
public class CustomMap : Map
{
public static readonly BindableProperty PolylineAddressPointsProperty =
BindableProperty.Create(nameof(PolylineAddressPoints), typeof(List<string>), typeof(CustomMap), new List<string>(), BindingMode.TwoWay);
public List<string> PolylineAddressPoints
{
get { return (List<string>)GetValue(PolylineAddressPointsProperty); }
set
{
SetValue(PolylineAddressPointsProperty, value);
generatePolylineCoordinates(value);
}
}
public static readonly BindableProperty PolylineCoordinatesProperty =
BindableProperty.Create(nameof(PolylineCoordinates), typeof(List<GeoPosition>), typeof(CustomMap), new List<GeoPosition>(), BindingMode.TwoWay);
public List<GeoPosition> PolylineCoordinates
{
get { return (List<GeoPosition>)GetValue(PolylineCoordinatesProperty); }
set { SetValue(PolylineCoordinatesProperty, value); }
}
private async void generatePolylineCoordinates(List<string> value)
{
this.PolylineCoordinates = await GeneratePolylineCoordinates(value);
MessagingCenter.Send<App>((App)Application.Current, "PolylineUpdated");
}
}
I tried using the MessagingCenter but it doesn't works.. It look like the thread is cancel, I said that because I don't see the debug as well.. I don't understand what is happening..
There is the MainPage.xaml.cs:
public partial class MapPolylinePage : ContentPage
{
public List<string> AddressPointList { get; set; }
public MapPolylinePage()
{
base.BindingContext = this;
MessagingCenter.Subscribe<App>((App)Application.Current, "PolylineUpdated", (sender) =>
{
Debug.WriteLine("--DEBUG ADDRESS--");
foreach (string item in MapTest.PolylineAddressPoints)
{
Debug.WriteLine("Adress => [{0}]", item);
}
Debug.WriteLine("--DEBUG ADDRESS--");
Debug.WriteLine("--DEBUG POSITION--");
foreach (GeoPosition item in MapTest.PolylineCoordinates)
{
Debug.WriteLine("GeoPosition => [{0}/{1}]", item.Latitude, item.Longitude);
}
Debug.WriteLine("--DEBUG POSITION--");
});
AddressPointList = new List<string>()
{
"Le Mans, 72100, France",
"Arnages, 72230, France",
"Allonnes, 72700, France"
};
InitializeComponent();
}
}
and the MainPage.xaml:
<ContentPage.Content>
<local:CustomMap x:Name="MapTest" PolylineAutoUpdate="True" PolylineAddressPoints="{Binding AddressPointList}" PolylineColor="Aqua" PolylineWidth="5"
VerticalOptions="Fill" HorizontalOptions="Fill"/>
</ContentPage.Content>
Thank in advance !