I'm coming today because I'm not sure to understand something about the Binding in C#. I'm currently making a stupid app to help myself with my classes and their locations. Some others information also comes in the app.
So I have this design, which is a simple ListView of my 4 classes.
MainPage.xaml :
<ContentPage.Content>
<AbsoluteLayout>
<Label
AbsoluteLayout.LayoutBounds="0.5, 0, 1, 0.1"
AbsoluteLayout.LayoutFlags="All"
Text="My CSULB Schedule" />
<ListView x:Name="CoursesListView" ItemsSource="{Binding Courses}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="2*" />
<RowDefinition Height="6*" />
<RowDefinition Height="2*" />
</Grid.RowDefinitions>
<!-- TOP -->
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="6*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<Label
Grid.Column="0"
HorizontalTextAlignment="Center"
Text="{Binding DaysToString}"
VerticalTextAlignment="Center" />
<Label
Grid.Column="1"
HorizontalTextAlignment="Center"
Text="{Binding Title}"
VerticalTextAlignment="Center" />
<Label
Grid.Column="2"
HorizontalTextAlignment="Center"
Text="{Binding TypeAndNumber}"
VerticalTextAlignment="Center" />
</Grid>
<!-- Top -->
<!-- Center -->
<ListView Grid.Row="1" ItemsSource="{Binding Sections}">
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<ColumnDefinition Width="25*" />
<ColumnDefinition Width="5*" />
<ColumnDefinition Width="25*" />
</Grid.RowDefinitions>
</Grid>
<Label
Grid.Column="0"
HorizontalTextAlignment="Center"
Text="{Binding Location}"
VerticalTextAlignment="Center" />
<Label
Grid.Column="1"
HorizontalTextAlignment="Center"
Text="{Binding TeacherName}"
VerticalTextAlignment="Center" />
<Label
Grid.Column="2"
HorizontalTextAlignment="Center"
Text="{Binding Time}"
VerticalTextAlignment="Center" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<!-- Center -->
<!-- Bottom -->
<Grid Grid.Row="2">
<Grid.RowDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="8*" />
<ColumnDefinition Width="1*" />
</Grid.RowDefinitions>
<!-- Useless stuff at the moment -->
</Grid>
<!-- Bottom -->
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</AbsoluteLayout>
</ContentPage.Content>
In the CS part, I have this:
MainPage.xaml.cs :
public partial class MainPage : ContentPage
{
public ObservableCollection<Course> Courses { get; set; }
public MainPage()
{
base.BindingContext = this;
Debug.WriteLine("1.");
InitCoursesList();
Debug.WriteLine("2.");
InitializeComponent();
Debug.WriteLine("3.");
//CoursesListView.ItemsSource = Courses;
}
private void InitCoursesList()
{
Debug.WriteLine("1.1");
this.Courses = new ObservableCollection<Course>()
{
new Course()
{
Days = new List<Course.Day>() { Course.Day.M, Course.Day.W },
Title = "Object Oriented Programming",
Type = "CECS",
Number = "274",
Sections = new List<Section>()
{
new Section()
{
Location = "VEC-419",
TeacherName = "Foss S",
Time = "1-1:50pm"
},
new Section()
{
Location = "ECS-414",
TeacherName = "Foss S",
Time = "2-3:15pm"
}
}
},
new Course()
{
Days = new List<Course.Day>() { Course.Day.M, Course.Day.W },
Title = "Adv Artificial Intelligence",
Type = "CECS",
Number = "551",
Sections = new List<Section>()
{
new Section()
{
Location = "VEC-331",
TeacherName = "Todd Ebert",
Time = "3:30-4:45pm"
}
}
},
new Course()
{
Days = new List<Course.Day>() { Course.Day.Tu, Course.Day.Th },
Title = "Operating Syetems",
Type = "CECS",
Number = "326",
Sections = new List<Section>()
{
new Section()
{
Location = "VEC-330",
TeacherName = "Lam S",
Time = "9:30-10:20am"
},
new Section()
{
Location = "ECS-416",
TeacherName = "Lam S",
Time = "10:30-11:45am"
}
}
},
new Course()
{
Days = new List<Course.Day>() { Course.Day.Tu, Course.Day.Th },
Title = "C++ for Java Developers",
Type = "CECS",
Number = "282",
Sections = new List<Section>()
{
new Section()
{
Location = "VEC-518",
TeacherName = "Nachawati S",
Time = "11-11:50am"
},
new Section()
{
Location = "ECS-403",
TeacherName = "Nachawati S",
Time = "12-1:15pm"
}
}
},
};
Debug.WriteLine("1.2");
}
}
Everything is coming in the good way, I mean the display of the Debug.WriteLine();. The thing I don't understand now it's why my listview is empty? For me, it doesn't make sense but everyone knows that, often, the problem comes between the computer and the chair so.. Where am I wrong?
I bind my main ListView which is <ListView x:Name="CoursesListView" ...> with my public ObservableCollection<Course> Courses { get; set; }. I fill it, initalize everything, I set the binding context but at the end.. The page is empty, why?
Also I was asking to myself another question. If I bind a collection to a list, can I bind another list inside of every element of this list view? If you are confuse, take a look at the List<Section> Sections in every Course object. In the xaml part, I have a list in the center part of each element.
I hope my question is clear, maybe it's a stupid question, maybe this question is interesting, I don't know but for sure, I didn't and I still don't understand something I think.
Thank for your help.
PS: Tell me if you want any edit.
You're creating the ObservableCollection in your InitCoursesList function. The binding, which will have already bound to Courses with a value of null, won't update when you create the ObservableCollection because you're not raising the PropertyChanged event (see INotifyPropertyChanged) so any additions to the collection won't register. The solution is to create the collection when the containing class is created:
public ObservableCollection<Course> Courses { get; set; } = new ObservableCollection<Course>();
And then just populate it in the initialise function:
private void InitCoursesList()
{
Courses.Add(new Course { etc });
}
And of course, if you wanted to bind a second list (ObservableCollection) you can do that. In your Course class you'd simply have an ObservableCollection and bind that to whatever control you want to use to view that data.
Your code will work if you change the Courses property to a dependency property.
public ObservableCollection<Course> Courses
{
get { return (ObservableCollection<Course>)GetValue(CoursesProperty); }
set { SetValue(CoursesProperty, value); }
}
public static readonly DependencyProperty CoursesProperty =
DependencyProperty.Register(nameof(Courses), typeof(ObservableCollection<Course>), typeof(MainPage));
The reason is, as stated in an answer already, is that you're re-creating the ObservableCollection and breaking the binding to the old one because there is no PropertyChanged event fired when you re-assigned it. Since this is a UIElement it is best to just make this a DependencyProperty rather than implementing INotifyPropertyChanged, which you also have the choice of doing. Then you would need to write out a full Courses property call PropertyChanged in the setter of the Courses property.
Furthermore you need to update your binding statement as well... It needs to look at the MainPage rather than the DataContext.
ItemsSource="{Binding Courses, Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainPage}}}
Or name your MainPage x:Name="mainPage" and use this binding...
ItemsSource="{Binding Courses, ElementName=mainPage}"
Related
I can't believe I have spent a day trying to get this to work! I began developing in Xamarin.Forms last month and for the most part things have been going pretty smooth. My background is ASP.Net MVC and Windows Forms so the MVVM concept is new to me.
The issue that I am having is very frustrating and seems silly really. I imagine it could be my implementation possibly because I am not using MVVM but am performing all binding in the code behind? I have a page that has a search box and a ListView controll. Each of the items in the ListView is fairly simple... it only consists of a few labels and a switch. The ListView is bound every time someone performs a search. The problem that I am having is that every time someone performs a search it refreshes the Model everyting is bound to... this in turn triggers the Switches Toggle Event. Within the Toggle Event I have code that would update the database when the value is changed so I do not want this fired off every time the model changes. It is very frustrating as there does not appear to be a Click Event. See Xaml and the Method I am using below:
public async void SearchItems()
{
PageLoaded = false;
if(string.IsNullOrEmpty(this.txtSearch.Text)) { return; }
this.LoadSearch.IsRunning = true;
// Populate Locations For Item
string ItemType = HttpUtility.UrlEncode(this.SelectItemType.SelectedItem.ToString());
string Search = HttpUtility.UrlEncode(this.txtSearch.Text.ToLower());
string url = string.Format("https://test.com/api/LocationsSearch?itemType={0}&search={1}", ItemType, Search);
LocationsList = new List<Locations>();
var json = await func.GetIBApi(url);
LocationsList = Newtonsoft.Json.JsonConvert.DeserializeObject<List<Locations>>(json);
this.ItemsList.ItemsSource = LocationsList;
this.LoadSearch.IsRunning = false;
PageLoaded = true;
}
Toggle Event:
private async void Switch_Toggled(object sender, ToggledEventArgs e)
{
try
{
if (PageLoaded)
{
bool isPrimary = e.Value;
ViewCell cell = (sender as Switch).Parent.Parent.Parent as ViewCell;
var location = cell.BindingContext as Locations;
string display = "";
if (isPrimary)
{
display = "Are you sure you want to make this the 'Primary Location'?";
}
else
{
display = "Are you sure you want to unset this as the 'Primary Location'? Note: Make sure you set another location as the 'Primary' on this PartNumber!";
}
var result = await App.Current.MainPage.DisplayAlert("Primary Change Confirmation", display, "Yes", "No");
if (result)
{
string url = string.Format("https://test.com/api/LocationsSearch?partNumber={0}&location={1}&isPrimary={2}", location.ID, location.Location, isPrimary);
var json = await func.PostIBApi(url);
await Navigation.PopModalAsync();
}
}
}
catch (Exception e2)
{
}
}
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="IBMobile.Views.LocationsPage"
xmlns:local2="clr-namespace:FontAwesome"
Visual="Material">
<ContentPage.IconImageSource>
<FontImageSource FontFamily="{StaticResource FontAwesomeSolid}" Glyph="{x:Static local2:IconFont.MapMarkerAlt}" />
</ContentPage.IconImageSource>
<ContentPage.Content>
<StackLayout Padding="25">
<Picker x:Name="SelectItemType" SelectedIndexChanged="SelectItemType_SelectedIndexChanged" />
<SearchBar x:Name="txtSearch" SearchButtonPressed="TxtSearch_SearchButtonPressed" Placeholder="Search..." FontSize="Title"></SearchBar>
<ActivityIndicator Color="#007D5D" IsRunning="false" x:Name="LoadSearch" HorizontalOptions="Center" />
<ListView x:Name="ItemsList" ItemTapped="ItemsList_ItemTapped" HasUnevenRows="True" BackgroundColor="White">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout VerticalOptions="FillAndExpand">
<Grid VerticalOptions="FillAndExpand" Padding="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="3*"></ColumnDefinition>
<ColumnDefinition Width="3*"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Label TextColor="#007D5D" Text="{Binding ViewID}" FontSize="Title" VerticalTextAlignment="Center" Grid.Column="0" Grid.Row="0"></Label>
<Label TextColor="#007D5D" Text="{Binding Location}" FontSize="Title" VerticalTextAlignment="Center" Grid.Column="1" Grid.Row="0"></Label>
<Label Text="Primary" Grid.Column="2" Grid.Row="0"></Label>
<Switch IsToggled="{Binding IsPrimary}" x:Name="Toggle1" ThumbColor="#007D5D" Grid.Column="2" Grid.Row="1" Toggled="Switch_Toggled"/>
<Label Text="{Binding Description}" Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2"></Label>
<Label Text="{Binding Message}" Grid.Column="0" Grid.Row="2" Grid.ColumnSpan="3" FontSize="Small" TextColor="Red"></Label>
</Grid>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
if (PageLoaded)
{
//entered handler
bool isPrimary = e.Value; //we are invoking the handler again from inside itsself
// all the mess comes from this
Use another lock:
if (PageLoaded && !_internal)
{
//entered handler
_internal=true;
bool isPrimary = e.Value;
_internal=false;
Attempting to add an image to my xamarin app ->
add image to project, then add code behind
public Products()
{
InitializeComponent();
Products_List = new List<App1.Classes.Product>();
Products_List.Add(new App1.Classes.Product
{
Id = 0001,
Name = "Coors",
ImageUrl = "App1.Images.Coors.jpg",
Price = "£9.99"
});
Products_List.Add(new App1.Classes.Product
{
Id = 0002,
Name = "Bud",
ImageUrl = "App1.Images.Bud.jpg",
Price = "£13.99"
});
Products_List.Add(new App1.Classes.Product
{
Id = 0003,
Name = "Guinness",
ImageUrl = "App1.Images.Coors.jpg",
//have also tried-> ImageUrl = "App1\\Images\\Coors.jpg",
Price = "£9.99"
});
BindingContext = this;
}
then add front end ->
<ListView ItemsSource="{Binding Products_List}"
HasUnevenRows="true"
ItemSelected="OnListViewItemSelected"
ItemTapped="OnListViewItemTapped">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid Padding="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Grid.RowSpan="2"
Source="{Binding ImageUrl}"
Aspect="AspectFill"
HeightRequest="60"
WidthRequest="60" />
<Label Grid.Column="1"
Text="{Binding Name}"
FontAttributes="Bold"/>
<Label Grid.Row="1"
Grid.Column="1"
Text="{Binding Price}"
VerticalOptions="End" />
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
So the text appears on screen but no image ->
output window shows...image not found
As Jason said that we put the image in Project.Android---Resources---drawable, then you can change image property for Build action as AndroidResource, then you can use
Products_List.Add(new App1.Classes.Product
{
Id = 0001,
Name = "Coors",
ImageUrl = "Coors.jpg",
Price = "£9.99"
});
I'm using Visual Studio 2019 to develop Cross-Platform Mobile App (iOS & Android) via Xamarin C#.
I want to display my data in ListView via 2 columns.
But, I've no idea how to do it. As far of my knowledge, I know the ListView is only display in 1 columns.
public MainPage()
{
InitializeComponent();
ObservableCollection<CardInfo> Name = new ObservableCollection<CardInfo>{
new CardInfo{BrandName = "Brand 1"},
new CardInfo{BrandName = "Brand 2"},
new CardInfo{BrandName = "Brand 3"},
new CardInfo{BrandName = "Brand 4"},
new CardInfo{BrandName = "Brand 5"},
new CardInfo{BrandName = "Brand 6"},
new CardInfo{BrandName = "Brand 7"},
new CardInfo{BrandName = "Brand 8"},
new CardInfo{BrandName = "Brand 9"},
new CardInfo{BrandName = "Brand 10"}
};
ListViewName.ItemsSource = Name;
<ListView x:Name="ListViewName" HasUnevenRows="True" HorizontalOptions="Start" VerticalOptions="Start" VerticalScrollBarVisibility="Never" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell >
<Grid >
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Frame Style="{StaticResource FrameStyle}" Grid.Row="0" Grid.Column="0" >
<Label Text="{Binding BrandName}" Style="{StaticResource LabelStyle}" />
</Frame>
<Frame Style="{StaticResource FrameStyle}" Grid.Row="0" Grid.Column="1" >
<Label Text="{Binding BrandName}" Style= {StaticResource LabelStyle}" />
</Frame>
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
If you want a listview with multiple columns(i.e multiple model items in a row) you can use FlowListView library.
<flv:FlowListView FlowColumnCount="2" FlowItemsSource="{Binding Name}" >
<flv:FlowListView.FlowColumnTemplate>
<DataTemplate>
<Frame Style="{StaticResource FrameStyle}" >
<Label Text="{Binding BrandName}" Style="{StaticResource LabelStyle}" />
</Frame>
</DataTemplate>
</flv:FlowListView.FlowColumnTemplate>
</flv:FlowListView>
You are missing the Value property inside the CardInfo class. Because that is what the line
<Label Text="{Binding Value}" Style= {StaticResource LabelStyle}" />
is looking for. The BindingContext is the object to represent from the ItemsSource (in your case, ItemsSource is the Name ObserveableCollection) by default in a ListView item. So it is a CardInfo object. The CardInfos in your ObserveableCollection do not have this property.
Yes you can have multiple columns, it is all controlled by the way you code design your list view item template using properties like width and minimum width. You can also use a grid within your item template that will help you take care of it.
Here's a good example from the Microsoft Xamarin's official documentation to help you.
wondering if anybody can help with a solution I am looking for. In my Xamarin project, I have a 'add another line' button which adds another stack layout underneath it with the same controls as the first one. I want to save the new added one to JSON but not sure where to start, has anybody done this before?
I have added the first stack to json but i need it to reckonise the new ones on the button press. (sorry if it does not make sense)
Page.xaml Code:
<Grid Margin="0, 30, 0, 30">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Label x:Name="NameLabel" Text="Name" TextColor="Black" FontSize="20" Grid.Column="0" Grid.Row="0" />
<Label x:Name="ArrivalLabel" Text="Arrival" TextColor="Black" FontSize="20" Grid.Column="1" Grid.Row="0" />
<Label x:Name="DescriptionLabel" Text="Description" TextColor="Black" FontSize="20" Grid.Column="2" Grid.Row="0" />
<Label x:Name="DepartLabel" Text="Depart" TextColor="Black" FontSize="20" Grid.Column="3" Grid.Row="0" />
<Label x:Name="SignLabel" Text="Sign" TextColor="Black" FontSize="20" Grid.Column="4" Grid.Row="0" />
</Grid>
<StackLayout x:Name="AddMoreNotes">
<StackLayout>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Entry x:Name="Name" FontSize="20" Grid.Column="0" Grid.Row="0" />
<TimePicker x:Name="Arrival" FontSize="20" Grid.Column="1" Grid.Row="0" />
<Entry x:Name="Description" FontSize="20" Grid.Column="2" Grid.Row="0" />
<TimePicker x:Name="Depart" FontSize="20" Grid.Column="3" Grid.Row="0" />
<Entry x:Name="Sign" FontSize="20" Grid.Column="4" Grid.Row="0"/>
</Grid>
</StackLayout>
<!--Add More View Spawns Here Do Not Change-->
</StackLayout>
<StackLayout Orientation="Horizontal" HorizontalOptions="End">
<Button x:Name="AddMoreButton" Text="Add More" FontSize="16" BorderRadius="6" Clicked="AddMoreButton_Clicked" />
</StackLayout>
Page.xaml.cs Code:
private void AddMoreButton_Clicked(object sender, EventArgs e)
{
AddMoreNotes.Children.Add(new AttendanceAddMoreView()
{
});
}
private void SubmitButton_Clicked(object sender, EventArgs e)
{
var attendanceChecklist = new AttendanceChecklist();
var AttentionOf1 = new List<AttentionOf>();
var AttendanceSection1 = new List<AttendanceSection>();
attendanceChecklist.TheDate = TheDate.Date;
attendanceChecklist.AttendanceNumber = AttendanceNumber.Text;
attendanceChecklist.EmployeeName = EmployeeName.Text;
AttentionOf1.Add(new AttentionOf()
{
FirstName = FirstName.Text,
LastName = LastName.Text,
AddressLine1 = AddressLine1.Text,
AddressLine2 = AddressLine2.Text,
CityOrTown = CityOrTown.Text,
County = County.Text,
Postcode = Postcode.Text,
ContractTitleOrRef = ContractTitleOrRef.Text,
CustomerName = CustomerName.Text,
SiteContact = SiteContact.Text,
});
AttendanceSection1.Add(new AttendanceSection()
{
Name = Name.Text,
Arrival = Arrival.Time,
Description = Description.Text,
Depart = Depart.Time,
Sign = Sign.Text
});
attendanceChecklist.attentionOf = AttentionOf1;
attendanceChecklist.attendanceSection = AttendanceSection1;
var json = JsonConvert.SerializeObject(attendanceChecklist, Newtonsoft.Json.Formatting.Indented);
var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var filename = Path.Combine(documents, "AttendanceNote.json");
File.WriteAllText(filename, json);
}
Edit:
added new code
public string LargerJsonString;
private void AddMoreButton_Clicked(object sender, EventArgs e)
{
AddMoreNotes.Children.Add(new AttendanceAddMoreView()
{
});
StringContent content = new StringContent(JsonConvert.SerializeObject(AddMoreNotes), Encoding.UTF8, "application/json");
LargerJsonString += content;
}
To Start this I have done this in MVVM and hence you might have to use that if you are not already using it.
Now I made a dummy layout and a dummy model which can be seen below:
<?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="XamarinCertUnderstanding.Views.MainPage">
<StackLayout Margin="0, 15, 0, 0">
<Button VerticalOptions="CenterAndExpand" HorizontalOptions="CenterAndExpand" BorderColor="Black" BackgroundColor="Blue" Command="{Binding AddButton}" TextColor="Green" Text="Add Button"/>
<ListView x:Name="MainList" ItemsSource="{Binding DefaultModels}" BackgroundColor="Transparent" HasUnevenRows="True"
SeparatorVisibility="Default" Margin="0, 15, 0, 0">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<StackLayout BackgroundColor="White" Spacing="10" >
<Entry Text="{Binding Name,Mode=TwoWay}" Placeholder="Name" FontSize="20" BackgroundColor="White"/>
<TimePicker Time="{Binding Arrival,Mode=TwoWay}" FontSize="20" BackgroundColor="White"/>
<Entry Text="{Binding Description,Mode=TwoWay}" Placeholder="Description" FontSize="20" BackgroundColor="White"/>
<TimePicker Time="{Binding Depart,Mode=TwoWay}" FontSize="20" BackgroundColor="White"/>
<Entry FontSize="20" Text="{Binding Sign,Mode=TwoWay}" Placeholder="Sign" BackgroundColor="White"/>
</StackLayout>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
I am using BindingMode as TwoWay so that any change on the View is reflected to the ViewModel Collection and Vice versa
The DummyModel that I use is as shown below:
public class DefaultModel : INotifyPropertyChanged
{
private string userName = string.Empty;
private string description = string.Empty;
private string sign = string.Empty;
private TimeSpan arrival = TimeSpan.Zero;
private TimeSpan depart = TimeSpan.Zero;
public string Name
{
get
{
return userName;
}
set
{
userName = value;
RaisePropertyChanged(nameof(Name));
}
}
public TimeSpan Arrival
{
get
{
return arrival;
}
set
{
arrival = value;
RaisePropertyChanged(nameof(Arrival));
}
}
public string Description
{
get
{
return description;
}
set
{
description = value;
RaisePropertyChanged(nameof(Description));
}
}
public TimeSpan Depart
{
get
{
return depart;
}
set
{
depart = value;
RaisePropertyChanged(nameof(Depart));
}
}
public string Sign
{
get
{
return sign;
}
set
{
sign = value;
RaisePropertyChanged(nameof(Sign));
}
}
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void RaisePropertyChanged([CallerMemberName] string propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
I am using the INotifyPropertyChanged interface to notify any changes in the property. For better understanding check this:https://learn.microsoft.com/en-us/dotnet/framework/winforms/how-to-implement-the-inotifypropertychanged-interface
In your MainPage.Xaml.cs set the BindingContext to MainViewModel in the constructor.
public partial class MainPage : ContentPage
{
public MainPage()
{
InitializeComponent();
BindingContext = new MainViewModel();
}
}
Now in your ViewModel do the following:
public class MainViewModel : BindableObject
{
public Command AddButton { get; set; }
private ObservableCollection<DefaultModel> defaultModels;
public ObservableCollection<DefaultModel> DefaultModels
{
get { return defaultModels; }
set
{
defaultModels = value;
OnPropertyChanged(nameof(DefaultModels));
}
}
public MainViewModel()
{
DefaultModels = new ObservableCollection<DefaultModel>();
DefaultModels.Add(new DefaultModel());
AddButton = new Command(AddButtonCommand);
}
private void AddButtonCommand(object obj)
{
DefaultModels.Add(new DefaultModel());
}
}
Now how this works is quite simple on AddButton Click I call the AddButtonCommand and I initialize a blank object and add it to the ObservableCollection which then updates my View and adds another set of controls as far as get this data goes you can get all the data from your DefaultModels collection.
Goodluck
Revert in case of queries
Sorry I'm not able to comment so I'll leave my thoughts here: first off I'd use a ListView as it allows you to easily bind items to your ListView. A little example beneath
<ListView x:Name="MainList" ItemsSource="{Binding Messages}" BackgroundColor="Transparent" HasUnevenRows="True" SeparatorVisibility="Default" Margin="0, 15, 0, 0" IsPullToRefreshEnabled="True" RefreshCommand="{Binding RefreshingMessages}" IsRefreshing="{Binding IsRefreshing}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
Put your code in here
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
This should work if you're doing this in the MVVM, Model View to View Model, structure which makes life a lot easier. However I don't believe this is a necessity. In the case of you wanting to add another line to your JSON, I see you're already using JsonConvert, so can't you deserialize the the JSON to a list of the object and loop through the list and output a number of items on your screen?
string JsonContent = await WhateverYourJsonIs.Content.ReadAsStringAsync().ConfigureAwait(false);
var something = JsonConvert.DeserializeObject<List<WhateverYourObjectIsCalled>>(JsonContent)
I'm not exactly sure if that's what you're trying to achieve, if not, let me know and I'll try to help! In this case, WhateverYourJsonIs would most likely need to be a string of Json, you might not need that line, in my case of returning information from a server I do need to read it as a string however you may not need to as it may already be a string, that would be a little testing on your part as I haven't done it this way so I've grabbed snips from my code. Hope this helps!
Edit:
If you serialize your object and have a list of string using this method
StringContent content = new StringContent(JsonConvert.SerializeObject(NewItem), Encoding.UTF8, "application/json");
That'll convert it into Json for you, you can then append it on the end of a larger Json string for example:
LargerJsonString += content
And then add it to your File. Alternatively you could use something such as SQLite and store it in a local Database Table? This is the method I use
Edit 2
So I did a little digging on the Docs.Microsoft to try and find what you're using for "File.WriteAllText" and I found this method which appends extra text onto the end of the file
string appendText = "This is extra text" + Environment.NewLine;
File.AppendAllText(path, appendText);
Try using this rather than WriteAll
I can't seem to delete entries from my ListView which binds the database. I can see everything in the database, and have added checkboxes and it doesn't matter how I select it... it won't delete the entry. I'm going nuts, I've tried everything I can find. So here's my listview code
Okay, and update. When I put in
var existingconact = conn2.Query<Medications>("select * from Medications where Id = 4").FirstOrDefault();
I click the button and it deletes it and updates the table. So I'm doing something wrong with the Id.
<StackPanel Margin="20, 240, 0 0">
<ListView Header="Medications" x:Name="myList" Background="DimGray" HorizontalAlignment="Right" Margin="0,0,0,0" Width="600">
<ListView.ItemTemplate>
<DataTemplate>
<Grid Width="600">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<CheckBox HorizontalAlignment="Right" VerticalAlignment="Center" x:Name="CheckBoxItem"/>
<TextBlock x:Name="Id" Grid.Column="1" Text="{Binding Id}" TextWrapping="Wrap" />
<TextBlock x:Name="medName" Grid.Column="2" Text="{Binding MedName}" TextWrapping="Wrap" />
<TextBlock Grid.Column="3" x:Name="medDose" Text="{Binding MedDose}" TextWrapping="Wrap" />
<TextBlock Grid.Column="4" x:Name="whatFor" Text="{Binding WhatFor}" TextWrapping="Wrap" />
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackPanel>
Here is the button command I'm using to try and delete the entry
private void btn_Remove_Click(object sender, RoutedEventArgs e)
{
DBPath2 = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "meds.sqlite");
using (SQLite.Net.SQLiteConnection conn2 = new SQLite.Net.SQLiteConnection(new SQLite.Net.Platform.WinRT.SQLitePlatformWinRT(), DBPath2))
{
var existingconact = conn2.Query<Medications>("select * from Medications where Id = ?").FirstOrDefault();
if (existingconact != null)
{
conn2.RunInTransaction(() =>
{
conn2.Delete(existingconact);
});
And if you want to take a look, my schema
public class Medications
{
[PrimaryKey, AutoIncrement]
public int Id { get; set; }
public string MedName { get; set; }
public string MedDose { get; set; }
public string WhatFor { get; set; }
}
Okay, I got it! It was one little line in Meds.xaml.cs
This is the button that is handling the click.
private void btn_Remove_Click(object sender, RoutedEventArgs e)
{
DBPath2 = Path.Combine(Windows.Storage.ApplicationData.Current.LocalFolder.Path, "meds.sqlite");
using (SQLite.Net.SQLiteConnection conn2 = new SQLite.Net.SQLiteConnection(new SQLite.Net.Platform.WinRT.SQLitePlatformWinRT(), DBPath2))
{
// **This as where the error was**
var existingconact = conn2.Query<Medications>("select * from Medications where **Id = Id"**).FirstOrDefault();
if (existingconact != null)
{
conn2.RunInTransaction(() =>
{
conn2.Delete(existingconact);
});
myList.ItemsSource = conn2.Table<Medications>();
}
}
}
}
}
You never passed in the ID to find the SQLite record. This line:
var existingconact = conn2.Query<Medications>("select * from Medications where Id = ?").FirstOrDefault();
Needs to pass in the value of Id:
var existingconact = conn2
.Query<Medications>(
"select * from Medications where Id = ?",
myList.SelectedItem.Id)
.FirstOrDefault();
Now, getting the ID from the selected row is what you need to handle. You might have to cast the myList.SelectedItem to your expected data type to compile.
You can validate this by retrying your hard coded test into the parameterized one:
var existingconact = conn2.Query<Medications>("select * from Medications where Id = ?", 4).FirstOrDefault();