Data Binding in Xamarin.Forms - asp.net

I want all the records created in ASP.NET Web Application to be shown in my Mobile App Xamarin.Forms. What's happening to my program is that I was able to create records in my Web Application but I wasn't able to make it bind it in my Xamarin.Forms Mobile app. I have created a MainViewModel that will get the records from the Web Application which I have binded in my MainPage.
What can I do to show all the records I have created?
These are my codes. Any help will be highly appreciated. Thank you so much.
MainPageMain.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"
xmlns:local="clr-namespace:XamarinDemoApp"
x:Class="XamarinDemoApp.MainPageMain"
xmlns:ViewModels="clr-namespace:XamarinDemoApp.ViewModels;assembly=XamarinDemoApp"
BackgroundColor="Teal"
Title=" Title Bar">
<ContentPage.BindingContext>
<ViewModels:MainViewModel/>
</ContentPage.BindingContext>
<StackLayout Orientation="Vertical">
<ListView ItemsSource="{Binding EmployeesList}"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Name}"
FontSize="24"/>
<Label Text="{Binding Department}"
FontSize="24"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Label Text="This is the Main Page"/>
</StackLayout>
</ContentPage>
MainViewModel.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 XamarinDemoApp.Models;
using XamarinDemoApp.Services;
namespace XamarinDemoApp.ViewModels
{
public class MainViewModel : INotifyPropertyChanged
{
private List<Employee> _employeesList;
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)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

In your code you are using List<Employee> as a source to your listview.
Refer this link. It says :
If you want the ListView to automatically update as items are added, removed and changed in the underlying list, you'll need to use an ObservableCollection. ObservableCollection is defined in System.Collections.ObjectModel and is just like List, except that it can notify ListView of any changes
Update your code to use ObservableCollection.
ObservableCollection<Employee> employees= new ObservableCollection<Employee>();
// Convert/Add your list in observable collection
foreach (Employee e in EmployeesList )
{
employees.Add(new Employee { Name=e.Name,Department=e.Department } )
}
After that you can set this as a source to your ListView.
UPDATE
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using XamarinDemoApp.Models;
using XamarinDemoApp.Services;
namespace XamarinDemoApp.ViewModels
{
public class MainViewModel : INotifyPropertyChanged
{
private ObservableCollection<Employee> _employeesList;
public ObservableCollection<Employee> EmployeesList
{
get {return _employeesList;}
set
{
_employeesList = value;
OnPropertyChanged();
}
}
public MainViewModel()
{
InitializeDataAsync();
}
private async Task InitializeDataAsync()
{
var employeesServices = new EmployeesServices();
var EmployeesListNew = await employeesServices.GetEmployeesAsync();
foreach (Employee e in EmployeesListNew )
{
EmployeesList.Add(new Employee { Name=e.Name,Department=e.Department } )
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

Related

Dynamic CollectionView with FreshMvvm Xamarin.Forms

Is there a way to generate dynamic layouts in collectionview?
I'm familiar with DataTemplateSelector https://www.xamarinhelp.com/xamarin-forms-datatemplateselector/
and I'm familiar with FreshMvvm content by convention.
Can I add objects to an observable collection and have FreshMvvm resolve the content page?
<?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="trycollections.Pages.MainPage">
<CollectionView ItemsSource="{Binding MyItems}">
<CollectionView.ItemsLayout>
<GridItemsLayout Span="1" Orientation="Vertical" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding .}"></ContentPresenter>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</ContentPage>
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
namespace trycollections.ViewModels
{
public class MainViewModel : FreshMvvm.FreshBasePageModel
{
public ObservableCollection<object> MyItems { get; set; } =
new ObservableCollection<object>
{
new ItemOneViewModel(),
new ItemTwoViewModel(),
new ItemOneViewModel(),
new ItemOneViewModel(),
new ItemOneViewModel(),
};
}
public class ItemOneViewModel : FreshMvvm.FreshBasePageModel
{
public string ThisIsOneText { get; set; } = $"Hello world from {nameof(ItemOneViewModel)}";
}
public class ItemTwoViewModel : FreshMvvm.FreshBasePageModel
{
public string ThisIsTwoText { get; set; } = $"Hello world from {nameof(ItemTwoViewModel)}";
}
}

Weird space on top of the listview after updating iOS to 15

I'm using
Xamarin.Froms: 5.0.0.24125
Xcode Version: 13.0 (13A233)
Visual Studio for Mac Community Version 8.10.9 (Build 3)
NuGet Version: 5.9.0.7134
Xamarin.iOS Version: 14.20.0.27 (Visual Studio Community)
MainPage.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="ListViewTopSpace.MainPage"
xmlns:ios="clr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;assembly=Xamarin.Forms.Core"
Title="Page title"
ios:Page.UseSafeArea="true">
<ListView ItemsSource="{Binding Users}"
BackgroundColor="Blue"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Margin="0,0,0,0.5"
Padding="10,0"
BackgroundColor="AliceBlue">
<Label FontSize="12">
<Label.FormattedText>
<FormattedString>
<Span Text="User Id: "
FontAttributes="Bold"/>
<Span Text="{Binding Id, Mode=OneWay}"/>
</FormattedString>
</Label.FormattedText>
</Label>
<Label FontSize="12">
<Label.FormattedText>
<FormattedString>
<Span Text="User name: "
FontAttributes="Bold"/>
<Span Text="{Binding Name, Mode=OneWay}"/>
</FormattedString>
</Label.FormattedText>
</Label>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</ContentPage>
MainPageViewModel.cs
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace ListViewTopSpace
{
public class MainPageViewModel : ObservableProperty
{
public MainPageViewModel()
{
Users = new ObservableCollection<UserListModel>()
{
new UserListModel { Id = 1, Name = "User 1" },
new UserListModel { Id = 2, Name = "Test user 1" },
new UserListModel { Id = 3, Name = "User 2" },
new UserListModel { Id = 4, Name = "Test user 2" },
};
}
private ObservableCollection<UserListModel> _users;
public ObservableCollection<UserListModel> Users
{
get { return _users; }
set
{
_users = value;
OnPropertyChanged(nameof(Users));
}
}
}
public class UserListModel
{
public int Id { get; set; }
public string Name { get; set; }
}
public class ObservableProperty : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Result:
[1]: https://i.stack.imgur.com/BKn29.png
This highlighted space shouldn’t be there.
#Colex is right, but you have to replace this string of code:
Control.TableHeaderView = new UIKit.UIView(frame: new CoreGraphics.CGRect(0, 0, 0, float.MinValue));
with this:
Control.SectionHeaderTopPadding = new nfloat(0);
Full code of Renderer:
[assembly: ExportRenderer(typeof(ListView), typeof(LVRenderer))]
namespace YourNameSpace.iOS
{
public class LVRenderer : ListViewRenderer
{
public LVRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.SectionHeaderTopPadding = new nfloat(0);
}
}
}
}
The problem is related with apple not xamarin side .
Please see this thread : https://developer.apple.com/forums/thread/683980.
To solve it we can add a custom renderer for ListView
Try the following code
[assembly:ExportRenderer(typeof(ListView),typeof(MyRenderer))]
namespace Formssss.iOS
{
public class MyRenderer : ListViewRenderer
{
public MyRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
if(Control != null)
{
Control.TableHeaderView = new UIKit.UIView(frame: new CoreGraphics.CGRect(0, 0, 0, float.MinValue));
}
}
}
}
After updating to xamarin.forms version to latest from NuGet manager will solve this issue.
Updating to version 5.0.0.2244 will definitely fix this issue
and extra top padding from list view will be removed on iOS 15.0 or greater

Xamarin Forms: How can i correctly bind data from two view models to a single view?

This is the short code for testing purpose. The problem is that the UI is not displaying the Text from the Label which is binded with ViewModelB. In debugging when I hover the mouse in xaml over the Text from the Label I see the right binding data is there, but the UI simply won't display. With ViewModelA there are no problems.
In XAML:
<StackLayout>
<StackLayout>
<StackLayout.BindingContext>
<testbinding:ViewModelA/>
</StackLayout.BindingContext>
<Button Command ="{Binding Get}"/>
</StackLayout>
<StackLayout>
<StackLayout.BindingContext>
<testbinding:ViewModelB/>
</StackLayout.BindingContext>
<Label Text="{Binding Metadata}"/>
</StackLayout>
ViewModelA: where BaseViewModel is a INotifyPropertyChanged interface
public ViewModelA:BaseViewModel
{
public ViewModelA()
{
Get = new Command(SendText);
vmB = new ViewModelB();
}
ViewModelB vmB;
public ICommand Get { get; }
private void SendText()
{
string data = "someText";
vmB.GetMetadata(data);
}
}
ViewModelB is like this:
class ViewModelB:BaseViewModel
{
private string _metadata = string.Empty;
public string Metadata
{
get { return _metadata; }
set
{
_metadata = value;
OnPropertyChanged();
}
}
GetMetadata()
{
Metadata = "Some text";
}
}
In ViewModelA there are more properties which I need and in ViewModelB is just one property which gets data from a function. I could make just one ViewModel from both of them which works fine, but I'm trying to keep them smaller and organized. I already tried so many scenarios and is getting really frustrating.
Thanks for helping.
In the second StackLayout in your xaml file you're not binding it's BindingContext property to the ViewModelB instance from ViewModelA but instead you are creating a new one.
Here's a working solution for you:
public class ViewModelA : BaseViewModel
{
public ViewModelB ViewModelB { get; }
public ICommand GetMetadataCommand { get; }
public ViewModelA()
{
ViewModelB = new ViewModelB();
GetMetadataCommand = new Command((_) => GetMetadata());
}
private void GetMetadata()
{
string data = "someText";
ViewModelB.GetMetadata(data);
}
}
public class ViewModelB : BaseViewModel
{
private string _metadata;
public string Metadata
{
get { return _metadata; }
set
{
_metadata = value;
OnPropertyChanged();
}
}
public void GetMetadata(string data)
{
Metadata = data;
}
}
XAMl:
<StackLayout>
<StackLayout x:Name="StackLayout1">
<StackLayout.BindingContext>
<local:ViewModelA />
</StackLayout.BindingContext>
<Button Command ="{Binding GetMetadataCommand}"/>
</StackLayout>
<StackLayout BindingContext="{Binding Source={x:Reference StackLayout1}, Path=BindingContext.ViewModelB}">
<Label Text="{Binding Metadata}" />
</StackLayout>
</StackLayout>

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation?

I getting this erro when I run my application after adding the Xamarin.Forms.Maps NuGet package. I am trying to display a Map in my MapPage. I have hadded name space definitions to my XAML file.
I have and I have added Xamarin.FormsMaps.Init(this, bundle) to the Android project in the MainActivity and to the iOS project using Xamarin.FormsMaps.Init(); in the AppDelegate file.
I searched google and stackoverflow and couldn't find any useful document.
here is the MainPage.xaml code
<?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:TravelRecord"
x:Class="TravelRecord.MainPage">
<StackLayout VerticalOptions="Center" Margin="20,0">
<Entry Placeholder="Enter Your Email... " Keyboard="Email" x:Name="emailEntry"
TextColor="{StaticResource blueColor}" Text="Test"/>
<Entry Placeholder="Enter Your Password... " TextColor="{StaticResource blueColor}"
IsPassword="True" x:Name="passwordEntry" Text="Test"/>
<Button Text="Log in" x:Name="LoginButton" Clicked="LoginButton_Clicked" Margin="0,50,0,0"
BackgroundColor="{StaticResource blueColor}" TextColor="White"/>
</StackLayout>
</ContentPage>
and the MainPage.cs file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace TravelRecord
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private void LoginButton_Clicked(object sender, EventArgs e)
{
bool emailIsNullOrEmpty = string.IsNullOrEmpty(emailEntry.Text);
bool passwordIsNullOrEmpty = string.IsNullOrEmpty(passwordEntry.Text);
if (emailIsNullOrEmpty || passwordIsNullOrEmpty)
{
}
else
{
Navigation.PushAsync(new HomPage());
}
}
}
}
from which I navigate to the MapsPage using the Login button.
here is the MapsPage.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="TravelRecord.MapPage"
xmlns:maps="clr-namespace:Xamarin.Forms.GoogleMaps;assembly=Xamarin.Forms.GoogleMaps">
<ContentPage.Content>
<maps:Map />
</ContentPage.Content>
</ContentPage>
and the MapsPage.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
namespace TravelRecord
{
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
}
private void LoginButton_Clicked(object sender, EventArgs e)
{
bool emailIsNullOrEmpty = string.IsNullOrEmpty(emailEntry.Text);
bool passwordIsNullOrEmpty = string.IsNullOrEmpty(passwordEntry.Text);
if (emailIsNullOrEmpty || passwordIsNullOrEmpty)
{
}
else
{
Navigation.PushAsync(new HomPage());
}
}
}
}
here MainActivity.cs of the android project:
using System;
using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using System.IO;
namespace TravelRecord.Droid
{
[Activity(Label = "TravelRecord", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle bundle)
{
TabLayoutResource = Resource.Layout.Tabbar;
ToolbarResource = Resource.Layout.Toolbar;
base.OnCreate(bundle);
global::Xamarin.Forms.Forms.Init(this, bundle);
Xamarin.FormsMaps.Init(this, bundle);
string dataBaseName = "travelRecord.db";
var dataBaseBath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
string fullPath = Path.Combine(dataBaseBath, dataBaseName);
LoadApplication(new App(fullPath));
}
}
}
and the AppDelegate of the iOS project
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Foundation;
using UIKit;
namespace TravelRecord.iOS
{
// The UIApplicationDelegate for the application. This class is responsible for launching the
// User Interface of the application, as well as listening (and optionally responding) to
// application events from iOS.
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
//
// This method is invoked when the application has loaded and is ready to run. In this
// method you should instantiate the window, load the UI into it and then make the window
// visible.
//
// You have 17 seconds to return from this method, or iOS will terminate your application.
//
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
global::Xamarin.Forms.Forms.Init();
Xamarin.FormsMaps.Init();
string dataBaseName = "travelRecord.db";
var dataBaseBath =Path.Combine( System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal),"..","Library");
string fullPath = Path.Combine(dataBaseBath, dataBaseName);
LoadApplication(new App(fullPath));
return base.FinishedLaunching(app, options);
}
}
}
I have also added this line to the AndroidManifest.xml file
<meta-data android:name="com.google.android.geo.API_KEY" android:value ="AIzaSyBXuv3VQ4-5pxLcbje-397wvmb3y6RoxsE"></meta-data>
Does anyone know how to solve this ? thanks

Connecting Xamarin.Forms to Web Services

Good Day Everyone. I want all the records created in ASP.NET Web Application to be shown in my Mobile App Xamarin.Forms. What's happening to my program is that I was able to create records in my Web Application and save it, but I wasn't able to make it appear in my Xamarin.Forms Mobile app. I have created a MainViewModel that will get the records from the Web Application which I have binded to my MainPage. These are my codes:
MainPageMain.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"
xmlns:local="clr-namespace:XamarinDemoApp"
x:Class="XamarinDemoApp.MainPageMain"
xmlns:ViewModels="clr-namespace:XamarinDemoApp.ViewModels;assembly=XamarinDemoApp"
BackgroundColor="Teal"
Title=" Title Bar">
<ContentPage.BindingContext>
<ViewModels:MainViewModel/>
</ContentPage.BindingContext>
<StackLayout Orientation="Vertical">
<ListView ItemsSource="{Binding EmployeesList}" HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Name}" FontSize="24"/>
<Label Text="{Binding Department}" FontSize="24"/>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<Label Text="This is the MainPage"/>
</StackLayout>
MainViewModel.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using XamarinDemoApp.Models;
using XamarinDemoApp.Services;
namespace XamarinDemoApp.ViewModels
{
public class MainViewModel : INotifyPropertyChanged
{
private List<Employee> _employeesList;
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)
{
PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
You should use ObservableCollections, something like this:
private ObservableCollection<Employee> _employeesList;
public ObservableCollection<Employee> EmployeesList
{
get { return _employeesList; }
set { Set(() => EmployeesList, ref _employeesList, value); }
}
then
private async Task InitializeDataAsync()
{
var employeesServices = new EmployeesServices();
var employees = await employeesServices.GetEmployeesAsync();
EmployeesList = new ObservableCollection<Employee>(employees);
}

Resources