简体   繁体   中英

How to debug binding problems in Xamarin Picker

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!

Some suggestions that can't be the solution:

  1. when you use "Bindings" you should use ObservableCollection instead of List
  2. Your properties should implement INotifyPropertyChanged
  3. You should use get and set

    Vehicle Vehicle {get;set;}

    List Templates {get;set;}

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>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM