I created a combobox and set observable collection as the itemsource and implemented INotifyPropertyChanged on the observable collection item. Even after that, when I select different item in the combobox, the OnPropertyChange method is not invoked. I think I am not making the binding properly. Could any one please correct me/ suggest me in this regard.
---------------------------------MainPage.xaml---------------------------------------------------
<UserControl xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" x:Class="MasterDetailsUpdate.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"
mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
<StackPanel Width="300">
<ComboBox Name="cboName"></ComboBox>
<TextBox Name="tbxName" Text="{Binding Path=name,Mode=TwoWay,ElementName=cboName}" ></TextBox>
</StackPanel>
</UserControl>
---------------------------MainPage.xaml.cs-----------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
namespace MasterDetailsUpdate
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
Loaded += new RoutedEventHandler(MainPage_Loaded);
}
void MainPage_Loaded(object sender, RoutedEventArgs e)
{
ObservableCollection<Person> persons = new ObservableCollection<Person>();
persons.Add(new Person { city = "c1", name = "n1" });
persons.Add(new Person { city = "c2", name = "n2" });
persons.Add(new Person { city = "c3", name = "" });
persons.Add(new Person { city = "c4", name = "" });
persons.Add(new Person { city = "c5", name = "n1" });
cboName.ItemsSource = persons;
cboName.DisplayMemberPath = "name";
}
}
public class Person : INotifyPropertyChanged
{
private string _name;
private string _city;
public string name
{
set
{
_name = value;
OnPropertyChanged("name");
}
get
{
return _name;
}
}
public string city
{
set
{
_city = value;
OnPropertyChanged("city");
}
get
{
return _city;
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
Thank You
No you aren't binding right, your textbox binding should look like this:-
<TextBox Name="tbxName" Text="{Binding Path=SelectedItem.name, Mode=TwoWay, ElementName=cboName}" />
A ComboBox doesn't have a name property. It does have a SelectedItem property which will be the Person instance currently selected. You then want the name property of that instance. Hence the path you need in the binding is SelectedItem.name.
Now you should find that when you edit the textbox and move the focus elsewhere then PropertyChange event will fire and you should see the change in the combo box.
Note by default textbox does not update its value until the user presses tab or in some other way moves the focus on.
Related
Following many examples here, I created a dynamically created DataGrid from a collection. When I include the DataValidation component for one of the columns, the following code compiles, but displays nothing. Without the validation component, it works. I would appreciate it very much if an experienced member could point out my error.
Thank very much.
i. konuk
XAML:
<Window x:Class="SimpleGridX.MainWindow"
xmlns:local="clr-namespace:SimpleGridX"
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"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<ScrollViewer HorizontalAlignment="Left" Margin="10,10,0,0"
VerticalAlignment="Top" Width="497" Height="299"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Auto" >
<DataGrid Name="dgSimpleX" AutoGenerateColumns="True"
SelectionUnit="FullRow" SelectionMode="Extended"
CanUserReorderColumns="False" CanUserAddRows="True">
<DataGridTextColumn Header="Age">
<DataGridTextColumn.Binding>
<Binding Path="[Age]">
<Binding.ValidationRules>
<local:MyValidationRule />
</Binding.ValidationRules>
</Binding>
</DataGridTextColumn.Binding>
</DataGridTextColumn>
</DataGrid>
</ScrollViewer>
</Grid>
</Window>
Window code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
namespace SimpleGridX
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
// Create an instance of the PersonCollection class
PersonCollection people =
new PersonCollection();
InitializeComponent();
dgSimpleX.SelectionUnit = DataGridSelectionUnit.CellOrRowHeader;
dgSimpleX.ItemsSource = people;
dgSimpleX.Items.SortDescriptions.Add(new SortDescription("FirstName", ListSortDirection.Descending));
}
}
}
Object Class:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Windows.Controls;
using System.Globalization;
namespace SimpleGridX
{
class Person : INotifyPropertyChanged
{
private string firstName;
private string lastName;
private float age;
private string occupation;
// Each property calls the OnPropertyChanged method
// when its value changed, and each property that
// affects the Person's Description, also calls the
// OnPropertyChanged method for the Description property.
public string FirstName
{
get
{
return firstName;
}
set
{
if (firstName != value)
{
firstName = value;
OnPropertyChanged("FirstName");
OnPropertyChanged("Description");
}
}
}
public string LastName
{
get
{
return lastName;
}
set
{
if (this.lastName != value)
{
this.lastName = value;
OnPropertyChanged("LastName");
OnPropertyChanged("Description");
}
}
}
public float Age
{
get
{
return age;
}
set
{
if (this.age != value)
{
this.age = value;
OnPropertyChanged("Age");
OnPropertyChanged("Description");
}
}
}
public string Occupation
{
get { return occupation; }
set
{
if (this.occupation != value)
{
this.occupation = value;
OnPropertyChanged("Occupation");
OnPropertyChanged("Description");
}
}
}
// The Description property is read-only,
// and is composed of the values of the
// other properties.
public string Description
{
get
{
return string.Format("{0} {1}, {2} ({3})",
firstName, lastName, age, occupation);
}
}
// The ToString returns the Description,
// so that it is displayed by default when
// the Person object is a binding source.
public override string ToString()
{
return Description;
}
#region INotifyPropertyChanged Members
/// Implement INotifyPropertyChanged to notify the binding
/// targets when the values of properties change.
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(
string propertyName)
{
if (this.PropertyChanged != null)
{
// Raise the PropertyChanged event
this.PropertyChanged(
this,
new PropertyChangedEventArgs(
propertyName));
}
}
#endregion
}
#region ValidationRule
public class MyValidationRule : ValidationRule
{
public override ValidationResult Validate(object value, CultureInfo cultureInfo)
{
float myage = (float)0.0;
try
{
if (((string)value).Length > 0)
{
myage = (float)Decimal.Parse((string)value, NumberStyles.Any, cultureInfo);
}
}
catch
{
return new ValidationResult(false, "Illegal ccharacters");
}
if ((myage < (float)0.0) || (myage > (float)75.0))
{
return new ValidationResult(false, "Not in range");
}
else
{
return new ValidationResult(true, null);
}
}
}
#endregion
}
Collection:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.ObjectModel;
namespace SimpleGridX
{
class PersonCollection : ObservableCollection<Person>
{
public PersonCollection()
{
// Load the collection with dummy data
//
Add(new Person()
{
FirstName = "Elin",
LastName = "Binkles",
Age = 26,
Occupation = "Professional"
});
Add(new Person()
{
FirstName = "Samuel",
LastName = "Bourts",
Age = 28,
Occupation = "Engineer"
});
Add(new Person()
{
FirstName = "Alan",
LastName = "Jonesy",
Age = 37,
Occupation = "Engineer"
});
Add(new Person()
{
FirstName = "Sam",
LastName = "Nobles",
Age = 25,
Occupation = "Engineer"
});
}
}
}
The Age property should be bound without square brackets.
Also.
It'll be bound to two columns if you leave autogenerate true.
Edit:
Also.
You need any columns inside a columns collection.
What you have there produces an error when I try it, because it tries to make your age column into an item and then the code tries to set itemssource.
<DataGrid.Columns>
<DataGridTextColumn Header="Age">
……
</DataGrid.Columns>
I am trying to replicate the solution from Create a custom DataGrid's ItemsSource but my rows are not populating.
The columns appears with the correct headers, but no data in the grid.
Can anyone please tell me what I am doing wrong?
using System;
using System.Windows;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Runtime.CompilerServices;
namespace WPFBindingDataGridTest2
{
public partial class MainWindow : Window
{
public ObservableCollection<MyObject> myList { get; set; }
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
testClass = new ObservableCollection<TestClass>();
testClass.Add(new TestClass(NameValue: "John"));
testClass.Add(new TestClass(NameValue: "Frank"));
testClass.Add(new TestClass(NameValue: "Sarah"));
testClass.Add(new TestClass(NameValue: "David"));
}
}
class TestClass : INotifyPropertyChanged
{
private string name;
public TestClass(string NameValue)
{
this.Name = NameValue;
}
public String Name
{
get { return name; }
set { this.name = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
And the XAML...
<Grid>
<DataGrid x:Name="dataGrid" ItemsSource="{Binding testClass}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
The error I am getting is
System.Windows.Data Error: 40 :
BindingExpression path error:
'testClass' property not found on 'object'''MainWindow'
(Name='')'.BindingExpression:
Path=testClass;
DataItem='MainWindow' (Name='');
target element is 'DataGrid' (Name='dataGrid');
target property is 'ItemsSource' (type 'IEnumerable')
I feel like I am so close to this, but am just missing one thing.
Possible the data context is wrong?
when you assign DataContext, myList is null. When it is created later, property doesn't report about changing (via event)
quick fix is to change the order of operations:
public MainWindow()
{
myList = new ObservableCollection<MyObject>
{
new MyObject() { MyID = "6222" },
new MyObject() { MyID = "12" },
new MyObject() { MyID = "666" }
};
this.DataContext = this;
InitializeComponent();
}
also fix column binding (Binding="{Binding MyID}") or property name (MyId) because binding path should match property name
but I suggest to create a separate class (which implements INotifyPropertyChanged) with myList property, and use an instance of that class (view model) to set DataContext of a window.
Figured it out.
using System;
using System.Windows;
using System.ComponentModel;
using System.Collections.ObjectModel;
using System.Runtime.CompilerServices;
namespace WPFBindingDataGridTest2
{
public partial class MainWindow : Window
{
private ObservableCollection<TestClass> testClass;
public MainWindow()
{
InitializeComponent();
testClass = new ObservableCollection<TestClass>();
testClass.Add(new TestClass(NameValue: "John"));
testClass.Add(new TestClass(NameValue: "Frank"));
testClass.Add(new TestClass(NameValue: "Sarah"));
testClass.Add(new TestClass(NameValue: "David"));
// this.DataContext = this does not appear to be nessecary if it the class is stored in the main window
dataGrid.ItemsSource = testClass;
}
}
class TestClass : INotifyPropertyChanged
{
private string name;
public TestClass(string NameValue)
{
this.Name = NameValue;
}
public String Name
{
get { return name; }
set { this.name = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
XAML
<Window x:Class="WPFBindingDataGridTest2.MainWindow"
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:local="clr-namespace:WPFBindingDataGridTest2"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid x:Name="dataGrid" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
I am working with list view bindings as part of my xamarin forms. I could not able to see the data updated to list view even after data binding is done through xaml. With debugging i observed that, PropertyChanged event is null. The reason for getting this event null is - "Not setting the data context properly". I set the data context as well but no data is populated in list view.
Here is my code.
SampleCode.Xaml
<ContentPage
Title="ListViewBinder"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:SampleNS;assembly=SampleNS"
x:Class="SampleNS.SampleCode">
<ContentPage.Content>
<StackLayout Orientation="Vertical" VerticalOptions="FillAndExpand" >
<ListView x:Name= "listView" ItemsSource="{Binding lsData}" >
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<ViewCell.View>
<Label Text="{Binding Name}"></Label>
</ViewCell.View>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage.Content>
ViewModel.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace SampleNS
{
public class ViewModel : INotifyPropertyChanged
{
private string _name;
public string Name
{
get {return _name;}
set {
if (value.Equals(_name, StringComparison.Ordinal))
{
return;
}
_name = value;
OnPropertyChanged(_name);
}
}
public event PropertyChangedEventHandler PropertyChanged;
void OnPropertyChanged ([CallerMemberName] string propertyName=null)
{
var handler = PropertyChanged;
if (handler != null) {
handler (this, new PropertyChangedEventArgs (propertyName));
}
}
}
}
SampleCode.Xaml.cs
using System;
using System.Collections.Generic;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace SampleNS
{
public partial class SampleCode : ContentPage
{
public static List<ViewModel> lsData;
private void init()
{
//this.BindingContext = lsData;
this.BindingContext = new ViewModel();
this.LoadFromXaml (typeof(SampleCode));
lsData = new List<ViewModel> () {
new ViewModel { Name = "VM1" },
new ViewModel { Name = "VM2" },
new ViewModel { Name = "VM3" },
new ViewModel { Name = "VM4" },
}
I assumed BindingContext is same as DataContext in WPF. (Please suggest if i am wrong)
I doubt about setting the BindingContext.
Please suggest me, where i am doing the mistake. Presently my PropertyChanged event is null and i don't see any data in my list view.
Thanks in advance.
you sample code.xaml.cs should look like this:
using System;
using System.Collections.Generic;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace SampleNS
{
public partial class SampleCode : ContentPage
{
public static List<ViewModel> lsData;
public SampleCode ()
{
InitializeComponent ();
lsData = new List<ViewModel> () {
new ViewModel { Name = "VM1" },
new ViewModel { Name = "VM2" },
new ViewModel { Name = "VM3" },
new ViewModel { Name = "VM4" },
this.BindingContext = lsData;
}
}
}
I am trying to bind an observable collection to a user control but it is not getting updated on user change but it is getting updated when the user control is changed through code. Following is an example i tried. It might be a bit long but it is working so you can copy and paste the code as it is.
Please see my question at the end of the post.
--Customer.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace TestMVVM
{
class Customer : INotifyPropertyChanged
{
private string firstName;
private string lastName;
public string FirstName
{
get { return firstName; }
set
{
if (firstName != value)
{
firstName = value;
RaisePropertyChanged("FirstName");
}
}
}
public string LastName
{
get { return lastName; }
set
{
if (lastName != value)
{
lastName = value;
RaisePropertyChanged("LastName");
}
}
}
#region PropertChanged Block
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this,
new PropertyChangedEventArgs(property));
}
}
#endregion
}
}
--UCTextBox.xaml
<UserControl x:Class="TestMVVM.UCTextBox"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="40" Width="200">
<Grid>
<TextBox Height="23" HorizontalAlignment="Left" Margin="10,10,0,0" Name="txtTextControl"
VerticalAlignment="Top" Width="120" />
</Grid>
--UCTextBox.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.ComponentModel;
namespace TestMVVM
{
///
/// Interaction logic for UCTextBox.xaml
///
public partial class UCTextBox : UserControl, INotifyPropertyChanged
{
public UCTextBox()
{
InitializeComponent();
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(UCTextBox),
new UIPropertyMetadata(string.Empty, new PropertyChangedCallback(textChangedCallBack)));
static void textChangedCallBack(DependencyObject property, DependencyPropertyChangedEventArgs args)
{
UCTextBox pasTextBox = (UCTextBox)property;
pasTextBox.txtTextControl.Text = (string)args.NewValue;
}
public string Text
{
get
{
return (string)GetValue(TextProperty);
}
set
{
SetValue(TextProperty, value);
NotifyPropertyChanged("Text");
}
}
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
}
-- Window1.xaml
<Window x:Class="TestMVVM.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestMVVM"
Title="Window1" Height="300" Width="300">
<Grid>
<local:UCTextBox x:Name="txtUC" />
<Button Height="23" HorizontalAlignment="Left" Margin="39,0,0,82"
Name="btnUpdate" VerticalAlignment="Bottom" Width="75" Click="btnUpdate_Click">Update</Button>
<Button Height="23" Margin="120,0,83,82" Name="btnChange" VerticalAlignment="Bottom" Click="btnChange_Click">Change</Button>
</Grid>
-- Window1.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
namespace TestMVVM
{
///
/// Interaction logic for Window1.xaml
///
public partial class Window1 : Window
{
CustomerHeaderViewModel customerHeaderViewModel = null;
public Window1()
{
InitializeComponent();
customerHeaderViewModel = new CustomerHeaderViewModel();
customerHeaderViewModel.LoadCustomers();
txtUC.DataContext = customerHeaderViewModel.Customers[0];
Binding binding = new Binding();
binding.Source = customerHeaderViewModel.Customers[0];
binding.Path = new System.Windows.PropertyPath("FirstName");
binding.Mode = BindingMode.TwoWay;
binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
txtUC.SetBinding(UCTextBox.TextProperty, binding);
}
private void btnUpdate_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(customerHeaderViewModel.Customers[0].FirstName);
}
private void btnChange_Click(object sender, RoutedEventArgs e)
{
txtUC.Text = "Tom";
}
}
class CustomerHeaderViewModel
{
public ObservableCollection Customers { get; set; }
public void LoadCustomers()
{
ObservableCollection customers = new ObservableCollection();
customers.Add(new Customer { FirstName = "Jim", LastName = "Smith", NumberOfContracts = 23 });
Customers = customers;
}
}
}
When i run the Window1.xaml my user control shows the data as "Jim". Now when i change the text to say "John" and click on Update, the messagebox still shows "Jim" that means the observable collection is not getting updated. When i click on Change button the user control changes the data to "Tom". Now when i click on Update button the Messagebox shows "Tom". Can anyone please tell me how to achieve the updation of observable collection by changing the data in user control rather than through code?
That's because you're not handling the txtTextControl.TextChanged event, so your Text dependency property is never updated.
Anyway, you don't need to handle that manually with a DependencyPropertyChangedCallback and an event handler, you can just bind the txtTextControl.Text to the Text dependency property :
<TextBox Height="23" HorizontalAlignment="Left" Margin="10,10,0,0" Name="txtTextControl"
VerticalAlignment="Top" Width="120"
Text="{Binding Path=Text, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:UCTextBox}}}"/>
An observable collection, observers the collection only. You will get notified when items are added or deleted not when fields of a single items did change. That is something completely different. Like Thomas Levesque said, you just need to bind the right property.
Is there any way, how to force ObservableCollection to fire CollectionChanged?
I have a ObservableCollection of objects ListBox item source, so every time I add/remove item to collection, ListBox changes accordingly, but when I change properties of some objects in collection, ListBox still renders the old values.
Even if I do modify some properties and then add/remove object to the collection, nothing happens, I still see old values.
Is there any other way around to do this? I found interface INotifyPropertyChanged, but I don't know how to use it.
I agree with Matt's comments above. Here's a small piece of code to show how to implement the INotifyPropertyChanged.
===========
Code-behind
===========
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Data;
using System.Windows.Documents;
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
Nicknames names;
public Window1()
{
InitializeComponent();
this.addButton.Click += addButton_Click;
this.names = new Nicknames();
dockPanel.DataContext = this.names;
}
void addButton_Click(object sender, RoutedEventArgs e)
{
this.names.Add(new Nickname(myName.Text, myNick.Text));
}
}
public class Nicknames : System.Collections.ObjectModel.ObservableCollection<Nickname> { }
public class Nickname : System.ComponentModel.INotifyPropertyChanged
{
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
void Notify(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propName));
}
}
string name;
public string Name
{
get { return name; }
set
{
name = value;
Notify("Name");
}
}
string nick;
public string Nick
{
get { return nick; }
set
{
nick = value;
Notify("Nick");
}
}
public Nickname() : this("name", "nick") { }
public Nickname(string name, string nick)
{
this.name = name;
this.nick = nick;
}
public override string ToString()
{
return Name.ToString() + " " + Nick.ToString();
}
}
}
XAML
<Window x:Class="WpfApplication1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Grid>
<DockPanel x:Name="dockPanel">
<TextBlock DockPanel.Dock="Top">
<TextBlock VerticalAlignment="Center">Name: </TextBlock>
<TextBox Text="{Binding Path=Name}" Name="myName" />
<TextBlock VerticalAlignment="Center">Nick: </TextBlock>
<TextBox Text="{Binding Path=Nick}" Name="myNick" />
</TextBlock>
<Button DockPanel.Dock="Bottom" x:Name="addButton">Add</Button>
<ListBox ItemsSource="{Binding}" IsSynchronizedWithCurrentItem="True" />
</DockPanel>
</Grid>
Modifying properties on the items in your collection won't fire NotifyCollectionChanged on the collection itself - it hasn't changed the collection, after all.
You're on the right track with INotifyPropertyChanged. You'll need to implement that interface on the class that your list contains. So if your collection is ObservableCollection<Foo>, make sure your Foo class implements INotifyPropertyChanged.