0

This should be an extremely simple solution, but searching through the internet there seems to be multiple different ways to do binding and NONE seem to actually work.

I've created a simple application with a button, textbox and listbox. The user adds text to the textbox, clicks Add and I want the text to appear in the list box. Note that the Add button will create a Person with the firstname the text in the textbox and the last name "Jones". This is just to figure out how to get binding to actually work. I have the ObservableCollection but can't seem to even figure out how to put in the resource to the object within the class itself. Is this even possible? do I have to create a separate class to have a binding?

Here is the complete XMAL

<UserControl x:Class="simpleBinding.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:z="clr-namespace:simpleBinding"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">

<Canvas x:Name="LayoutRoot" Background="White">
    <Button Name="_b"  Content="Add" Height="23" HorizontalAlignment="Left"  VerticalAlignment="Top" Width="58" Canvas.Left="90" Canvas.Top="5" Click="OnAdd" />
    <TextBox Name="_tb" Canvas.Left="12" Canvas.Top="4" Height="24" Width="72"></TextBox>
    <ListBox Name="_list" Canvas.Left="18" Canvas.Top="41" Height="98" Width="190" />
</Canvas>

and here is the complete Code behind

namespace simpleBinding
{
    public partial class MainPage : UserControl
    {
        public ObservableCollection<Person> PersonList = new ObservableCollection<Person>    ();
        public MainPage()
        {
            InitializeComponent();
        }

        private void OnAdd(object sender, RoutedEventArgs e)
        {
            PersonList.Add(new Person(_tb.Text, "Jones"));
        }
    }

    public class Person
    {
        public string FirstName {private set; get;}
        public string LastName {private set; get; }

        public Person(string fName, string lName)
    {
            FirstName = fName;
        LastName = lName;
    }
    }
}

thanks for any help, chris

4

2 回答 2

2

To illustrate Ravuthasamy's & aqwert's comments. You have to set a DataContext first. You can set this in DataContext or read how MVVM work (It's a good Silvelight binding pattern) :

c#

public MainPage()
{
    InitializeComponent();
    DataContext = this;
}

After you can bind the class properties to elements :

Xaml

<ListBox
    ItemsSource="{Binding PersonList}"
    Canvas.Left="18"
    Canvas.Top="41"
    Height="98"
    Width="190" />
于 2013-07-10T07:07:41.733 回答
1

Following the timeline you can see that this has taken me a week to finally get to a solution. I post it here now in hopes that someone else won't waste this much time. There seems to be a lot of posts about how to deal with this issue and the examples are limited. They either show only C# or Xaml. Then CollectionChanged and PropertyChanged aren't dealt with in a single example.

This is a simple example, that implements both collection changed and property changed. As well as binding in Xaml

Here is the Xaml.

<UserControl x:Class="simpleBinding.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:src="clr-namespace:simpleBinding"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">

<Canvas x:Name="LayoutRoot" Background="White" DataContext="{Binding}">
    <Canvas.Resources>
        <src:PersonList x:Key="myDataSource"></src:PersonList>
    </Canvas.Resources>

    <Button Name="_b"  Content="Add" Height="23" HorizontalAlignment="Left"  VerticalAlignment="Top" Width="58" Canvas.Left="90" Canvas.Top="5" Click="OnAdd" />
    <Button Canvas.Left="150" Canvas.Top="5" Content="Edit" Height="23" Name="button1" Width="58" Click="OnEdit" />
    <TextBox Name="_tb" Canvas.Left="12" Canvas.Top="4" Height="24" Width="72"></TextBox>
    <ListBox Name="_list" Canvas.Left="18" Canvas.Top="41" Height="98" Width="190" ItemsSource="{Binding Source={StaticResource myDataSource}}" >
        <ListBox.ItemTemplate>
            <DataTemplate>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding Path=FirstName}" Margin="0,0,2,0" />
                    <TextBlock Text="{Binding Path=LastName}" />
                </StackPanel>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>

</Canvas>

  1. Add a xmlns that will reference your code behind. In this case my namespace is xmlns:src then you can use VS intellisense to go to the correct class.
  2. Add a resource to the layoutRoot item. In my case I'm using a canvas, but it could be Grid or Stackpanel etc.
  3. With the resource declared, you can now set the ItemSource binding in the ListBox.
  4. I've chosen to use a template to display the data which I think is really cool (best part of Xaml!) In this case there are two textBlocks but if my underlying data source had an image, I could have used this was well to graphically display the data. The binding for each textbox can be set because the exposed properties of the object are declared in the C# code. Which will be discussed next

C# Code behind

namespace simpleBinding
{
public partial class MainPage : UserControl
{
    public PersonList m_pList = new PersonList();

    public MainPage()
    {
        InitializeComponent();
        _list.ItemsSource = m_pList;
        m_pList.Add(new Person("John", "Doe"));

    }

    private void OnAdd(object sender, RoutedEventArgs e)
    {
        m_pList.Add(new Person("Jones", _tb.Text));

    }

    private void OnEdit(object sender, RoutedEventArgs e)
    {
        m_pList[1].FirstName = _tb.Text;
    }
}

public class PersonList : ObservableCollection<Person> , INotifyPropertyChanged
{
    public PersonList() : base()    // need to call  base on intialization otherwise the binded resource is not updated.
    {
        Add(new Person("Willa", "Cather"));
        Add(new Person("Isak", "Dinesen"));
        Add(new Person("Victor", "Hugo"));
        Add(new Person("Jules", "Verne"));


    }

}

public class Person : INotifyPropertyChanged
{
    private string _fName;
    private string _lName;

    public event PropertyChangedEventHandler PropertyChanged;



    public string FirstName
    {
        set
        {
            _fName = value;
            NotifyPropertyChanged("FirstName");
        }
        get
        {
            return _fName;
        }
    }
    public string LastName
    {
        set
        {
            _lName = value;
            NotifyPropertyChanged("LastName");
        }
        get
        {
            return _lName;
        }
    }



    public Person(string fName, string lName) : base()
    {
        FirstName = fName;
        LastName = lName;
    }

    public override string ToString()
    {
        return String.Format("{0} {1}", FirstName, LastName);
    }


    private void NotifyPropertyChanged(String propertyName = "")
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }


}

}

I've chosen to use the ObservableCollection because it implements INotifyCollectionChanged. The public variable is exposed which allows you to bind to the resource declared in the Xaml. (Better code, make the var private and have a property that exposes the variable through a get!)

  1. The ListBox _List needs to have its ItemsSource property set in Code Behind!!! without this whenever you change the list (add, delete etc) the UI is not updated. AND in fact you do not need the binding in the ListBox at all because we set the source in Code behind it is nice however in that in the designer with this bound control you can see that the binding is working because there are four names added when instantiating the PersonList.
  2. The ObservableCollection needs to have the INotifyCollectionChanged added. Without this, when a property is changed the UI is NOT changed.
  3. The properties that are to be exposed to the UI need to be implement in the object that is contained within the ObservableCollection (in my case the class Person exposed both FirstName and LastName) and then these properties can be bound in the Xaml (see the textBlocks's)
  4. INotifyPropertyChanged requires that you implement a PropertyChanged event i.e. public event PropertyChangedEventHandler PropertyChanged;
  5. To actually fire that event the "Person" object needs to implement code to do that, which in my case is the NotifyPropertyChanged Method. Each time a property is set, I call this method, which in turn looks to see is the PropertyChanged event is not null, and if not, then it raises that event.
  6. Here is the key to property changes, without adding the , INotifyPropertyChanged to the Observable collection PropertyChanged is null.

Hope this helps someone

于 2013-07-12T17:56:20.220 回答