How can I add an icon to the NavigationBar in all pages? - c#

So I'm trying to implement an icon in my NavigationBar, so it can be visible in all my pages... I'm using xamarin forms so I want it to be able in both android and IOS... I'm not sure how to do this, but I was trying to add this in my MyCar.xaml
<customControls:BasePage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
xmlns:views="clr-namespace:OficinaDigitalX.Views"
xmlns:customControls="clr-namespace:OficinaDigitalX.ViewModel"
x:Name="MyCar">
<customControls:BasePage.Content>
<AbsoluteLayout>
<StackLayout Padding="10, 0, 10, 0">
<ListView
ItemsSource="{Binding Path=CarList}"
IsPullToRefreshEnabled="False"
SelectedItem="{Binding Path=SelectedCar}">
<ListView.Header>
<Label Text="Os Meus Carros" FontSize="Large" />
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding VID}"
TextColor="Black"
Detail="{Binding LicensePlate}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</AbsoluteLayout>
</customControls:BasePage.Content>
</customControls:BasePage>
This is my MyCar.xaml.cs
namespace OficinaDigitalX.Views
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MyCar : ViewModel.BasePage
{
public MyCar()
{
Extensions.LoadFromXaml(this, typeof(MyCar));
BindingContext = new MyCarViewModel(Navigation);
}
this is my MyCarViewModel.cs
public class MyCarViewModel : ViewModelBase
{
public MyCarViewModel()
{
}
public MyCarViewModel(INavigation navigation)
{
this.Navigation = navigation;
this.SelectedCar = null;
GetClientCars();
}
private List<CarInfo> _CarList;
public List<CarInfo> CarList
{
get
{
return _CarList;
}
set
{
_CarList = value;
OnPropertyChanged("CarList");
}
}
private CarInfo _SelectedCar;
public CarInfo SelectedCar
{
get
{
return _SelectedCar;
}
set
{
_SelectedCar = value;
OnPropertyChanged("SelectedCar");
if (_SelectedCar != null)
ChangeWindow(_SelectedCar);
}
}
public INavigation Navigation { get; set; }
private void ChangeWindow(CarInfo car)
{
Navigation.PushAsync(new Interactions(car));
this.SelectedCar = null;
}
public void GetClientCars()
{
string command = "asdasd";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(string.Format(MainPage.server + command));
request.ContentType = "application/json";
request.Method = "POST";
//request.ContentLength = 999999;
using (var stream = new StreamWriter(request.GetRequestStream()))
{
string postData = JsonConvert.SerializeObject(command);
//stream.Write(postData);
stream.Flush();
stream.Close();
}
HttpWebResponse response = null;
try
{
response = (HttpWebResponse)request.GetResponse();
using (var responseString = new StreamReader(response.GetResponseStream()))
{
CarList = JsonConvert.DeserializeObject<List<CarInfo>>(responseString.ReadToEnd());
}
}
catch (WebException ex)
{
using (StreamReader reader = new StreamReader(ex.Response.GetResponseStream()))
{
}
throw;
}
}
}
}
Can anyone help with this?

The correct way of doing this in my Knowledge is extending the content page:
public class BasePage : ContentPage
{
public ICommand CartItemCommand { get; set; }
public ICommand NotificationPageCommand { get; set; }
public BasePage() : base()
{
CartItemCommand = new Command(async () => await GoCartItemCommand());
NotificationPageCommand = new Command(GoNotificationPage);
Init();
}
private void Init()
{
this.ToolbarItems.Add(new ToolbarItem()
{
Icon = "nav_notification_btn",
Command = NotificationPageCommand,
});
this.ToolbarItems.Add(new ToolbarItem()
{
Icon = "nav_cart_btn",
Command = CartItemCommand
});
}
}
}
And once you are done with that just use this BasePage everywhere in Place of ContentPage
In your XAML
<customControls:Basepage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
xmlns:views="clr-namespace:OficinaDigitalX.Views"
x:Class="OficinaDigitalX.MainPage"
xmlns:customControls="Your Control NameSpace"
x:Name="Main"
NavigationPage.HasNavigationBar="True">
</customControls:Basepage>
And in your Xaml.cs file
public partial class MainPage: BasePage
See to it that both partial classes inherit from one base class i.e BasePage or ContentPage as per your need.
And when you do not want to use the NavBar controls just inherit your XAML classes
from a Normal ContentPage.
Goodluck revert in case of queries!

You have two ways to implement this:
1- NavigationPage.TitleView which you can put your icon in that with the Image tag.
2- Using a custom control named NavBarView and use that in your pages inside ControlTemplate attribute.
The implementation of NavBarView could be like this :
<?xml version="1.0" encoding="UTF-8" ?>
<ControlTemplate
x:Class="YourAppName.View.Controls.NavBarView"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="46" />
<RowDefinition Height="1" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<StackLayout
Padding="10,5"
x:DataType="vm:XeposBaseViewModel"
BackgroundColor="{StaticResource XeposHeaderBackgroundColor}"
BindingContext="{TemplateBinding BindingContext}"
Orientation="Horizontal">
<!-- YOUR NAVBAR CONTENT HERE -->
</StackLayout>
<BoxView Grid.Row="1" BackgroundColor="Black" />
<ContentPresenter Grid.Row="2" />
</Grid>
</ControlTemplate>
And usage should be like this:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="YourAppName.View.Sell.SomeView"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:YourAppName.View.Controls;assembly=YourAppName.View"
ControlTemplate="{StaticResource NavBar}">
<!-- Your other page data is here -->
</ContentPage>
NavBar is defined in App.xaml like this, so you can use it with StaticResource:
<controls:NavBarView x:Key="NavBar" />

Related

New elements added to ObservableCollection from external Page are not persisted

I have two pages. On the first I want to see every elements from ObservableCollection and the second I want to add new element to ObservableCollection. When I give breakpoint I see that the ObservableCollection has new element, but when I back to the first page new element dosen't exist. What should I do?
There is code
public ListViewFlascardViewModel()
{
GoToAddFlashcard = new Command(goToAddFlashcard);
Fiszka = new ObservableCollection<Flashcard>();
Fiszka.Add(new Flashcard {Name = "hello" });
}
public Command GoToAddFlashcard { get; }
Flashcard flashcard = new Flashcard();
async void goToAddFlashcard()
{
await Shell.Current.GoToAsync(nameof(View.NoweFiszki));;
}
public ObservableCollection<Flashcard> Fiszka { get; set; }
}
And there is second page:
class NoweFiszkiViewModel
{
public Command SaveFlashcard { get; }
public NoweFiszkiViewModel()
{
SaveFlashcard = new Command(save);
}
ListViewFlascardViewModel list = new ListViewFlascardViewModel();
private async void save()
{
list.Fiszka.Add(new Flashcard { Name = "Bye" });
await Shell.Current.GoToAsync("..");
}
}
I try a lot of things, but nothing help. I am new in C# and I will be appreciated for every help.
I add whole code.
There is view
ListViewFlashcard
<ContentPage
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewmodels="clr-namespace:MVVM.ViewModel"
xmlns:model="clr-namespace:MVVM.Model"
x:Class="MVVM.View.ListViewFlashcard"
x:DataType="viewmodels:ListViewFlascardViewModel"
>
<ContentPage.BindingContext>
<viewmodels:ListViewFlascardViewModel/>
</ContentPage.BindingContext>
<StackLayout>
<Button
Command="{Binding GoToAddFlashcard}"
Text="Click Me"/>
<ListView
ItemsSource="{Binding Fiszka}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="model:Flashcard">
<TextCell Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
NoweFiszki
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewmodels="clr-namespace:MVVM.ViewModel"
x:Class="MVVM.View.NoweFiszki">
<ContentPage.BindingContext>
<viewmodels:NoweFiszkiViewModel/>
</ContentPage.BindingContext>
<ContentPage.Content>
<StackLayout>
<Entry Text="{Binding Text, Mode=TwoWay}"/>
<Button Text="Save"
Command="{Binding SaveFlashcard}"/>
</StackLayout>
</ContentPage.Content>
And Model
Flashcard
public class Flashcard
{
public string Name { get; set; }
}
And AppShell
<Shell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="MVVM.AppShell"
xmlns:local="clr-namespace:MVVM.View"
>
<FlyoutItem Title="Add Flashcard" Icon="icon_about.png">
<ShellContent ContentTemplate="{DataTemplate local:ListViewFlashcard}"/>
</FlyoutItem>
public partial class AppShell : Xamarin.Forms.Shell
{
public AppShell()
{
InitializeComponent();
Routing.RegisterRoute(nameof(NoweFiszki), typeof(NoweFiszki));
}
}
It is everything what I wrote.
A couple of things have to be corrected on your code:
In NoweFiszkiViewModel you are creating a new instance of ListViewFlascardViewModel every time, and adding elements on that new instance, which does not affect the instance to which ListViewFlashcard is actually bound.
To fix this, you will have to create a public static ListViewFlascardViewModel in ListViewFlashcard.xaml.cs and set the binding context to it, as follows
ListViewFlashcard.xaml.cs
public static ListViewFlascardViewModel vm { get; set; }
public ListViewFlashcard()
{
InitializeComponent();
vm = new ListViewFlascardViewModel();
}
protected override void OnAppearing()
{
base.OnAppearing();
BindingContext = vm;
}
then correct ListViewFlashcard.xaml as follows
ListViewFlashcard.xaml
<!--<ContentPage.BindingContext>
<viewmodels:ListViewFlascardViewModel/>
</ContentPage.BindingContext>-->
<StackLayout>
<Button
Command="{Binding GoToAddFlashcard}"
Text="Click Me"/>
<ListView ItemsSource="{Binding Fiszka}">
<ListView.ItemTemplate>
<DataTemplate x:DataType="viewmodels:Flashcard">
<TextCell Text="{Binding Name}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
Finally you have to correct NoweFiszkiViewModel.cs as follows, adding the new elements to the public static view model:
NoweFiszkiViewModel.cs
public Command SaveFlashcard { get; }
public NoweFiszkiViewModel()
{
SaveFlashcard = new Command(save);
}
//ListViewFlascardViewModel list = new ListViewFlascardViewModel();
private async void save()
{
ListViewFlashcard.vm.Fiszka.Add(new Flashcard { Name = "Bye" });
await Shell.Current.GoToAsync("..");
}

how to output data from one page to another from the collection of the selected item?

I have two XAML pages and one their common ViewModel page.I want to output data from one page to another from the collection of the selected item. It must be Label`s Text.
I have 2 problems
1)I can not bind text from label to the object field
2)If I bind Label`s Text to a variable.I can see data only on the current page. But if I go to another page and place the same label there, the information is not displayed.I do not understand why so because on the next page the same variable which already contains data
FIRST XAML 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="App1.TryPage">
<ContentPage.Content>
<StackLayout>
<CollectionView x:Name="AddCar" ItemsSource="{Binding Hearts}"
SelectionMode="None">
<CollectionView.ItemsLayout>
<GridItemsLayout Orientation="Vertical"
Span="2" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="135" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150" />
<ColumnDefinition Width="150" />
</Grid.ColumnDefinitions>
<Frame CornerRadius="10" BorderColor="Black" Padding="0" >
<Button
CornerRadius="10" HorizontalOptions="Center" VerticalOptions="Center" HeightRequest="135" WidthRequest="150"
BackgroundColor="{Binding CustButtonColor}" ImageSource="{Binding Image}"
Command="{ Binding BindingContext.ChangeColor,
Source={x:Reference Name=AddCar} }" CommandParameter="{Binding .}"/>
</Frame>
</Grid>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
<Label x:Name="small12" FontSize="Large" HorizontalOptions="Center" VerticalOptions="Start" Text="{Binding tryHeart.TypeHeart}" />
<Button Text="Navigate" Command="{Binding navigateCommand }">
</StackLayout>
</ContentPage.Content>
</ContentPage>
CODE BEHIND
public partial class TryPage : ContentPage
{
public TryPage()
{
InitializeComponent();
BindingContext = new TryPageCS(this.Navigation);
}
}
VIEW MODEL PAGE
public class TryPageCS : INotifyPropertyChanged
{
public ObservableCollection<CircleColor> Hearts { get; set; }
public ICommand ChangeColor { protected set; get; }
public TryHeart tryHeart { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
INavigation Navigation { get; set; }
public Command navigateCommand { get; set; }
public async Task GotoPage2()
{
await Navigation.PushModalAsync(new MainPage());
}
public TryPageCS(INavigation navigation)
{
tryHeart = new TryHeart();
this.Navigation = navigation;
this.navigateCommand = new Command(async () => await GotoPage2());
Hearts = new ObservableCollection<CircleColor>();
Hearts.Add(new CircleColor() { Name = "one", Image = "heart", CustButtonColor = Color.White });
Hearts.Add(new CircleColor() { Name = "two", Image = "heart", CustButtonColor = Color.White });
Hearts.Add(new CircleColor() { Name = "three", Image = "heart", CustButtonColor = Color.White });
Hearts.Add(new CircleColor() { Name = "four", Image = "heart", CustButtonColor = Color.White });
var DefaultCars = new ObservableCollection<CircleColor>();
DefaultCars = Hearts;
ChangeColor = new Command<CircleColor>((key) =>
{
foreach (var item in Hearts)
{
item.CustButtonColor = Color.White;
item.Image = "heart";
}
var car = key as CircleColor;
car.CustButtonColor = Color.LightCoral;
tryHeart.TypeHeart = car.Name;
});
}
}
SECOND 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"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:controls="clr-namespace:App1"
x:Class="App1.MainPage">
<StackLayout>
<Label FontSize="Large" Text="{Binding tryHeart.TypeHeart}" />
</StackLayout>
</ContentPage>
CODE BEHIND
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new TryPageCS(this.Navigation);
}
}
Also I have a class
public class TryHeart : INotifyPropertyChanged
{
string typeHeart;
public string TypeHeart
{
set
{
if (typeHeart != value)
{
typeHeart = value;
OnPropertyChanged("TypeHeart");
}
}
get
{
return typeHeart;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
I will explain why I need it. In my real project I have to collect information about the car from different pages. object of this class it will be my machine. Therefore I want to write down the collected data in object of a class and then on the last page to display data
On the SECOND XAML PAGE I write only THE SAME LABEL
<Label x:Name="small123" FontSize="Large" HorizontalOptions="Center" VerticalOptions="Start" Text="{Binding Name}" />
Please,help me with my 2 problems
1)Why I can not to write
<Label x:Name="small12" FontSize="Large" HorizontalOptions="Center" VerticalOptions="Start" Text="{Binding tryHeart.TypeHeart}" />
Information does not display
2)How I must display information from one page from collection view of selected item to another page
Navigation does not contain a definition for PushModalAsync
For question 1
TryHeart in the ViewModel is a private property in your case . You need to set it as public .
public TryHeart tryHeart {get;set;}
public TryPageCS()
{
//...
tryHeart = new TryHeart();
//...
}
For question 2
If you want to handle navigation logic in VM , you need to pass the current navigation from current page .
in ViewModel
Add a property
INavigation CurrentNavigation { get; set; }
public TryPageCS(INavigation navigation)
{
CurrentNavigation = navigation;
}
And now you can use the property in the method
await CurrentNavigation.PushModalAsync(new MainPage());
in ContentPage
Pass the Navigation as params
BindingContext = new TryPageCS(this.Navigation);

Xamarin.Forms Compiled Bindings does not work on DataTemplate

I am having trouble with Use compiled bindings in a DataTemplate.
I added XamlComilation in App.xaml.cs
// https://learn.microsoft.com/en-us/xamarin/xamarin-forms/xaml/xamlc
[assembly: XamlCompilation(XamlCompilationOptions.Compile)]
namespace Solution.Project
And I changed my DataTemplate to
<DataTemplate x:Key="RectLayerDataTemplate" x:DataType="{x:Type viewmodels:RectLayerViewModel}">
<forms:RectLayerView forms:ValueX="{Binding ValueX}"
forms:ValueY="{Binding ValueY}"
forms:ValueWidth="{Binding ValueWidth}"
forms:ValueHeight="{Binding ValueHeight}"
forms:Color="{Binding Color}" />
</DataTemplate>
However, DataTemplate does not applied to BindableLayout.ItemSource.
<AbsoluteLayout x:Name="CanvasLayout"
BindableLayout.ItemsSource="{Binding LayerViewModels}" />
Did you use AbsoluteLayout to make the layout overlap, so you think it didn't work?
I create a simple sample,with the DataTemplate and set it to the StackLayout BindableLayout,it works well.
the page.xaml:
<StackLayout x:Name="CanvasLayout" Orientation="Vertical"
BindableLayout.ItemsSource="{Binding LayerViewModels}" />
page.xam.cs:
public MyPage()
{
InitializeComponent();
BindingContext = new MyData();
LayerDataTemplate s = new LayerDataTemplate();
BindableLayout.SetItemTemplate(CanvasLayout, s.template);
}
MyData :
public class MyData
{
public ObservableCollection<RectLayerViewModel> LayerViewModels { set; get; } = new ObservableCollection<RectLayerViewModel>();
public MyData()
{
for (var x = 0; x < 6; x++)
{
LayerViewModels.Add(new RectLayerViewModel { Name = $"Added at {LayerViewModels.Count}", Type = 1 });
}
}
}
RectLayerViewModel:
public class RectLayerViewModel
{
public string Name { get; set; }
public int Type { get; set; }
}
DataTemplate:
<?xml version="1.0" encoding="UTF-8"?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local ="clr-namespace:EntryCa"
x:Class="EntryCa.LayerDataTemplate">
<DataTemplate x:Key="RectLayerDataTemplate" x:DataType="local:RectLayerViewModel">
<Label Text="{Binding Name}"
TextColor="Red"
FontAttributes="Bold" />
</DataTemplate>
Sample usage of compiled bindings in a DataTemplate. Below ItemViewModel is the type of the items in a ListView.ItemsSource:
XAML:
xmlns:vm="clr-namespace:MyViewModels"
...
<DataTemplate x:Key="ListViewItemTemplate" x:DataType="vm:ItemViewModel" >
...
<ListView ... ItemTemplate="{StaticResource ListViewItemTemplate}"/>
ItemViewModel:
namespace MyViewModels
{
public class ItemViewModel : ViewModelPropertyBase
...
public abstract class ViewModelPropertyBase : INotifyPropertyChanged
...

Xamarin Forms - How to display data binded (List from HttpClient)?

Can't display data in list view after json deserialization. How do i need to set it up in order to see it?
I'm data binding my reponse from http client using binding context so i can use it in my list view.
MainPage.xaml
<ContentPage
Title="Home"
>
<Grid>
<ListView ItemsSource="{Binding .}">
<ListView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding Description}"/>
<Label Text="{Binding Value}"/>
</StackLayout>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</ContentPage>
MainPage.cs
public MainPage()
{
DataService ds = new DataService();
BindingContext = ds.GetBillsAsync();
InitializeComponent();
Chart1.Chart = new BarChart { Entries = entries, LabelTextSize = (float)Convert.ToDouble(Device.GetNamedSize(NamedSize.Large, typeof(Label))), BackgroundColor = SKColors.Transparent };
}
DataService.cs
public class DataService
{
HttpClient client = new HttpClient();
public async Task<List<Bill>> GetBillsAsync()
{
try
{
string url = "my url";
var response = await client.GetStringAsync(url).ConfigureAwait(continueOnCapturedContext: false); ;
var bills = JsonConvert.DeserializeObject<List<Bill>>(response);
return bills;
}
catch (Exception e)
{
throw e;
}
}
}
I'm not getting any error messages, except for this output message "Binding: System.Threading.Tasks.Task`1[System.Collections.Generic.List`1[Test.Models.Bill]] can not be converted to type 'System.Collections.IEnumerable'" which i don't think it is the problem since i can see my list being populated correctly when i'm debugging.
Can you help me please?
Thank you in advance
Nikhil's method is one way ,if you only want to set the List which request from http client to the listview,you could binding to the ListView's ItemsSource property.
in the MainPage.xaml.cs :
public partial class MainPage: ContentPage
{
DataService ds = new DataService();
public MainPage()
{
InitializeComponent();
Chart1.Chart = new BarChart { Entries = entries, LabelTextSize = (float)Convert.ToDouble(Device.GetNamedSize(NamedSize.Large, typeof(Label))), BackgroundColor = SKColors.Transparent };
}
protected override async void OnAppearing()
{
base.OnAppearing();
listview.ItemsSource = await ds.GetBillsAsync();
}
}
in MianPage.xaml:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="Home"
x:Class="App18.MainPage">
<ContentPage.Content>
<StackLayout>
<ListView x:Name="listview">
<ListView.ItemTemplate>
<DataTemplate>
<StackLayout>
<Label Text="{Binding Description}"/>
<Label Text="{Binding Value}"/>
</StackLayout>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
You need to change the GetBillsAsync Method to retrieve the Array and then convert into List using Array.ToList() with System.Linq.
You need to implement a ViewModel for this. You can use the NuGet MVVM helpers for the BaseViewModel or implement your own.
using System.Windows.Input;
using MvvmHelpers.Interfaces;
namespace NameSpace.ViewModel
{
public partial class HomeViewModel : BaseViewModel
{
private ObservableCollection<Bill> _listOfbills = new ObservableCollection<Bill>();
public ObservableCollection<Bill> ListOfbills
{
get => _listOfbills;
set => SetProperty(ref _listOfbills, value);
}
public HomeViewModel()
{
DataService ds = new DataService();
ListOfbills = ds.GetBillsAsync()
}
}
}
Then your MainPage becomes:-
HomeViewModel vm;
public MainPage()
{
BindingContext = vm = new HomeViewModel();
InitializeComponent();
Chart1.Chart = new BarChart { Entries = entries, LabelTextSize = (float)Convert.ToDouble(Device.GetNamedSize(NamedSize.Large, typeof(Label))), BackgroundColor = SKColors.Transparent };
}
Then in the Xaml you can change this line as follows :-
<ListView ItemsSource="{Binding ListOfbills}">
These changes should work. Let me know if you face difficulties. Thanks!

Xamarin.Forms binding does not work

I try to rewrite my UWP C# app for Windows10 to Xamarin app using XAML. But Binding (for example here in ListView ItemSource=...) is not working for me and I don´t know why.
Visual Studio tells me, Cannot Resolve Symbol Recording due to unknown Data Context.
Here is my XAML (MainPage.xaml) for testing purpose:
<?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:XamarinTest;assembly=XamarinTest"
x:Class="XamarinTest.MainPage">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="100" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<ListView x:Name="listView" IsVisible="false" ItemsSource="{Binding Recording}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Orientation="Horizontal">
<Image Source="Accept" WidthRequest="40" HeightRequest="40" />
<StackLayout Orientation="Vertical" HorizontalOptions="StartAndExpand">
<Label Text="TEST" HorizontalOptions="FillAndExpand" />
<Label Text="TEST" />
</StackLayout>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</ContentPage>
Here is C# (MainPage.xaml.cs):
namespace XamarinTest
{
public partial class MainPage : ContentPage
{
public MainPage()
{
this.InitializeComponent();
this.AllTestViewModel = new RecordingViewModel();
this.BindingContext = AllTestViewModel;
}
public RecordingViewModel AllTestViewModel { get; set; }
}
}
And finally ViewModel (RecordingViewModel.cs):
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using XamarinTest.Model;
namespace XamarinTest.ViewModel
{
public class RecordingViewModel : INotifyPropertyChanged
{
public ObservableCollection<Recording> Recordings { get; } = new TrulyObservableCollection<Recording>();
public RecordingViewModel()
{
Recordings.Add(new RecordingTest2()
{
TestName = "Test 1",
TestNote = "Vytvoreni DB",
TestTime = new TimeSpan(0, 0, 0)
});
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public sealed class TrulyObservableCollection<T> : ObservableCollection<T>
where T : INotifyPropertyChanged
{
public TrulyObservableCollection()
{
CollectionChanged += FullObservableCollectionCollectionChanged;
}
public TrulyObservableCollection(IEnumerable<T> pItems) : this()
{
foreach (var item in pItems)
{
this.Add(item);
}
}
private void FullObservableCollectionCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.NewItems != null)
{
foreach (Object item in e.NewItems)
{
((INotifyPropertyChanged)item).PropertyChanged += ItemPropertyChanged;
}
}
if (e.OldItems != null)
{
foreach (Object item in e.OldItems)
{
((INotifyPropertyChanged)item).PropertyChanged -= ItemPropertyChanged;
}
}
}
private void ItemPropertyChanged(object sender, PropertyChangedEventArgs e)
{
NotifyCollectionChangedEventArgs args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, sender, sender, IndexOf((T)sender));
OnCollectionChanged(args);
}
}
}
Everything (models and viewmodels) are working in native UWP Windows 10 app. Only the Binding and making same view is problem in Xamarin. Could someone please help with binding?
Thx.
EDIT
Recording.cs is here:
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace XamarinTest.Model
{
public abstract class Recording : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string TestName { get; set; }
private TimeSpan _testTime;
private string _testNote;
private string _actualIco = "Play";
private bool _isActive = false;
private bool _enabled = true;
public double IcoOpacity { get; private set; } = 1.0;
public string ActualIco
{
get => _actualIco;
set
{
if (_actualIco == null) _actualIco = "Admin";
_actualIco = value;
NotifyPropertyChanged("ActualIco");
}
}
public bool IsActive
{
get => _isActive;
set
{
if (_isActive == value) return;
_isActive = value;
IcoOpacity = !value ? 1.0 : 0.3;
NotifyPropertyChanged("IsActive");
NotifyPropertyChanged("IcoOpacity");
}
}
public bool Enabled
{
get => _enabled;
set
{
if (_enabled == value) return;
_enabled = value;
NotifyPropertyChanged("Enabled");
}
}
public string TestNote
{
get => _testNote;
set
{
if (_testNote == value) return;
_testNote = value;
NotifyPropertyChanged("TestNote");
}
}
public TimeSpan TestTime
{
get => _testTime;
set
{
if (_testTime == value) return;
_testTime = value;
NotifyPropertyChanged("TestTime");
}
}
protected Recording()
{
TestName = "Unkonwn";
TestNote = "";
_testTime = new TimeSpan(0, 0, 0);
}
protected Recording(string testName, string testNote, TimeSpan testTime)
{
TestName = testName;
TestNote = testNote;
_testTime = testTime;
}
public string OneLineSummary => $"{TestName}, finished: "
+ TestTime;
private void NotifyPropertyChanged(string propertyName = "")
{
var handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public abstract bool playTest();
}
}
I tried add DataContext in XAML (postet in origin question), because of intellisence like this:
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
xmlns:dvm="clr-namespace:XamarinTest.ViewModel"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
d:DataContext="{system:Type dvm:RecordingViewModel}"
and this to Grid:
<Label Text="{Binding Recordings[0].TestName}" Grid.Row="0" Grid.Column="2" />
IntelliSence is OK, but text doesn´t show in app.
Finally is working!
XAML should looks like code below.
Imporant is xmls:viewModel="..." and <ContentPage.BindingContext>...</>.
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:viewModel="clr-namespace:XamarinTest.ViewModel;assembly=XamarinTest"
x:Class="XamarinTest.MainPage"
>
<ContentPage.BindingContext>
<viewModel:RecordingViewModel/>
</ContentPage.BindingContext>
<ListView x:Name="listView" ItemsSource="{Binding Recordings}" Grid.Row="1" Grid.Column="1">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout Orientation="Horizontal">
<Image Source="Accept" WidthRequest="40" HeightRequest="40" />
<StackLayout Orientation="Vertical" HorizontalOptions="StartAndExpand">
<Label Text="{Binding TestName}" HorizontalOptions="FillAndExpand" />
<Label Text="{Binding TestNote}" />
</StackLayout>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</ContentPage>
and MainPage.xaml.cs is okey
namespace XamarinTest
{
public partial class MainPage : ContentPage
{
public MainPage()
{
this.InitializeComponent();
this.AllTestViewModel = new RecordingViewModel();
this.BindingContext = AllTestViewModel;
}
public RecordingViewModel AllTestViewModel { get; set; }
}
}
looking at your ViewModel, it looks like there is no Recording member, but you do have a Recordings member.
EDIT
So you are adding your DataContext in the code behind so ignore the Xaml part.
Your View (MainPage.xaml) has a ViewModel(RecordingViewModel.cs). The ViewModel has a member called Recordings (a collection of type Recording). But in your Xaml, you are try to bind to Recording.
Change:
<ListView x:Name="listView" IsVisible="false" ItemsSource="{Binding Recording}">
to:
<ListView x:Name="listView" IsVisible="false" ItemsSource="{Binding Recordings}">
2nd EDIT
The only Labels in your example is the one inside of the ListView yes?
If so, you can access the Recordings children like TestNote by:

Categories

Resources