Have a small app that I would like to capture information from my code behind and pass it to my ViewModel. The ViewModel has access to a 3rd Party API, so I would send that data too. I don't know the best way to pass that type of information.
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:vm="clr-namespace:BlueRC.ViewModels"
x:Class="BlueRC.Views.MainPage"
x:DataType="vm:MainViewModel">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="10*" />
<RowDefinition Height="90*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="0"
Text="Connect to Bluetooth"
FontAttributes="Bold"
HorizontalOptions="CenterAndExpand"
Command="{Binding BluetoothConnectClickCommand}"/>
<Label Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2"
Text="{Binding ConsoleMessage}"
x:Name="StatusMessage"/>
<BoxView
Grid.ColumnSpan="3" Grid.Row="1"
VerticalOptions="Center"
HorizontalOptions="Center"
Color="Gray">
<BoxView.GestureRecognizers>
<PanGestureRecognizer PanUpdated="PanGestureRecognizer_PanUpdated" />
</BoxView.GestureRecognizers>
</BoxView>
</Grid>
</ContentPage>
Code behind
MainPage.xaml.cs
namespace BlueRC.Views;
public partial class MainPage : ContentPage
{
private double deltaX;
private double deltaY;
public MainPage(MainViewModel viewModel)
{
InitializeComponent();
BindingContext = viewModel;
}
private void PanGestureRecognizer_PanUpdated(object sender, PanUpdatedEventArgs e)
{
deltaX = e.TotalX; // want to send deltaX to my ViewModel
deltaY = e.TotalY; // want to send deltaY to my ViewModel
if (e.StatusType == GestureStatus.Completed || e.StatusType == GestureStatus.Canceled)
{
// Capture the deltas and send the info over. Then reset them.
deltaX = 0f;
deltaY = 0f;
}
StatusMessage.Text = $"X: {deltaX:N}, Y: {deltaY:N} StatusType: {e.StatusType}";
}
How can I send deltaX and deltaY to my ViewModel? If it was a matter of creating a RelayCommand and linking it I would have done that ages ago. PanGestureRecognizer doesn't contain the Command attribute for me to data bind to.
Any advice?
Getting hold of ViewModel.
Two Possibilities:
ViewModel passed in to View's constructor as a parameter.
Then store it in a local field, so can access it later:
public MyView(MyViewModel vm)
{
InitializeComponent();
VM = vm;
BindingContext = vm;
}
private MyViewModel VM;
BindingContext is set in XAML.
private MyViewModel VM => BindingContext as MyViewModel;
Either way, to access:
VM.SomeProperty = ...;
Related
I have a ListView with items in Binding via the ItemsSource property. I cannot understand why the DataTemplate data is not displayed. They are still downloaded correctly from the internet but are not displayed. I've tried both inserting the ItemsSource property from c # and xaml, but the result doesn't change
xaml
<yummy:PancakeView x:Name="ViewFrasi" IsVisible="False" Grid.Row="1" CornerRadius="30,30,0,0" BackgroundColor="White" VerticalOptions="FillAndExpand">
<yummy:PancakeView.Border>
<yummy:Border Color="Blue" Thickness="4"/>
</yummy:PancakeView.Border>
<Grid Margin="15">
<ListView x:Name="Frasi"
ItemsSource="{Binding FrasiJsonOnline1}">
<ListView.Header>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="40"/>
</Grid.RowDefinitions>
<BoxView CornerRadius="100" Grid.Row="0" HorizontalOptions="End" WidthRequest="40">
<BoxView.Background>
<LinearGradientBrush StartPoint="1,0" EndPoint="1,1">
<GradientStop Color="Blue" Offset="0" />
<GradientStop Color="LightBlue" Offset="1.0" />
</LinearGradientBrush>
</BoxView.Background>
</BoxView>
<Image Source="checked.png" Grid.Row="0" HorizontalOptions="End" HeightRequest="20" Margin="0,0,10,0"/>
<Button x:Name="BtSave" IsVisible="False" Clicked="BtSave_Clicked" BackgroundColor="Transparent" Grid.Row="0" HorizontalOptions="End" HeightRequest="25"/>
<ImageButton Source="close.png" Grid.Row="0" HorizontalOptions="Start" HeightRequest="20" Margin="5,0,0,0" Clicked="Close_Clicked" BackgroundColor="Transparent"/>
</Grid>
</ListView.Header>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<ScrollView>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="5*"/>
</Grid.ColumnDefinitions>
<ImageButton Grid.Row="0" Grid.Column="0" Source="IconCopy.png" Clicked="CopyClipboard_Clicked"/>
<Label Grid.Row="0" Grid.Column="1" Text="Test" TextColor="Black" FontSize="15"/>
</Grid>
</ScrollView>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</yummy:PancakeView>
c#
private async void CategoryView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selected = e.CurrentSelection;
ClassCategory model = e.CurrentSelection.FirstOrDefault() as ClassCategory;
WebClient clientw = new WebClient();
clientw.Credentials = new NetworkCredential("xxxxxxx", "xxxxxx");
string Frasi1 = "ftp://epiz_27426656#ftpupload.net/htdocs/" + model.Titolo + ".json";
string contents1 = await clientw.DownloadStringTaskAsync(Frasi1);
ObservableCollection<FraseClass> FrasiJsonOnline1 = JsonConvert.DeserializeObject<ObservableCollection<FraseClass>>(contents1);
ViewFrasi.IsVisible = true;
ViewFrasi.TranslationY = 600;
ViewFrasi.TranslateTo(0, 0, 500, Easing.SinInOut);
}
You are using a local variable, make it a public property outside the method and change its value inside the method as shown here:
public ObservableCollection<FraseClass> FrasiJsonOnline1 { get; set; }
private async void CategoryView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selected = e.CurrentSelection;
ClassCategory model = e.CurrentSelection.FirstOrDefault() as ClassCategory;
WebClient clientw = new WebClient();
clientw.Credentials = new NetworkCredential("xxxxxxx", "xxxxxx");
string Frasi1 = "ftp://epiz_27426656#ftpupload.net/htdocs/" + model.Titolo + ".json";
string contents1 = await clientw.DownloadStringTaskAsync(Frasi1);
FrasiJsonOnline1 = JsonConvert.DeserializeObject<ObservableCollection<FraseClass>>(contents1);
ViewFrasi.IsVisible = true;
ViewFrasi.TranslationY = 600;
ViewFrasi.TranslateTo(0, 0, 500, Easing.SinInOut);
}
When you bind the ListView's ItemsSource to FrasiJsonOnline1.
That means you are binding to the BindingContext.FrasiJsonOnline1.
ItemsSource="{Binding BindingContext.FrasiJsonOnline1}".
So FrasiJsonOnline1 should be a property of your BindingContext. For example, if you BindingContext is myViewModel, FrasiJsonOnline1 should be a property of myViewModel, then the binding will success:
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new myViewModel();
}
}
public class myViewModel
{
public ObservableCollection<FraseClass> FrasiJsonOnline1 { get; set; }
public myViewModel()
{
FrasiJsonOnline1 = new ObservableCollection<FraseClass>();
//...your codes
}
}
If you define the FrasiJsonOnline1 like the way you do:
public class myViewModel
{
public myViewModel()
{
}
private async void CategoryView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ObservableCollection<FraseClass> FrasiJsonOnline1 = JsonConvert.DeserializeObject<ObservableCollection<FraseClass>>(contents1);
}
}
FrasiJsonOnline1 is defined inside the method and it is a local variable. The BindingContext(myViewModel) does not have a property called FrasiJsonOnline1 and the binding won't work.
What I'm trying to implement is a message chat bubble wherein you tap the control/message and the message would expand showing details of the date and the seen / sent status below. I do have a DataTemplate Selector for the different controls I have for the Sender and the Receiver.
My problem is changing the Height of the message in the ListView. I tried implementing the Binding of the RowDefinition to whatever the Height variable is in my Message class (the class that holds info regarding the message). Although the height was updated, it didn't reflect on the ListView. I've scoured over the internet for existing chat UI templates but I think most of them are paid. Hence, I'm trying to do follow Change WPF DataTemplate for ListBox item if selected. But for Xamarin, there's no ListBoxItem as there's only a ListView.
On a further note, I am working on Android and iOS. An example that is cross-platform to solve this will be greatly appreciated. Below are parts of my code.
datatemplate.cs
class MessageTemplateSelector : DataTemplateSelector
{
public MessageTemplateSelector()
{
ReceiverDataTemplate = new DataTemplate(typeof(MessageReceiver));
SenderDataTemplate = new DataTemplate(typeof(MessageSender));
}
protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
{
var message = item as Message;
if (message == null)
return null;
return message.isSender ? ReceiverDataTemplate : SenderDataTemplate;
}
private readonly DataTemplate ReceiverDataTemplate;
private readonly DataTemplate SenderDataTemplate;
}
MessageSender.xaml
<?xml version="1.0" encoding="UTF-8"?>
<ViewCell xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Project.layout.MessageSender">
<ViewCell.View>
<Grid HorizontalOptions="EndAndExpand">
<Grid.RowDefinitions>
<RowDefinition Height="{Binding Path=Height}"/>
<RowDefinition Height="*" />
<RowDefinition Height="{Binding Path=Height}" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="75" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="15" />
</Grid.ColumnDefinitions>
<Label Text="{Binding Path=timestamp}" Grid.Column="0" Grid.Row="0" Grid.ColumnSpan="3" HorizontalTextAlignment="Center" HorizontalOptions="Center" VerticalOptions="Center" IsVisible="{Binding Path=Selected}"/>
<Frame Padding="0" CornerRadius="20" Grid.Column="1" Grid.Row="1" HorizontalOptions="EndAndExpand" >
<Grid BackgroundColor="White" VerticalOptions="FillAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Label Text="{Binding Path=text}" VerticalOptions="FillAndExpand" Margin="15,10"/>
</Grid>
</Frame>
<Label Text="Seen" Grid.Column="1" Grid.Row="2" HorizontalOptions="EndAndExpand" IsVisible="{Binding Path=Selected}"/>
</Grid>
</ViewCell.View>
</ViewCell>
Message.cs
class Message
{
public bool isSender { get; set; }
public sbyte status { get; set; }
public string text { get; set; }
public string timestamp { get; set; }
public Message(bool isSender, sbyte status, string text, string timestamp)
{
this.isSender = isSender;
this.status = status;
this.text = text;
this.timestamp = timestamp;
}
public sbyte height = 0;
public sbyte Height { get { return height; } set { height = value; } }
bool selected = false;
public bool Selected
{
get { return selected; }
set { selected = value;if (value) { Height = 25; } else { Height = 0; } }
}
}
Show mainpage.xaml
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:Project.model"
x:Class="Project.MainPage">
<ContentPage.Resources>
<ResourceDictionary>
<local:MessageTemplateSelector x:Key="MessageTemplateSelector"></local:MessageTemplateSelector>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<ListView x:Name="conversation"
ItemTemplate="{StaticResource MessageTemplateSelector}"
ItemsSource="{Binding Message}"
HasUnevenRows="True"
SeparatorVisibility="None"
RelativeLayout.HeightConstraint="{ConstraintExpression Type=RelativeToParent,Property=Height,Factor=1,Constant=0}"
IsPullToRefreshEnabled="true"
ItemTapped="Conversation_ItemTapped"
Refreshing="Conversation_Refreshing">
</ListView>
</ContentPage>
MainPage.cs
private void Conversation_ItemTapped(object sender, ItemTappedEventArgs e)
{
if (e.Item == null) return;
Message selectedItem = (Message)e.Item;
Log.Debug("ItemTap","Height before:" + selectedItem.Height);
if (selectedItem.Selected) { ((ListView)sender).SelectedItem = null; selectedItem.Selected = false; }
else { selectedItem.Selected = true; }
Log.Debug("ItemTap", "Height after:" + selectedItem.Height);
}
This is a screenshot of my log that is present in the ItemTapped event in my ListView. As you can see, the height updates but it's not reflecting on the ListView.
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 new to Xamarin and C#, so forgive me if my code is crap.
So essentially I'm trying to create a simple page that displays a list of course names, and allows a button to insert or remove items from the list within the view.
As of now the program builds and runs but the view does not display the contents of the list. Any help would be greatly appreciated. Thanks!
Here's the view:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="65" />
<RowDefinition Height="*"/>
<RowDefinition Height="50" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Frame OutlineColor="Black" Grid.Row="0" Grid.ColumnSpan="2">
<Label Text= "My Courses" FontSize="20" TextColor="Black" HorizontalTextAlignment="Center"/>
</Frame>
<Frame OutlineColor="Black" Grid.Row="1" Grid.ColumnSpan="2">
<ListView x:Name="CourseList" ItemsSource="{Binding GetCourseList}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="50" />
</Grid.ColumnDefinitions>
<Label FontSize="14" Text="{Binding Name}" TextColor="Black" Grid.Row="0" Grid.Column="0"/>
<Button FontSize="14" Text="X" TextColor="Black" Grid.Row="0" Grid.Column="1"/>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Frame>
<Button Text="+" FontSize="20" TextColor="Green" BackgroundColor="Silver"
HorizontalOptions="Fill" VerticalOptions="Center"
Grid.Row="2" Grid.ColumnSpan="2"
/>
</Grid>
Here's the View.cs code:
public partial class MainPage : ContentPage
{
MainPageViewModel vm;
public MainPage()
{
vm = new MainPageViewModel();
vm.AddCourse();
BindingContext = vm;
InitializeComponent();
}
Here's the View Model:
class MainPageViewModel
{
public string Name { get; set; }
public List<Course> Courses;
public MainPageViewModel()
{
Courses = new List<Course>();
}
public List<Course> GetCourseList()
{
return Courses;
}
public void AddCourse()
{
Course NewCourse = new Course();
NewCourse.Name = "New Course Added";
Courses.Add(NewCourse);
}
}
Finally, Here's my Model:
public class Course
{
public string Name { get; set; }
}
So I have been able to display a list using this code but I do not believe this will allow me to add or delete from the list and update the view via button click (Correct me if I'm wrong).
public List<Course> Courses{
get{
return new List<Course>(){
new Course(){Name = "Added a course"}
};
}
I think I just found my error and solution.
public List<Course> Courses{
get{
return new List<Course>(){
new Course(){Name = "Added a course"}
};
}
Because Courses is "read only" and now being bound to the view I created a member variable List VMCourse to hold the actually list of objects and am using Courses to be bounded to and read into the view.
It appears to work, but I still need to test it with button features for adding and deleting from the list. Does this look like a decent solution to you guys?
Thanks in advance!
class MainPageViewModel
{
public string Name { get; set; }
public List<Course> VMCourse;
//This is now bound to the view's listview
public List<Course> Courses {
get
{
return VMCourse;
}
}
public MainPageViewModel()
{
VMCourse = new List<Course>();
}
public void AddCourse()
{
Course NewCourse = new Course();
NewCourse.Name = "New Course Added";
VMCourse.Add(NewCourse);
}
You cannot bind a list to a method. You should use binding with properties. So to fix this you only need to change
public List<Course> GetCourseList()
{
return Courses;
}
To
public List<Course> GetCourseList
{
get { return Courses; }
}
But, as it's mentioned in the comments not any property is suitable to be used with binding unless you know what you are doing so search about binding.