Im using Xamarin to develop an application
Basically an admin user should be able to have access to this button. I have used this logic previously and has worked fine.
A regular user should not have access to hide other regular users reviews.
I was told there is issue with it being nested within a carousel view.
I could restructure the whole thing but i like the design currently being used
The XAML code is as follows:
<?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="GOV.ReviewPage" Title="Reviews" >
<Grid RowDefinitions="20,180,20,32,230,90" ColumnDefinitions="20,*,20">
<Frame Grid.Row="1" Grid.Column="1" Grid.RowSpan="2">
<CarouselView x:Name="MainCarousel" IndicatorView="indicatorView" IsBounceEnabled="True" >
<CarouselView.ItemTemplate>
<DataTemplate>
<ScrollView>
<Grid RowDefinitions="20,20,*" ColumnDefinitions="75,*" Margin="15,0,25,0">
<Label Text="Username:" Grid.Row="0" Grid.Column="0" HorizontalTextAlignment="Start" />
<Label Text="{Binding User.Username}" FontSize="15" Grid.Row="0" Grid.Column="1" HorizontalTextAlignment="Start" />
<Label Text="Description:" Grid.Row="1" Grid.Column="0" HorizontalTextAlignment="Start" />
<Label Text="{Binding Description}" FontSize="15" HorizontalTextAlignment="Start" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2"/>
<Frame Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" >
<Button x:Name="HideButtonRef" Clicked="HideButton" Text="{Binding stringVal}"
HeightRequest="50" />
</Frame>
</Grid>
</ScrollView>
</DataTemplate>
</CarouselView.ItemTemplate>
</CarouselView>
</Frame>
</Grid>
</ContentPage>
and The problematic C# logic:
protected override async void OnAppearing()
{
if (User.Admin) { HideButtonRef.IsEnabled = false; } // this doesnt work for some reason
else {HideButtonRef.IsEnabled = true; }
base.OnAppearing();
await LoadList();
}
The c# logic says " the name 'HideButtonRef' does not exist in the current context"
Create a property on the codebehind to bind to. You have to make sure the ViewModel implements INotifyPropertyChanged:
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Then create a property that holds the value of whether or not to show the button.
bool _isAdmin;
public bool IsAdmin
{
get {return _isAdmin;}
set
{
_isAdmin = value;
OnPropertyChanged("IsAdmin");
}
}
Then change the button definition in the XAML to:
<Button x:Name="HideButtonRef"
Clicked="HideButton"
Text="{Binding stringVal}"
IsVisible="{Binding IsAdmin}"
HeightRequest="50" />
And the OnAppearing now becomes
protected override async void OnAppearing()
{
IsAdmin = !User.Admin;
base.OnAppearing();
await LoadList();
}
Related
I have a collectionView that gets the item source from the backend based on the model
public partial class Post
{
public int Id {get; set;}
public int Upvotes {get; set;}
public string Text {get; set;}
}
The collectionView looks like this:
<CollectionView x:Name="collectionView" Margin="10" SelectionMode="Single" SelectionChanged="collectionView_SelectionChanged">
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame HasShadow="True" CornerRadius="20" BorderColor="DarkGray">
<StackLayout Orientation="Vertical" Padding="0" Spacing="0" >
<StackLayout Orientation="Vertical">
<StackLayout Orientation="Horizontal">
<Label Text="Text:" FontAttributes="Bold" HorizontalOptions="Start" VerticalOptions="Center" FontSize="Body" TextColor="#000000"/>
<Label Text="{Binding Text}" HorizontalOptions="Start" VerticalOptions="Center" FontSize="Body" TextColor="#000000"/>
</StackLayout>
<Grid BackgroundColor="White" Margin="2">
<StackLayout>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" x:Name="btnUpvote" Clicked="btnUpvote_Clicked" Text="Upvote" TextColor="Green" CornerRadius="50" HeightRequest="40" HorizontalOptions="Start" VerticalOptions="Center" BackgroundColor="#1110"/>
<StackLayout Grid.Column="1" Orientation="Horizontal">
<Label Text="Upvotes:" TextColor="Black" FontSize="Body" FontAttributes="Bold" VerticalOptions="Center"/>
<Label Text="{Binding Upvotes}" HorizontalOptions="Start" VerticalOptions="Center" FontSize="Body" TextColor="#000000"/>
</StackLayout>
</Grid>
</StackLayout>
</Grid>
</StackLayout>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
I want to click on the Upvote button so I could call an API to increase the number of upvotes, but I need the Id from the selected collectionView Item. I am not sure how to get the Id (without clicking on the collection view, only clicking the Upvote button) before calling the API that needs the Id.
To complete my remark above. I have a button in my xaml that looks like this:
<Button Text="Delete"
Command="{Binding Path=BindingContext.Delete, Source={x:Reference SongsCollectionView}}"
CommandParameter="{Binding .}"
Grid.Column="3" Grid.Row="0" StyleClass="Delete"/>
It references the collection it is inside (in your case that would be the 'collectionView').
Now in my case the program was written with MvvmCross. In the init of the SongsCollectionView the following code is present for the Delete.
public SongsViewModel(IMvxNavigationService navigationService) : base(navigationService)
{
Delete = new MvxCommand<Song>(
execute: (song) =>
{
// Code for deleting song is in here.
});
}
public ICommand Delete { get; set; }
Simply replace the MvxCommand by Command and it should work.
in addition to using a Command, you can also do this with a simple event handler by using the BindingContext
void btnUpvote_Clicked(object sender, EventArgs args)
{
Button btn = (Button)sender;
var item = (Post)btn.BindingContext;
}
For my Xamarin Forms application I am working on developing a calendar page. I have a No-SQL database set up and working and I have my main calendar view page (EventManagerPage) and the backend of this page (EventManagerViewPage).
The issue I am facing is how to let the system know that the RefreshView is refreshing?
The normal Xamarin.Forms.IsBusy indicator has not been working and my page will continue to refresh without stopping. I can tell the calendar events are there as they appear below the calendar after I pull down the page to refresh and click on a date, but the calendar itself does not show the events present (blue square per event on date).
My ViewModel is based off of a custom calendar ViewModel that extends both the INotifyPropertyChanged interface and the BaseViewModel.
Using IsBusy does not result in any error being thrown nor any messages in the debug output. I've tried some other ways around this to try and get the reload to stop once done but those have all resulted in errors that prevent the app from compiling.
So far I have tried creating a custom Boolean to act as the IsBusy Xamarin.Forms indicator but this resulted in the error of the member not being found in the data context.
I additionally attempted to follow the example in this Microsoft Doc on RefreshViews. This also resulted in the error of the member not being found in the data context and I was unable to set refreshView.Command = refreshCommand; (I'm not sure if this is important for the error).
I have put my code below. As a note, the calendar I am using is the Plugin.XCalendar by author MarvinE.
I appreciate any help/suggestions anyone has to offer!
CalendarBaseViewModel.cs
using MvvmHelpers;
using PropertyChanged;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace PPA.ViewModels
{
[AddINotifyPropertyChangedInterface]
public abstract class CalendarBaseViewModel : BaseViewModel, INotifyPropertyChanged
{
#region Events
public event PropertyChangedEventHandler PropertyChanged;
#endregion
#region Methods
protected virtual void OnPropertyChanged([CallerMemberName] string PropertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
#endregion
}
}
EventManagerViewModel.cs
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Windows.Input;
using Xamarin.CommunityToolkit.ObjectModel;
using Xamarin.Forms;
using PPA.Models;
using System.Threading.Tasks;
using PPA.Views;
using PPA.Services;
using System.Diagnostics;
namespace PPA.ViewModels
{
public class EventManagerViewModel : CalendarBaseViewModel
{
#region Properties
public ObservableRangeCollection<Event> Events { get; }
public ObservableRangeCollection<DateTime> SelectedDates { get; }
public ObservableRangeCollection<Event> SelectedEvents { get; }
#endregion
public AsyncCommand AddEventCommand { get; }
public AsyncCommand LoadEventsCommand { get; }
IEventDataStore EventService;
#region Constructors
public EventManagerViewModel()
{
AddEventCommand = new AsyncCommand(OnAddEvent);
LoadEventsCommand = new AsyncCommand(LoadEvents);
EventService = DependencyService.Get<IEventDataStore>();
Events = new ObservableRangeCollection<Event>();
SelectedDates = new ObservableRangeCollection<DateTime>();
SelectedEvents = new ObservableRangeCollection<Event>();
SelectedDates.CollectionChanged += SelectedDates_CollectionChanged;
}
#endregion
#region Methods
private void SelectedDates_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
SelectedEvents.ReplaceRange(Events.Where(x => SelectedDates.Any(y => x.DateTime.Date == y.Date)).OrderByDescending(x => x.DateTime));
}
private async Task OnAddEvent()
{
await Shell.Current.GoToAsync(nameof(NewEventPage));
}
async Task LoadEvents()
{
IsBusy = true;
// refreshview.IsRefreshing = true;
try
{
Events.Clear();
var events = await EventService.GetEventsAsync();
foreach (var ev in events)
{
Events.Add(ev);
}
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
IsBusy = false;
}
public void OnAppearing()
{
IsBusy = true;
}
#endregion
}
}
EventManagerPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage x:Class="PPA.Views.EventManagerPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:Converters="clr-namespace:PPA.Converters"
xmlns:Models="clr-namespace:PPA.Models"
xmlns:ViewModels="clr-namespace:PPA.ViewModels"
xmlns:xc="clr-namespace:XCalendar;assembly=XCalendar"
xmlns:xcModels="clr-namespace:XCalendar.Models;assembly=XCalendar"
xmlns:xct="http://xamarin.com/schemas/2020/toolkit" xmlns:xcConverters="clr-namespace:XCalendar.Converters;assembly=XCalendar"
x:DataType="ViewModels:EventManagerViewModel"
x:Name="This"
Title="Event Calendar"
xct:SafeAreaEffect.SafeArea="True"
>
<ContentPage.Resources>
<!-- Limits a string to a certain amount of characters -->
<xcConverters:StringCharLimitConverter x:Key="StringCharLimitConverter"/>
<!-- Returns true if all bindings evaluate to true -->
<xct:VariableMultiValueConverter x:Key="AllTrueConverter" ConditionType="All"/>
<!-- Inverts a binded boolean value -->
<xct:InvertedBoolConverter x:Key="InvertedBoolConverter"/>
</ContentPage.Resources>
<ContentPage.ToolbarItems>
<ToolbarItem Text="Add" Command="{Binding AddEventCommand}" />
</ContentPage.ToolbarItems>
<RefreshView x:DataType="ViewModels:EventManagerViewModel" Command="{Binding LoadEventsCommand}" IsRefreshing="{Binding IsBusy, Mode=TwoWay}">
<Grid
ColumnSpacing="0"
RowDefinitions="Auto,*"
RowSpacing="0">
<Frame
Margin="10"
Padding="0"
BackgroundColor="White"
CornerRadius="15">
<xc:CalendarView
x:Name="MainCalendarView"
Grid.Row="0"
DayNameTextColor="{StaticResource ContentTextColor}"
NavigationArrowColor="{StaticResource ContentTextColor}"
NavigationBackgroundColor="Transparent"
NavigationTextColor="{StaticResource ContentTextColor}"
SelectedDates="{Binding SelectedDates}"
SelectionAction="Modify"
SelectionType="Single">
<xc:CalendarView.DayTemplate>
<DataTemplate x:DataType="{x:Type xcModels:CalendarDay}">
<!-- ContentView so that the margin is respected by the MonthView -->
<ContentView>
<xc:CalendarDayView
Margin="2.5"
HeightRequest="43"
CalendarView="{Binding ., Source={x:Reference MainCalendarView}}"
CurrentMonthTextColor="{StaticResource CalendarBackgroundTextColor}"
DateTime="{Binding DateTime}"
OutOfRangeTextColor="{StaticResource CalendarTertiaryColor}"
SelectedTextColor="{StaticResource CalendarPrimaryTextColor}"
TodayBorderColor="{StaticResource CalendarPrimaryColor}"
TodayTextColor="{StaticResource CalendarBackgroundTextColor}">
<xc:CalendarDayView.ControlTemplate>
<ControlTemplate>
<!-- Using a Grid to stack views on the z axis -->
<Grid RowSpacing="2">
<Grid.RowDefinitions>
<RowDefinition Height="1.5*"/>
<RowDefinition/>
</Grid.RowDefinitions>
<!-- ContentPresenter displays the default content for the control -->
<ContentPresenter
Grid.Row="0"
Grid.RowSpan="2"
VerticalOptions="Center"/>
<StackLayout
Grid.Row="1"
HorizontalOptions="Center"
Orientation="Horizontal"
Spacing="2.5">
<!-- I want the event indicators to only be visible when the DateTime is in the currently navigated month -->
<StackLayout.IsVisible>
<MultiBinding Converter="{StaticResource AllTrueConverter}">
<!-- TemplatedParent refers to the view that the ControlTemplate resides in -->
<Binding Path="IsCurrentMonth" Source="{RelativeSource TemplatedParent}"/>
<Binding
Converter="{StaticResource InvertedBoolConverter}"
Path="IsOutOfRange"
Source="{RelativeSource TemplatedParent}"/>
</MultiBinding>
</StackLayout.IsVisible>
<BindableLayout.ItemsSource>
<Binding Path="DateTime.Date" Source="{RelativeSource TemplatedParent}">
<Binding.Converter>
<Converters:EventWhereConverter
Items="{Binding BindingContext.Events, Source={x:Reference This}}"
UseTimeComponent="False"
WhiteList="True"/>
</Binding.Converter>
</Binding>
</BindableLayout.ItemsSource>
<BindableLayout.ItemTemplate>
<DataTemplate x:DataType="{x:Type Models:Event}">
<BoxView
CornerRadius="100"
HeightRequest="7"
HorizontalOptions="CenterAndExpand"
VerticalOptions="Center"
WidthRequest="7"
Color="Blue"/>
</DataTemplate>
</BindableLayout.ItemTemplate>
</StackLayout>
</Grid>
</ControlTemplate>
</xc:CalendarDayView.ControlTemplate>
</xc:CalendarDayView>
</ContentView>
</DataTemplate>
</xc:CalendarView.DayTemplate>
</xc:CalendarView>
</Frame>
<CollectionView Grid.Row="1" ItemsSource="{Binding SelectedEvents}">
<CollectionView.EmptyView>
<Label
FontAttributes="Bold"
FontSize="20"
HorizontalTextAlignment="Center"
Text="No Events on Selected Date(s)"
TextColor="{StaticResource ContentTextColor}"
VerticalTextAlignment="Center"/>
</CollectionView.EmptyView>
<CollectionView.ItemsLayout>
<LinearItemsLayout ItemSpacing="0" Orientation="Vertical"/>
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate x:DataType="{x:Type Models:Event}">
<ContentView Padding="5">
<Frame
Padding="0"
BackgroundColor="{StaticResource ContentBackgroundColor}"
CornerRadius="10">
<StackLayout Orientation="Horizontal" Spacing="0">
<BoxView BackgroundColor="CornflowerBlue" WidthRequest="20"/>
<StackLayout Padding="10" Spacing="0">
<Label
FontAttributes="Bold"
FontSize="20"
Text="{Binding DateTime, StringFormat='{0: dd MMMM HH:mm}'}"
TextColor="{StaticResource ContentTextColor}"
VerticalTextAlignment="Center"/>
<Label
FontSize="16"
Text="{Binding Title}"
TextColor="{StaticResource ContentTextColor}"
Margin="5,0,0,0"/>
<Label
Margin="5,10,0,0"
FontSize="14"
Text="{Binding Description}"
TextColor="{StaticResource ContentTextColor}"/>
</StackLayout>
</StackLayout>
</Frame>
</ContentView>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
</RefreshView>
</ContentPage>
EventManagerPage.xaml.cs
using PPA.ViewModels;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace PPA.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class EventManagerPage : ContentPage
{
EventManagerViewModel _viewModel;
public EventManagerPage()
{
InitializeComponent();
BindingContext = _viewModel = new EventManagerViewModel();
}
protected override void OnAppearing()
{
base.OnAppearing();
_viewModel.OnAppearing();
}
}
}
Have you created binding property for "IsBusy" in the ViewModel as below:
private bool _isBusy;
public bool IsBusy
{
get
{
return _isBusy;
}
set
{
_isBusy = value;
OnPropertyChanged("IsBusy");
}
}
I have a list view which contains cells like this :
<StackLayout Orientation="Horizontal" Margin="30,0,20,0" VerticalOptions="Center">
<Label Grid.Row="0" Text="{Binding AppointmentPatientName}" VerticalOptions="Center" HorizontalOptions="StartAndExpand" Style="{StaticResource labelView}"/>
<Label Grid.Row="0" Grid.Column="2" Text="{Binding AppointmentDate.Date}" Font="Small" HorizontalOptions="CenterAndExpand" Style="{StaticResource labelView}" />
<Label x:Name="Delete Button" Text="X"></Label>
</StackLayout>
How can I activate this label in order to make it Delete from firebase database it's smth like this :
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());
}
but i want it for label not ImgButton and want activate it in viewmodel page
i want it for label not ImgButton and want activate it in viewmodel
page
Yes, you can use TapGestureRecognizer and MVVM to achieve this.
Based on your code ,I made a simple demo.You can refer to the following code:
<ContentPage.BindingContext>
<tapapp1:MyViewModel></tapapp1:MyViewModel>
</ContentPage.BindingContext>
<StackLayout Orientation="Horizontal" Margin="30,0,20,0" VerticalOptions="Center">
<Label Text="{Binding AppointmentPatientName}" VerticalOptions="Center" HorizontalOptions="StartAndExpand" />
<Label Grid.Column="2" Text="{Binding AppointmentDate}" Font="Small" HorizontalOptions="CenterAndExpand" />
<Label x:Name="Delete" Text="X" >
<Label.GestureRecognizers>
<TapGestureRecognizer Command="{Binding TapCommand}"
/>
</Label.GestureRecognizers>
</Label>
</StackLayout>
MyViewModel.cs
public class MyViewModel
{
public string AppointmentPatientName { get; set; }
public string AppointmentDate { get; set; }
public ICommand TapCommand => new Command(RemoveItem);
private void RemoveItem()
{
// add your code here
}
public MyViewModel()
{
AppointmentPatientName = "Test1";
AppointmentDate = "2022-4-28";
}
}
Note: You can modify above code based on your requirement.
I am new to Xamarin Forms and trying to implement Infinite Loop Functionality in my app. The sample code that i followed is working fine and i have managed to integrate it into my app successfully.
The problem is that i am unable to find out how to change the source of the listview from the code.
I intend to have buttons above my listview (As in the following Image) and upon click the source of the listView should change.
This is how my code looks like
<ContentPage.BindingContext>
<local:MainViewModel />
</ContentPage.BindingContext>
<StackLayout>
<ListView x:Name="tyres_listview" ItemsSource="{Binding Items}"
HasUnevenRows="True" SeparatorVisibility="None" >
<ListView.Behaviors>
<extended:InfiniteScrollBehavior IsLoadingMore="{Binding IsBusy}" />
</ListView.Behaviors>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Grid Margin="10" VerticalOptions="FillAndExpand" HorizontalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.25*"></ColumnDefinition>
<ColumnDefinition Width="0.75*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackLayout Grid.Column="0" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" Margin="5,5,5,10">
<Image Source="{Binding image_url}" >
</Image>
</StackLayout>
<StackLayout Grid.Column="1" Spacing="0" >
<Label Text="{Binding name}" FontAttributes="Bold" FontSize="Small" Margin="0,5,5,0" VerticalOptions="Center" ></Label>
<Label Text="{Binding brand}" VerticalOptions="Center" FontSize="Micro" ></Label>
<Label Text="{Binding item_id}" VerticalOptions="Center" FontSize="Micro" ></Label>
<StackLayout Orientation="Horizontal" x:Name="sl_db" >
<Image Source="fuel.png" ></Image>
<Label Text="{Binding fuel_type}"></Label>
<Image Source="weather.png"></Image>
<Label Text="{Binding wheather_type }"></Label>
<Image Source="volume.png" ></Image>
<Label Text="{Binding noise}"></Label>
<Label Text="dB"></Label>
</StackLayout>
<StackLayout Orientation="Horizontal">
<Image></Image>
<Label Text="{Binding rated_count }"></Label>
</StackLayout>
<StackLayout Orientation="Horizontal">
<Label Text="Price: " VerticalOptions="Center"></Label>
<Label Text="{Binding price }" VerticalOptions="Center" TextColor="Green"></Label>
<Label Text=" EUR" VerticalOptions="Center"></Label>
</StackLayout>
<StackLayout Orientation="Horizontal">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped_1" >
</TapGestureRecognizer>
</StackLayout.GestureRecognizers>
<StackLayout BackgroundColor="Red" Orientation="Horizontal" Margin="5" >
<Image Source="shoppingcart.png" Margin="10,0,0,0"></Image>
<Label Text="Add to Cart" VerticalOptions="Center" HorizontalOptions="EndAndExpand" Margin="0,0,10,0" ></Label>
</StackLayout>
<Image x:Name="button_info" Source="info.png" >
<Image.GestureRecognizers>
<TapGestureRecognizer Tapped="button_info_Clicked"></TapGestureRecognizer>
</Image.GestureRecognizers>
</Image>
</StackLayout>
</StackLayout>
</Grid>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Footer>
<Grid Padding="6" IsVisible="{Binding IsBusy}">
<!-- set the footer to have a zero height when invisible -->
<Grid.Triggers>
<Trigger TargetType="Grid" Property="IsVisible" Value="False">
<Setter Property="HeightRequest" Value="0" />
</Trigger>
</Grid.Triggers>
<!-- the loading content -->
<Label Text="Loading..." TextColor="DeepPink" FontSize="20" FontAttributes="Bold" VerticalOptions="Center" HorizontalOptions="Center" />
</Grid>
</ListView.Footer>
</ListView>
<Button Clicked="button_Change_Order_Clicked"></Button>
</StackLayout>
public class MainViewModel : INotifyPropertyChanged
{
private bool _isBusy;
private const int PageSize = 10;
readonly DataService _dataService = new DataService();
public InfiniteScrollCollection<Product_Search> Items { get; }
public bool IsBusy
{
get => _isBusy;
set
{
_isBusy = value;
OnPropertyChanged();
}
}
public MainViewModel()
{
Items = new InfiniteScrollCollection<Product_Search>
{
OnLoadMore = async () =>
{
IsBusy = true;
// load the next page
var page = Items.Count / PageSize;
var items = await _dataService.GetItemsAsync(page, PageSize);
IsBusy = false;
// return the items that need to be added
return items;
},
OnCanLoadMore = () =>
{
return Items.Count < 44;
}
};
DownloadDataAsync();
}
private async Task DownloadDataAsync()
{
var items = await _dataService.GetItemsAsync(pageIndex: 0, pageSize: PageSize);
Items.AddRange(items);
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Any help would be of great importance.
Your ListView.Items property is already bound to your MainViewModel.Items property, a PropertyChanged event needs to be triggered to signal a change on the property value (corresponding to the property itself or its content if InfiniteScrollCollection<> is an Observable collection).
When you would want to replace the Items source you can give it a new value, signal it has changed and your ListView might be refreshed:
private InfiniteScrollCollection<Product_Search> _items;
public InfiniteScrollCollection<Product_Search> Items
{
get { return _items; }
set
{
_items = value;
OnPropertyChanged();
}
}
I try using mvvm in my xamarin forms but i'm still litle bit confused with the navigation in each page, I Already Create Interface and NavigationService to handle all Navigation in my Xamarin Forms, there is no error code line but when i click the buton and try to navigate to other page it always crash. Here is some of my code
My Interface
namespace KGVC.Interfaces
{
public interface INavigationService
{
void NavigateToDashboard();
void NavigateToLogout();
void NavigateBack();
void gotoNews();
void goEvent();
void gotoKGCash();
void gotoCardCommnunity();
void gotoSetting();
void gotoNearbyLocation();
}
}
my navigationservice
namespace KGVC.Services
{
public class NavigationService : INavigationService
{
public void goEvent()
{
var currentPage = GetCurrentPage();
currentPage.Navigation.PushAsync(new EventPage());
}
public void gotoCardCommnunity()
{
var currentPage = GetCurrentPage();
currentPage.Navigation.PushAsync(new CardCommunityPage());
}
public void gotoKGCash()
{
var currentPage = GetCurrentPage();
currentPage.Navigation.PushAsync(new KGCashPage());
}
public void gotoNearbyLocation()
{
var currentPage = GetCurrentPage();
currentPage.Navigation.PushAsync(new StoreMaps());
}
public void gotoNews()
{
var currentPage = GetCurrentPage();
currentPage.Navigation.PushAsync(new RssFeedView());
}
public void gotoSetting()
{
var currentPage = GetCurrentPage();
currentPage.Navigation.PushAsync(new SettingPages());
}
public void NavigateBack()
{
throw new NotImplementedException();
}
public void NavigateToDashboard()
{
var currentPage = GetCurrentPage();
Application.Current.MainPage = new MainPage();
}
public void NavigateToLogout()
{
var currentPage = GetCurrentPage();
Application.Current.MainPage = new NewPageLogin();
}
private Page GetCurrentPage()
{
var currentPage = Application.Current.MainPage.Navigation.NavigationStack.LastOrDefault();
return currentPage;
}
}
}
my view model
public class GridMenuViewModel
{
public ICommand gotoNews { get; private set; }
public ICommand goEvent { get; private set; }
public ICommand gotoKGCash { get; private set; }
public ICommand gotoSetting { get; private set; }
public ICommand gotoNearbyLocation { get; private set; }
public GridMenuViewModel()
{
gotoNews = new Command(() =>
{
var navigationService = new NavigationService();
navigationService.gotoNews();
});
goEvent = new Command(() =>
{
var navigationService = new NavigationService();
navigationService.goEvent();
});
gotoKGCash = new Command(() =>
{
var navigationService = new NavigationService();
navigationService.gotoKGCash();
});
gotoSetting = new Command(() =>
{
var navigationService = new NavigationService();
navigationService.gotoSetting();
});
gotoNearbyLocation = new Command(() =>
{
var navigationService = new NavigationService();
navigationService.gotoNearbyLocation();
});
}
}
And my view
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
NavigationPage.HasNavigationBar="True"
NavigationPage.BackButtonTitle="False"
BindingContext="{Binding GridMenuViewModel, Source={StaticResource Locator}}"
xmlns:control="clr-namespace:Xamarin.Forms;assembly=Xamarin.Forms.CarouselView"
xmlns:local="clr-namespace:KGVC.Views.MainPages"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="KGVC.Views.MainPages.GridMenu"
Title="HOME"
>
<ContentPage.Content>
<StackLayout >
<StackLayout BackgroundColor="##CEB053">
<StackLayout HeightRequest="35" BackgroundColor="##CEB053" Orientation="Horizontal">
<Label FontAttributes="Italic" TextColor="Black" Margin="9" Text="Hello," FontSize="15"/>
<Label TextColor="Black" Margin="9" Text="John Doe" FontSize="15" FontAttributes="Bold"/>
</StackLayout>
<StackLayout Padding="0" HeightRequest="30" BackgroundColor="#E3E6E3" Orientation="Horizontal">
<Image Margin="5" Source="ic_logo.png"/>
<Label Text="Points" Margin="5" FontSize="13" TextColor="Black"/>
</StackLayout>
</StackLayout>
<StackLayout HeightRequest="220" VerticalOptions="StartAndExpand">
<control:CarouselView HeightRequest="185" ItemsSource="{Binding MyDataSource}" Position="{Binding Position, Mode=TwoWay}">
<control:CarouselView.ItemTemplate>
<DataTemplate>
<Image Source="{Binding Image}"/>
</DataTemplate>
</control:CarouselView.ItemTemplate>
</control:CarouselView>
<local:CarouselIndicators IndicatorHeight="9" IndicatorWidth="9" UnselectedIndicator="unselected_circle.png" SelectedIndicator="selected_circle.png" Position="{Binding Position}" ItemsSource="{Binding MyDataSource}" />
</StackLayout>
<ScrollView IsClippedToBounds="True" VerticalOptions="StartAndExpand" Orientation="Vertical" >
<Grid x:Name="controlGrid" VerticalOptions="StartAndExpand" HeightRequest="370" Margin="15">
<Grid.RowDefinitions>
<RowDefinition Height="0" />
<RowDefinition Height="100" />
<RowDefinition Height="100" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="110" />
<ColumnDefinition Width="110" />
<ColumnDefinition Width="110" />
</Grid.ColumnDefinitions>
<StackLayout Orientation="Vertical" Grid.Row="1" Grid.Column="0" >
<Button Image="ic_account.png" Margin="5"
Style="{StaticResource plainButton}" />
<Label FontSize="12" Text="MY ACCOUNT" HorizontalOptions="Center"/>
</StackLayout>
<StackLayout Orientation="Vertical" Grid.Row="1" Grid.Column="1">
<Button Margin="5" Command="{Binding gotoCardCommunity}" Image="ic_card.png"
Style="{StaticResource plainButton}" />
<Label FontSize="12" Text="CARD" HorizontalOptions="Center"/>
</StackLayout>
<StackLayout Orientation="Vertical" Grid.Row="1" Grid.Column="2">
<Button Margin="5" Command="{Binding goEvent}" Image="ic_event"
Style="{StaticResource plainButton}" />
<Label FontSize="12" Text="PROMO" HorizontalOptions="Center"/>
</StackLayout>
<StackLayout Grid.Row="2" Grid.Column="0" Orientation="Vertical">
<Button Margin="5" Command="{Binding gotoNearbyLocation}" Image="ic_store"
Style="{StaticResource plainButton}" />
<Label FontSize="12" Text="STORE LOCATIONS" HorizontalOptions="Center"/>
</StackLayout>
<StackLayout Orientation="Vertical" Grid.Row="2" Grid.Column="1">
<Button Margin="5" Command="{Binding gotoNews}" Image="ic_news"
Style="{StaticResource plainButton}" />
<Label FontSize="12" Text="NEWS" HorizontalOptions="Center"/>
</StackLayout>
<StackLayout Grid.Row="2" Grid.Column="2" Orientation="Vertical">
<Button Margin="5" Command="{Binding gotoKGCash}" Image="ic_kgcash"
Style="{StaticResource plainButton}" />
<Label FontSize="12" Text="E-WALLET" HorizontalOptions="Center"/>
</StackLayout>
<StackLayout Orientation="Vertical" Grid.Row="3" Grid.Column="0">
<Button Margin="5" Image="ic_ecommerce"
Style="{StaticResource plainButton}" />
<Label FontSize="12" Text="E-COMMERCE" HorizontalOptions="Center"/>
</StackLayout>
<StackLayout Orientation="Vertical" Grid.Row="3" Grid.Column="1">
<Button Margin="5" Command="{Binding gotoSetting}" Image="ic_pointsummary.png"
Style="{StaticResource plainButton}" />
<Label FontSize="12" Text="POINT SUMMARY" HorizontalOptions="Center"/>
</StackLayout>
<StackLayout Orientation="Vertical" Grid.Row="3" Grid.Column="2">
<Button Margin="5" Command="{Binding gotoSetting}" Image="ic_setting"
Style="{StaticResource plainButton}" />
<Label FontSize="12" Text="SETTINGS" HorizontalOptions="Center"/>
</StackLayout>
</Grid>
</ScrollView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
i already try change the navigation service to use pushmodalasync, make the navigation async but still crash, Anyone can help me ? and i'm not using prism , mvvmlight and other 3rd party mvvm nuget
It depends on your MainPage. Did you check if GetCurrentPage() actually returns something?
Try to change the GetCurrentPage() to:
Page GetCurrentPage() =>
Application.Current.MainPage.Navigation?.NavigationStack?.LastOrDefault() ??
Application.Current.MainPage;
Another problems that I spotted:
1. C# naming conventions are different from Java. Method names should start with a capital letter. Currently you have a pure mix.
2. Initialising the BindingContext in XAML the way you did will create the ViewModel twice due to a bug. Change it to:
<ContentPage
xmlns:vm="your-namespace.vm"
...>
<ContentPage.BindingContext>
<vm:GridMenuViewModel />
</ContentPage.BindingContext>
</ContentPage>
3. No need to recreate the NavigationService for each command. Reuse it in your ViewModel.
4. You have some corrupted XAML:
<StackLayout BackgroundColor="##CEB053">
Fix the hex code. Generally speaking you could enable XAMLC so you would get notified about XAML errors while building your code.
5. In order to use PushAsync your MainPage should be wrapped by NavigationPage.
6. You need to await async code execution:
public async Task GoToCardCommnunity()
{
var currentPage = GetCurrentPage();
await currentPage.Navigation.PushAsync(new CardCommunityPage());
}
Check over the list above and pay attention to the application output and exceptions.