Reputation: 2951
I want to add a Picker to my Xamarin form, which shows a list of template names, and allows the user to choose one.
Here is the form:
<?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:DefectReport"
x:Class="DefectReport.VehiclePage">
<ContentPage.Content>
<ScrollView>
<StackLayout>
<Label x:Name="Message" TextColor="Red" />
<Label Text="Registration Number" />
<Entry Text="{Binding Vehicle.RegistrationNumber}" />
<Label Text="Description" />
<Entry Text="{Binding Vehicle.Description}" />
<Entry Text="Vehicle Type" />
<Picker ItemsSource="{Binding Templates, PresentationTraceSources.TraceLevel=High}" ItemDisplayBinding="{Binding TemplateName}" SelectedItem="{Binding SelectedTemplate}"/>
<Button Text="Save" Clicked="SaveButton_Clicked" />
</StackLayout>
</ScrollView>
</ContentPage.Content>
</ContentPage>
Here is the code behind:
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class VehiclePage : ContentPage {
Vehicle Vehicle { get; set; }
ObservableCollection<Template> Templates { get; set; }
Template SelectedTemplate { get; set; }
public VehiclePage(Vehicle vehicle, List<Template> templates) {
Vehicle = vehicle ?? new Vehicle();
Templates = new ObservableCollection<Template>(templates);
SelectedTemplate = templates.FirstOrDefault(t => t.ServerRecordId == Vehicle.TemplateId) ?? templates.FirstOrDefault();
BindingContext = this;
InitializeComponent();
}
private async void SaveButton_Clicked(object sender, EventArgs e) {
}
}
Template and Vehicle classes:
public class Template {
[PrimaryKey]
[AutoIncrement]
public int DeviceRecordId { get; set; }
[Indexed]
public int idTemplate { get; set; }
public string TemplateName { get; set; }
public string TemplateData { get; set; }
[Ignore]
public int ServerRecordId {
get {
return idTemplate;
}
set {
idTemplate = value;
}
}
}
public class Vehicle {
[PrimaryKey]
[AutoIncrement]
public int DeviceRecordId { get; set; }
[Indexed]
public int idVehicle { get; set; }
[Indexed]
public string RegistrationNumber { get; set; }
public string Description { get; set; }
public int TemplateId { get; set; }
[Ignore]
public ServerRecordId {
get {
return idVehicle;
}
set {
idVehicle = value;
}
}
}
When I display the form, supplying a list of 1 template, there are no templates in the drop-down list.
Also, when I enter data into the form, in the SaveButton_Clicked method, Vehicle.RegistrationNumber and Vehicle.Description are not filled in, even though I have entered some data.
I can't see how to debug this - it's all so hidden away!
Upvotes: 0
Views: 658
Reputation: 2951
Based on Alessandro Caliaro's answer, with additions:
I made a separate class for the data - using this
as the BindingContext
does not work, for some reason. I made the class implement INotifyPropertyChanged
.
I changed the List to an ObservableCollection
.
I made all the data into properties, with notifications, thus:
public class VehicleInfo : Model {
private Vehicle selectedVehicle;
public Vehicle SelectedVehicle {
get { return selectedVehicle; }
set {
if (selectedVehicle != value) {
selectedVehicle = value;
OnPropertyChanged("SelectedVehicle");
}
}
}
private ObservableCollection<Vehicle> vehicles;
public ObservableCollection<Vehicle> Vehicles {
get { return vehicles; }
set {
if (vehicles != value) {
vehicles = value;
OnPropertyChanged("Vehicles");
}
}
}
private Template selectedTemplate;
public Template SelectedTemplate {
get { return selectedTemplate; }
set {
if (selectedTemplate != value) {
selectedTemplate = value;
OnPropertyChanged("SelectedTemplate");
}
}
}
}
Model is a simple class that implements INotifyPropertyChanged
:
public class Model : System.ComponentModel.INotifyPropertyChanged {
public void OnPropertyChanged(string name) {
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
public event PropertyChangedEventHandler PropertyChanged;
}
In order to save all the tedious typing, I added a code snippet to Visual Studio 2017 to do the properties:
<?xml version="1.0" encoding="utf-8" ?>
<CodeSnippets xmlns="http://schemas.microsoft.com/VisualStudio/2005/CodeSnippet">
<CodeSnippet Format="1.0.0">
<Header>
<Title>propn</Title>
<Shortcut>propn</Shortcut>
<Description>Code snippet for property and backing field with notification</Description>
<Author>Nikki Locke</Author>
<SnippetTypes>
<SnippetType>Expansion</SnippetType>
</SnippetTypes>
</Header>
<Snippet>
<Declarations>
<Literal>
<ID>type</ID>
<ToolTip>Property type</ToolTip>
<Default>int</Default>
</Literal>
<Literal>
<ID>property</ID>
<ToolTip>Property name</ToolTip>
<Default>MyProperty</Default>
</Literal>
<Literal>
<ID>field</ID>
<ToolTip>The variable backing this property</ToolTip>
<Default>myVar</Default>
</Literal>
</Declarations>
<Code Language="csharp"><![CDATA[private $type$ $field$;
public $type$ $property$
{
get { return $field$;}
set {
if($field$ != value) {
$field$ = value;
OnPropertyChanged("$property$");
}
}
}
$end$]]>
</Code>
</Snippet>
</CodeSnippet>
</CodeSnippets>
Upvotes: 0
Reputation: 5768
Some suggestions that can't be the solution:
You should use get and set
Vehicle Vehicle {get;set;}
List Templates {get;set;}
Upvotes: 3