Good Day. I'm creating a simple Xamarin.Forms (Portable) application that allows me to create record of an Employee and saved it on a Database in Visual Studio. All records are being displayed to a ListView.
My application is doing fine when I run it on UWP. It displays the all the created records on a ListView properly.
But when I run it on Android Platform, it does not display any record in the ListView. I even tried to check on the Web API whether it returns a value or not. And I did get a value. Meaning, the problem is on Android Platform why it isn't displaying any record.
Have you encountered this problem? What do you think is the reason behind this? What can I do now? Sorry I'm just a newbie here in Xamarin. Hope you can help me. Thanks a lot.
These are some codes I have:
EmployeeRecordsPage.xaml
<?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="XamarinFormsDemo.EmployeeRecordsPage"
xmlns:ViewModels="clr-namespace:XamarinFormsDemo.ViewModels;assembly=XamarinFormsDemo"
xmlns:controls="clr-namespace:ImageCircle.Forms.Plugin.Abstractions;assembly=ImageCircle.Forms.Plugin.Abstractions"
BackgroundImage="bg3.jpg"
Title="List of Employees">
<ContentPage.BindingContext>
<ViewModels:MainViewModel/>
</ContentPage.BindingContext>
<StackLayout Orientation="Vertical">
<ListView ItemsSource="{Binding EmployeesList}"
HasUnevenRows="True"
IsPullToRefreshEnabled="True"
>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="10" RowSpacing="10" ColumnSpacing="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<controls:CircleImage Source="icon.png"
HeightRequest="66"
HorizontalOptions="CenterAndExpand"
Aspect="AspectFill"
WidthRequest="66"
Grid.RowSpan="2"
/>
<Label Grid.Column="1"
Text="{Binding EMPLOYEE_NAME}"
TextColor="#24e97d"
FontSize="24"/>
<Label Grid.Column="1"
Grid.Row="1"
Text="{Binding EMP_NUMBER}"
TextColor="White"
FontSize="18"
Opacity="0.6"/>
<Label Grid.Column="1"
Grid.Row="2"
Text="{Binding DEPT_COMP}"
TextColor="White"
FontSize="18"
Opacity="0.6"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackLayout Orientation="Vertical"
Padding="30,10,30,10"
HeightRequest="20"
BackgroundColor="#24e97d"
VerticalOptions="Center"
Opacity="0.5">
<Label Text="© Copyright 2016 SMESOFT.COM.PH All Rights Reserved "
HorizontalTextAlignment="Center"
VerticalOptions="Center"
HorizontalOptions="Center" />
</StackLayout>
</StackLayout>
</ContentPage>
..
EmployeeViewModel.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using XamarinFormsDemo.Models;
using XamarinFormsDemo.Services;
namespace XamarinFormsDemo.ViewModels
{
public class MainViewModel : INotifyPropertyChanged
{
private List<Employee> _employeesList;
private Employee _selectedEmployee = new Employee();
public List<Employee> EmployeesList
{
get { return _employeesList; }
set
{
_employeesList = value;
OnPropertyChanged();
}
}
public MainViewModel()
{
InitializeDataAsync();
}
private async Task InitializeDataAsync()
{
var employeesServices = new EmployeesServices();
EmployeesList = await employeesServices.GetEmployeesAsync();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
..
EmployeesServices.cs
using Plugin.RestClient;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using XamarinFormsDemo.Models;
namespace XamarinFormsDemo.Services
{
public class EmployeesServices
{
public async Task<List<Employee>> GetEmployeesAsync()
{
RestClient<Employee> restClient = new RestClient<Employee>();
var employeesList = await restClient.GetAsync();
return employeesList;
}
}
}
I think the issue is that you are not updating the ListView after you get your items with:
EmployeesList = await employeesServices.GetEmployeesAsync();
I would suggest using an ObservableCollection instead of a List. With an observable collection the ListView should get updated automatically when items in the ObservableCollection are added or deleted. So instead of:
private List<Employee> _employeesList;
try:
private ObservableCollection<Employee> _employeesList;
or you can just assign the ListView.ItemsSource to null and then back to the List<Employee> after you get your data:
EDIT: As noted by Jaycee, the following code to reset the ItemsSource should not be in the view model as the view model won't have access to listView. However the way to refresh the listView.ItemsSource is correct. The view model just needs to let the view know that the EmployeesList was updated and then you can reset the listView.ItemsSource in the code behind for the view. This could be done in a couple of ways I can think of. For instance you could have a delegate in the ViewModel that the view can provide the code implementation for or the view model could raise an event that the view can subscribe to. Basically you just have to let the view know that it needs to refresh its ListView's ItemsSource. But all of this could be avoided by using an ObservableCollection instead of a list as I noted previously.
EmployeesList = await employeesServices.GetEmployeesAsync();
listView.ItemsSource = null;
listView.ItemsSource = EmployeesList;
And to do the above you would have to give your ListView a name that you can use to reference it in code. YOu can set this name in the XAML for the ListView:
<ListView
ItemsSource="{Binding EmployeesList}"
HasUnevenRows="True"
IsPullToRefreshEnabled="True"
x:Name="listView"
>
But the ObservableCollection would be the preferable option.
Related
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");
}
}
This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 1 year ago.
I was following the xamarin 101 series on youtube and got an error in episode 6
This is how my MainPageViewModel.cs is
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Text;
using Xamarin.Forms;
namespace App2.ViewModels
{
class MainPageViewModel : INotifyPropertyChanged
{
public MainPageViewModel()
{
EraseCommand = new Command(() =>
{
TheNote = string.Empty;
});
SaveCommand = new Command(() =>
{
AllNotes.Add(TheNote);
TheNote = string.Empty;
});
}
public ObservableCollection<string> AllNotes { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
string theNote;
public string TheNote
{
get => theNote;
set
{
theNote = value;
var args = new PropertyChangedEventArgs(nameof(TheNote));
PropertyChanged?.Invoke(this, args);
}
}
public Command SaveCommand { get; }
public Command EraseCommand { get; }
}
}
and this is the MainPage.xaml file
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:App2.ViewModels"
x:Class="App2.MainPage">
<ContentPage.BindingContext>
<local:MainPageViewModel/>
</ContentPage.BindingContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="2*"/>
<RowDefinition Height=".5*"/>
<RowDefinition Height="2*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Image Source="vscode.png" BackgroundColor="PowderBlue" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="2"/>
<Editor Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="1" Placeholder="Enter note here" Text="{Binding TheNote}"/>
<Button Grid.Row="2" Grid.Column="0" Text="Save" Command="{Binding SaveCommand}" BackgroundColor="Green"/>
<Button Grid.Row="2" Grid.Column="1" Text="Delete" Command="{Binding EraseCommand}" BackgroundColor="Red"/>
<CollectionView ItemsSource="{Binding AllNotes}" Grid.Row="3" Grid.ColumnSpan="2">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Frame>
<Label Text="{Binding .}" FontSize="Title"/>
</Frame>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</Grid>
</ContentPage>
The EraseCommand works fine but the SaveCommand throws an error System.NullReferenceException: 'Object reference not set to an instance of an object.' on line 21 of MainPageViewModel.cs
In the tutorial everything works fine but i get an error why is that? or how do i fix it?
AllNotes is null because you have never initialized it, so it throws a NullRef exception when you try to add an item to it
AllNotes.Add(TheNote.ToString());
in your constructor add this line
AllNotes = new ObservableCollection<string>();
I have a DetailPage where I have put a ContenView into. In this ContenView I implemented a Grid because I wanted something like Refresh-Button for the JSON-ListView. That worked really well so far.
With your help I've accomplished to parse the JSON Data and put it into a ListView. My plan was to put this ListView in the 2nd row of my first Grid. I did another Grid in the first Grid where I tried to push the ListView inside, but the fields are empty...
Screenshot: http://imgur.com/a/Tj0kd
Here is my XAML Code from the DetailPage
<?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="ODC_Foto_ConWin_v1.YardList"
xmlns:local="clr-namespace:ODC_Foto_ConWin_v1"
Title="YardList"
BackgroundColor="LightGray">
<ContentPage.Content>
<Grid BackgroundColor="#004d93">
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Grid.Row="0" Text="File-Age: < 12m" FontSize="Small" Margin="5,5,0,0" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" HorizontalOptions="Start" VerticalOptions="Center" TextColor="White" FontAttributes="Bold"/>
<Image Source="sync.png" Grid.Column="1" Grid.Row="0" Scale="0.7" HorizontalOptions="End" VerticalOptions="Center" Margin="0,5,0,0"/>
<Label Grid.Column="2" Grid.Row="0" Text="Sync" FontSize="Small" HorizontalTextAlignment="Center" VerticalTextAlignment="Center" HorizontalOptions="End" VerticalOptions="Center" TextColor="White" Margin="0,5,5,0" FontAttributes="Bold"/>
<Button Grid.Column="1" Grid.Row="0" Grid.ColumnSpan="2" BackgroundColor="Transparent" VerticalOptions="Center" Clicked="yardListSyncButton_Clicked"/>
<Grid Grid.ColumnSpan="3" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ListView x:Name="listViewJson">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Grid.Column="0" Text="{Binding id}" BackgroundColor="Transparent" />
<Label Grid.Column="1" Text="{Binding kfz_nr}" BackgroundColor="Transparent" />
<Label Grid.Column="2" Text="{Binding kfz_kz}" BackgroundColor="Transparent" />
<Label Grid.Column="3" Text="{Binding timestamp}" BackgroundColor="Transparent" />
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
<ActivityIndicator x:Name="ProgressLoader" IsVisible="True" IsRunning="True"/>
</Grid>
</ContentPage.Content>
<ContentPage.ToolbarItems>
<ToolbarItem Icon="add.png" Activated="ToolbarItem_Activated"/>
</ContentPage.ToolbarItems>
</ContentPage>
and here is my C# Code:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace ODC_Foto_ConWin_v1
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class YardList : ContentPage
{
public YardList()
{
InitializeComponent();
GetJSON();
}
private void yardListSyncButton_Clicked(object sender, EventArgs e)
{
GetJSON();
}
private void ToolbarItem_Activated(object sender, EventArgs e)
{
// code for manual process
}
public async void GetJSON()
{
var client = new System.Net.Http.HttpClient();
var response = await client.GetAsync("http://x.x.x.x/xample.JSON");
string json = await response.Content.ReadAsStringAsync();
RootObject rootObject = new RootObject();
ListView listViewJson = new ListView();
if (json != "")
{
rootObject = JsonConvert.DeserializeObject<RootObject>(json);
}
listViewJson.ItemsSource = rootObject.process;
ProgressLoader.IsVisible = false;
}
public class Process
{
public string id { get; set; }
public string fzg_nr { get; set; }
public string fzg_kz { get; set; }
public string timestamp { get; set; }
}
public class RootObject
{
public string file_timestamp { get; set; }
public List<Process> process { get; set; }
}
}
}
Is this something about wrong DataBinding? Am I initiating the ItemSource at the wrong point?! Because when I set the datatemplate in the GetJSON() method the data is visible...
So far i tried a few examples but at some point there must be a little mistake. I am new programming (I'm normally a systemadministrator) so I don't where to start looking for the mistakes.
Any help is really appreciated.
Thanks in advance,
Paul
Short answer: don't create new list, use existing one - just remove ListView listViewJson = new ListView(); from GetJSON and it should work.
Long answer
In your XAML you have:
<ListView x:Name="listViewJson">
and in your GetJSON method you are creating NEW ListView and setting ItemSource like this:
public async void GetJSON()
{
ListView listViewJson = new ListView();
(...)
listViewJson.ItemsSource = rootObject.process;
}
So you have 2 ListViews, one in XAML, and one in GetJSON method. Your ListView in XAML has the same name like ListView in GetJSON method (listViewJson). Because ListView created in method is 'closer' in line listViewJson.ItemsSource = rootObject.process then ListView created in XAML,you are setting ItemSource of newly created ListView that was not added to your ContentView.
EDIT:
I see couple other minor issues in your code, not related to the question but because you are learning, I thing it's worth to point them out:
Labels in your ListView template do not have Grid as direct parent so Grid.Column="0" does not have any effect
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Grid.Column="0">
In C# it is a common practice to use PascalCase naming style for properties. I think that you did this: public string timestamp { get; set; } because of json format. You can use JsonPropertyAttribute to set your mapping between C# and json like this:
[JsonProperty("timestamp")]
public string Timestamp { get; set; }
You are hiding ProgressLoader at the end of GetJSON but you are not showing it at the beginning, so it is visible only on your fist GetJSON call (so in constructor).
now your are using code behind and it is totally ok when you are starting with Xamarin. Later on I would suggest you go take a look at MVVM pattern.
I want to create Xamarin search page with using MVVM. I have created a logic in my MainWindowViewModel which has to update my ListView once user entered a character into the searchar. But i have such result: In some reason UI is not updating
I do not know what i done wrong.
And also i would like to invoke ExecuteSearchCommand asynchronysly, and i will approsiate if you show how to implement it correctly.
Thanks.
<ContentPage.BindingContext>
<ViewModels:MainWindowViewModel/>
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout>
<SearchBar SearchCommand="{Binding SearchCommand}"
Text="{Binding EnteredText}"
/>
<Label Text="{Binding EnteredText}"></Label>
<ListView x:Name="lstContatos" ItemsSource="{Binding MyList}" HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout>
<Grid MinimumHeightRequest="80" HeightRequest="120" HorizontalOptions="FillAndExpand" >
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Text="{Binding PhrasalVerb}"/>
<Button Text="Delete" Grid.Column="1" BackgroundColor="Black" HeightRequest="30" WidthRequest="40" IsVisible="True"/>
</Grid>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
This is my ViewModel that binds to the View
public string EnteredText
{
get { return enteredText; }
set
{
enteredText = value;
this.SearchCommand.Execute(null);
OnPropertyChanged(nameof(EnteredText));
}
}
void ExecuteSearchCommand(object parameter)
{
if (enteredText.Length>=1)
{
MyList = new ObservableCollection<PhV_Get>(phrasalVerbGet
.Where(x => x.PhrasalVerb.ToLower()
.Contains(enteredText.ToLower())).ToList());
}
else
{
MyList = phrasalVerbGet;
}
}
public ObservableCollection<PhV_Get> MyList
{
set
{
phrasalVerbGet = value;
OnPropertyChanged(nameof(MyList));
}
get
{
return phrasalVerbGet;
}
}
public Command SearchCommand {
get
{
return new Command(ExecuteSearchCommand,
CanExecuteSeachCommand);
}
}
public bool CanExecuteSeachCommand(object parameter)
{
return true;
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
this.PropertyChanged?.Invoke(this, new
PropertyChangedEventArgs(propertyName));
}
The line MyList = new ObservableCollection in ExecuteSearchCommand is a no-no. Your listview is bound to the old ObservableCollection - when you create a new one, then you break that binding and the view will not update because it doesn't know anything about the new one.
When you need to change the contents of an observable collection, do it like this (in pseudocode):
MyList.Clear();
foreach (thing in myListOfThings)
{
MyList.Items.Add(thing);
}
That way you are updating the collection that the ListView is bound to, and the ListView will see the changes.
I'm Creating a Xamarin.Forms Portable Application. I have a database in my Visual Studio and I want to display the data inside it to a Xamarin ListView. But whenever I do that, the data are not being displayed on my Xamarin.Droid leaving only just a blank space. I tried it in UWP and it worked. How will I do that in my Xamarin.Droid?
(Screenshot of my Xamarin.Droid)
Notice that the ListView still occupy the space even if all the records are not being displayed. What do you think is the reason behind this? I even check this in my WEB API if the data are being retrieved and IT DOES.
Meaning, the real problem only occurs in displaying the records on a ListView. Hope you can help me.
Here are the codes I've tried.
ClientList.xaml
<?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="XamarinFormsDemo.Views.ClientListPage"
xmlns:ViewModels="clr-namespace:XamarinFormsDemo.ViewModels;assembly=XamarinFormsDemo"
xmlns:controls="clr-namespace:ImageCircle.Forms.Plugin.Abstractions;assembly=ImageCircle.Forms.Plugin.Abstractions"
BackgroundImage="bg3.jpg"
Title="Client List">
<ContentPage.BindingContext>
<ViewModels:CustomerVM/>
</ContentPage.BindingContext>
<StackLayout Orientation="Vertical">
<SearchBar Placeholder="Search" Text="{Binding Keyword}" SearchCommand="{Binding SearchCommand}" x:Name="txtSearch" />
<ListView ItemsSource="{Binding CustomerList}"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="10" RowSpacing="10" ColumnSpacing="5">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<controls:CircleImage Source="icon.png"
HeightRequest="66"
HorizontalOptions="CenterAndExpand"
Aspect="AspectFill"
WidthRequest="66"
Grid.RowSpan="2"
/>
<Label Grid.Column="1"
Text="{Binding CUSTOMER_NAME}"
TextColor="#24e97d"
FontSize="24"/>
<Label Grid.Column="1"
Grid.Row="1"
Text="{Binding CUSTOMER_CODE}"
TextColor="White"
FontSize="18"
Opacity="0.6"/>
<Label Grid.Column="1"
Grid.Row="2"
Text="{Binding CUSTOMER_CONTACT}"
TextColor="White"
FontSize="18"
Opacity="0.6"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackLayout Orientation="Vertical"
Padding="30,10,30,10"
HeightRequest="20"
BackgroundColor="#24e97d"
VerticalOptions="Center"
Opacity="0.5">
<Label Text="© Copyright 2016 SMESOFT.COM.PH All Rights Reserved "
HorizontalTextAlignment="Center"
VerticalOptions="Center"
HorizontalOptions="Center" />
</StackLayout>
</StackLayout>
</ContentPage>
ClientListViewModel.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
using Xamarin.Forms;
using XamarinFormsDemo.Models;
using XamarinFormsDemo.Services;
namespace XamarinFormsDemo.ViewModels
{
public class CustomerVM : INotifyPropertyChanged
{
private List<Customer> _customerList; // keep all customers
private List<Customer> _searchedCustomerList; // keep a copy for searching
private Customer _selectedCustomer = new Customer();
private string _keyword = "";
public string Keyword
{
get
{
return _keyword;
}
set
{
this._keyword = value;
// while keyword changed we filter Employees
//Filter();
}
}
private void Filter()
{
if (string.IsNullOrWhiteSpace(_keyword))
{
CustomerList = _searchedCustomerList;
}
else
{
// var lowerKeyword = _keyword.ToLower();
CustomerList = _searchedCustomerList.Where(r => r.CUSTOMER_NAME.ToLower().Contains(_keyword.ToLower())).ToList();
// EmployeesList = _searchedEmployeesList.Where(r => r.EMPLOYEE_NAME.Contains(_keyword)).ToList();
}
}
public List<Customer> CustomerList
{
get
{
return _customerList;
}
set
{
_customerList = value;
OnPropertyChanged();
}
}
public ICommand SearchCommand
{
get
{
return new Command((sender) =>
{
//var searchBar = (SearchBar)sender;
//this.Keyword = searchBar.Text;
Filter();
});
}
}
public CustomerVM()
{
InitializeDataAsync();
}
private async Task InitializeDataAsync()
{
var customerServices = new CustomerServices();
_searchedCustomerList = await customerServices.GetCustomerAsync();
CustomerList = await customerServices.GetCustomerAsync();
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
CustomerService.cs
using Plugin.RestClient;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using XamarinFormsDemo.Models;
namespace XamarinFormsDemo.Services
{
public class CustomerServices
{
public async Task<List<Customer>> GetCustomerAsync()
{
RestClient_Customer<Customer> restClient = new RestClient_Customer<Customer>();
var customerList = await restClient.GetCustomerAsync();//yung getasync ay pantawag as restclient
return customerList;
}
}
}
RestClient.cs
public class RestClient_Customer <T>
{
private const string WebServiceUrl = "http://localhost:50857/api/Customer/";
public async Task<List<T>> GetCustomerAsync()
{
var httpClient = new HttpClient();
var json = await httpClient.GetStringAsync(WebServiceUrl);
var taskModels = JsonConvert.DeserializeObject<List<T>>(json);
return taskModels;
}
}
In your ViewModel Change
public List<Customer> CustomerList
to
public ObservableCollection<Customer> CustomerList
and in your xaml, Change this
<ListView ItemsSource="{Binding CustomerList}"
to this
<ListView ItemsSource="{Binding CustomerList, Mode=TwoWay}"