简体   繁体   中英

Binding XAML Controls to a List

I am developing a windows store app. What I did is, I made a class, ContactBook , which contains a few properties, fields and constructors. I then made a list

List<ContactBook>

where I added my class to. I want to bind a couple of textblocks, and an image, to the list so that each display their respective values. I have, so far created the following code:

The Class

public class ContactBook
    {
        #region _Fields

        private string _Name;
        private string _Surname;
        private string _Number;
        private string _ImagePath;

        #endregion

        #region Constructors

        public ContactBook(string name, string surname, string number, string imagePath)
        {
            ImagePath = imagePath;
            Name = name;
            Surname = surname;
            Number = number;
        }

        public ContactBook()
        {
            ImagePath = null;
            Name = null;
            Surname = null;
            Number = null;
        }

        #endregion

        #region Properties

        public string Name
        {
            get
            {
                return _Name;
            }
            set
            {
                this._Name = value;
            }
        }

        public string Surname
        {
            get
            {
                return _Surname;
            }
            set
            {
                this._Surname = value;
            }
        }

        public string Number
        {
            get
            {
                return _Number;
            }
            set
            {
                this._Number = value;
            }
        }

        public string ImagePath
        {
            get
            {
                return _ImagePath;
            }
            set
            {
                this._ImagePath = value;
            }
        }

        #endregion

    }

The XAML

<Page
x:Class="Summative_LU08.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Summative_LU08"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:myNS="using:Summative_LU08"
mc:Ignorable="d" Loaded="Page_Loaded">

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock HorizontalAlignment="Left" Margin="10,10,0,0" TextWrapping="Wrap" Text="Contacts" VerticalAlignment="Top" FontFamily="Segoe UI" FontSize="72"/>
        <TextBlock HorizontalAlignment="Left" Height="7" Margin="1452,740,-101,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Width="15"/>
        <TextBlock x:Name="Name_Text" Text="Name: " FontSize="20" Margin="180,124,1112,620"/>
        <TextBlock x:Name="theName" Text="{Binding }" FontSize="20" Margin="180,153,939,585"/>
        <TextBlock x:Name="Surname" Text="Surname: " FontSize="20" Margin="180,208,1087,529"/>
        <TextBlock x:Name="theSurname" Text="" FontSize="20" Margin="180,244,1079,496"/>
        <TextBlock x:Name="thenumber" Text="" FontSize="20" Margin="10,308,1246,426"/>
        <Image Width="160" HorizontalAlignment="Left" Margin="10,124,0,496"/>

        <Image Width="160" HorizontalAlignment="Left" Margin="10,369,0,251"/>
        <TextBlock x:Name="ContactNumber" Text="Contact Number:" Width="160" HorizontalAlignment="Left" FontSize="20" Height="31" FontStyle="Normal" FontFamily="Segoe UI" Margin="10,277,0,460"/>
        <TextBlock x:Name="Name_2" Text="Name: " FontSize="20" Margin="182,369,1110,375"/>
        <TextBlock x:Name="theName_2" Text="" FontSize="20" Margin="182,398,1127,337"/>
        <TextBlock x:Name="Surname_2" Text="Surname: " FontSize="20" Margin="182,453,1085,284"/>
        <TextBlock x:Name="theSurname_2" Text="" FontSize="20" Margin="182,489,1077,251"/>
        <TextBlock x:Name="ContactNumber_2" Text="Contact Number:" Width="160" HorizontalAlignment="Left" FontSize="20" Height="31" FontStyle="Normal" FontFamily="Segoe UI" Margin="10,522,0,215"/>
        <TextBlock x:Name="thenumber_Copy" Text="" FontSize="20" Margin="10,553,1246,181"/>
    </Grid>
</Page>

The Code-Behind

private void Page_Loaded(object sender, RoutedEventArgs e)
        {
            List<ContactBook> contactsBook = new List<ContactBook>();
            ContactBook contactBook_1 = new ContactBook();
            ContactBook contactBook_2 = new ContactBook();


            contactBook_1.Name = "Jaco";
            contactBook_1.Surname = "Badenhorst";
            contactBook_1.Number = "0728568956";
            contactBook_1.ImagePath = "Assets\\Contact";

            contactBook_2.Name = "Dean";
            contactBook_2.Surname = "Lukas";
            contactBook_2.Number = "0825653565";
            contactBook_2.ImagePath = "Assets\\Contact";

            contactsBook.Add(contactBook_1);
            contactsBook.Add(contactBook_2);

            theName.SetBinding(contactsBook, contactsBook[0]);
        }

How can I bind the textblocks to the List so that the theName textblock will display the name and so forth. All the textblock names preseded by the "the" is just labels, the other will hold the actual value.

To support binding, the model classes should implement INotifyPropertyChanged - this will trigger events on changes of the data, so the controls know they need to redraw.

To start binding on a page, you first need to see the DataContext for the page (most common practice) - to define where the controls get the data. Then per control you define for a property which property is used using the {Binding } annotation. To that binding definition you can add behavior things like Mode to make data flow from the model to the control only or both ways (changes in the control are then stored in the model).

For changing collections it's best to use a ObservableCollection, so changes in the collection also trigger changes in a collection control (like ListVIew).

This might be helpful....

Add this class to your solution:

/// <summary>
/// Implementation of <see cref="INotifyPropertyChanged"/> to simplify models.
/// </summary>
public abstract class BindableBase : INotifyPropertyChanged
{
    /// <summary>
    /// Multicast event for property change notifications.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;

    /// <summary>
    /// Checks if a property already matches a desired value.  Sets the property and
    /// notifies listeners only when necessary.
    /// </summary>
    /// <typeparam name="T">Type of the property.</typeparam>
    /// <param name="storage">Reference to a property with both getter and setter.</param>
    /// <param name="value">Desired value for the property.</param>
    /// <param name="propertyName">Name of the property used to notify listeners.  This
    /// value is optional and can be provided automatically when invoked from compilers that
    /// support CallerMemberName.</param>
    /// <returns>True if the value was changed, false if the existing value matched the
    /// desired value.</returns>
    protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
    {
        if (Equals(storage, value)) return false;

        storage = value;
        OnPropertyChanged(propertyName);
        return true;
    }

    /// <summary>
    /// Notifies listeners that a property value has changed.
    /// </summary>
    /// <param name="propertyName">Name of the property used to notify listeners.  This
    /// value is optional and can be provided automatically when invoked from compilers
    /// that support <see cref="CallerMemberNameAttribute"/>.</param>
    protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var eventHandler = PropertyChanged;
        if (eventHandler != null)
            eventHandler(this, new PropertyChangedEventArgs(propertyName));
    }
}

Have your ContactBook class inherit from this class:

public class ContactBook : BindableBase

Instead of your current getters and setters, use this:

private string _name;
public string Name
{
    get { return _name; }
    set { SetProperty(ref _name, value); }
}

Your page code behind should look something like this (simple solution):

    public sealed partial class MainPage : BasePage
    {
        public ObservableCollection<ContactBook> Books;

        public MainPage()
        {
            this.InitializeComponent();
            // In Windows 8 apps you must set the DataContext. So uncomment the next line in that case
            //DataContext = this;
            Loaded += MainWindow_Loaded;
        }

        private async void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            Books = new ObservableCollection<ContactBook>();
            // add code to read from a service, database, etce
        }
}

In the XAML you could add a ListView to show all the items:

<ListView ItemsSource={Binding Books} />

To show data from one item, you should set the context appropriately, eg have an extra property in your page CurrentBook of type ContactBook that indicates the current selection.

To show (and edit) the name of that item you could use:

<TextBox Text={Binding CurrentBook.Name, Mode=TwoWay} />

A good study project for you could be investigate MVVM - this is a way to define a model, a view and connect the two together. For instance see: http://www.mvvmlight.net/ .

Hope this gets you started.

Martin

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