I am working on a WPF Projet, in which I have a view with two usercontrols on it. This is basically a UserControl with a grid on it and another one with a edit panel to edit the selected object in the DataGrid. The edit panel control, consists of textboxes to edit properties of the selected object in the other control and a button to save. What I would like to do is to pass the selected object to the edit panel,that is each time a object is selected in the grid, the edit panel updates to select that same object. What is the best way to do this, please help?An example would be super :0)
The best way to deal with this is using the MVVM pattern, where both of your user controls bind to the same ViewModel.
The grid can bind to your collection (List<>) of objects that you want to show, and it can also bind its SelectedRow/SelectedItem property to a corresponding property on the ViewModel called SelectedItem (or similar). This means that every time a row is selected in the grid, the underlying data object will be populated into the property on the ViewModel.
You then bind your details user control to the same SelectedItem property on the ViewModel. Check this very simple example of a DataGrid and TextBox binding to the same SelectedItem property:
ViewModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
namespace WpfApplication11
{
public class MyViewModel : INotifyPropertyChanged
{
public List<Customer> MyList
{
get { return _myList; }
set
{
_myList = value;
OnPropertyChanged("MyList");
}
}
public Customer SelectedItem
{
get { return _selectedItem; }
set
{
_selectedItem = value;
OnPropertyChanged("SelectedItem");
}
}
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
private Customer _selectedItem;
private List<Customer> _myList;
}
public class Customer
{
public string Name { get; set; }
}
}
MainWindow
<Window x:Class="WpfApplication11.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:swm="clr-namespace:System.Windows.Media;assembly=WindowsBase"
xmlns:swm1="clr-namespace:System.Windows.Media;assembly=PresentationCore"
Title="MainWindow" Height="289" Width="525">
<Grid>
<DataGrid AutoGenerateColumns="True" Margin="12,12,12,38" Name="dataGrid1" ItemsSource="{Binding MyList}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" />
<Label Content="Name" Height="28" HorizontalAlignment="Left" Margin="12,222,0,0" Name="label1" VerticalAlignment="Top" Width="57" />
<TextBox Text="{Binding Path=SelectedItem.Name, Mode=TwoWay}" Height="23" HorizontalAlignment="Left" Margin="60,222,0,0" Name="textBox1" VerticalAlignment="Top" Width="267" />
</Grid>
</Window>
MainWindow code behind
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;
namespace WpfApplication11
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
MyViewModel vm = new MyViewModel();
vm.MyList = new List<Customer>(new Customer[] { new Customer() { Name = "Bugs Bunny" }, new Customer() { Name = "Elmer Fudd" } });
this.DataContext = vm;
}
}
}
If you run this and then select a row in the grid the name of the customer will be populated into the textbox underneath. If you then modify the name in the textbox and remove focus from it (TAB out of it) then the row in the datagrid will get updated with the new name - all through binding.
For further information, there have previously been a few thousand questions on Stack Overflow regarding the MVVM pattern with WPF, many of them specifically about master-detail views like the one you want.
In your XAML markup, just bind the edit panel's object to the selected object in the grid object.
Related
I learn bindings in WPF via book. I have wrote such code:
using System;
namespace WpfBinding {
enum SomeColors {
Red,
Green,
Blue,
Gray
}
}
and
using System;
namespace WpfBinding {
class TestItem {
SomeColors color;
public TestItem(SomeColors color) {
Color = color;
}
internal SomeColors Color {
get { return color; }
set { color = value; }
}
public override string ToString() {
return Color.ToString();
}
}
}
XAML of my Window:
<Window x:Class="WpfBinding.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ListBox x:Name="listBox" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Margin="5"/>
<ComboBox x:Name="comboBox" HorizontalAlignment="Stretch"
VerticalAlignment="Top" Margin="5" Grid.Column="1"/>
</Grid>
</Window>
I have tried create binding through code:
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace WpfBinding {
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
// Data for listbox
TestItem[] items = new TestItem[] {
new TestItem(SomeColors.Red),
new TestItem(SomeColors.Green),
new TestItem(SomeColors.Green),
new TestItem(SomeColors.Red),
new TestItem(SomeColors.Blue),
new TestItem(SomeColors.Red),
};
// Create ObservableCollection item
ObservableCollection<TestItem> collection = new ObservableCollection<TestItem>(items);
listBox.ItemsSource = collection;// set data for listbox
comboBox.ItemsSource = Enum.GetValues(typeof(SomeColors)); // Get items from my enum
// Create bindings
Binding bind = new Binding();
bind.Source = listBox;
bind.Path = new PropertyPath("SelectedItem.Color");
bind.Mode = BindingMode.TwoWay;
comboBox.SetBinding(ComboBox.SelectedItemProperty, bind);
}
}
}
But my binding ain't working. Why?
Screen:
Here is the error -
System.Windows.Data Error: 40 : BindingExpression path error: 'Color' property
not found on 'object' ''TestItem' (HashCode=13974362)'.
BindingExpression:Path=SelectedItem.Color; DataItem='ListBox' (Name='listBox');
target element is 'ComboBox' (Name='comboBox'); target property is 'SelectedItem'
(type 'Object')
You need to make the property Color public instead of internal.
From MSDN here -
The properties you use as binding source properties for a binding must
be public properties of your class. Explicitly defined interface
properties cannot be accessed for binding purposes, nor can protected,
private, internal, or virtual properties that have no base
implementation.
It's always useful to watch the Output window of Visual Studio when debugging! Had you looked there, you'd have seen this:
System.Windows.Data Error: 40 : BindingExpression path error: 'Color' property not found on 'object' ''TestItem' (HashCode=20856310)'. BindingExpression:Path=SelectedItem.Color; DataItem='ListBox' (Name='listBox'); target element is 'ComboBox' (Name='comboBox'); target property is 'SelectedItem' (type 'Object')
Exactly, binding can be done with public properties only, so
internal SomeColors Color
should be
public SomeColors Color
I think the problem is that your classes are not implementing INotifyPropertyChanged.
In order for the bindings to know when a property has changed it's value, you have to send it notification, and you do that with INotifyPropertyChanged.
UPDATE
So your listbox is bound to an ObservableCollection which does provide change notifiations, but only to the list box and only if you add or remove items from the collections.
You might also want to enable WPF Binding trace information in visual studio (http://msdn.microsoft.com/en-us/library/dd409960%28v=vs.100%29.aspx) that might help you figure out what is going on too.
The last thing I noticed is that the Color property of your TestItem class is marked as internal. WPF won't have access to that property unless it's public.
Thanks all. I have edit my code:
using System;
using System.ComponentModel;
namespace WpfBinding {
public class TestItem : INotifyPropertyChanged{
SomeColors color;
public TestItem(SomeColors color) {
Color = color;
}
public SomeColors Color {
get { return color; }
set { color = value;
OnPropertyChanged("Color");
}
}
public override string ToString() {
return Color.ToString();
}
void OnPropertyChanged(String name) {
PropertyChangedEventHandler temp = PropertyChanged;
if (null != temp) {
temp(this, new PropertyChangedEventArgs(name));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
and
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
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;
namespace WpfBinding {
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window {
public MainWindow() {
InitializeComponent();
// Data for listbox
TestItem[] items = new TestItem[] {
new TestItem(SomeColors.Red),
new TestItem(SomeColors.Green),
new TestItem(SomeColors.Green),
new TestItem(SomeColors.Red),
new TestItem(SomeColors.Blue),
new TestItem(SomeColors.Red),
};
// Create ObservableCollection item
ObservableCollection<TestItem> collection = new ObservableCollection<TestItem>(items);
listBox.ItemsSource = collection;// set data for listbox
ObservableCollection<SomeColors> collection2 = new
ObservableCollection<SomeColors>(Enum.GetValues(typeof(SomeColors)).Cast<SomeColors>());
comboBox.ItemsSource = collection2; // Get items from my enum
// Create bindings
Binding bind = new Binding();
bind.Source = listBox;
bind.Path = new PropertyPath("SelectedItem.Color");
bind.Mode = BindingMode.TwoWay;
comboBox.SetBinding(ComboBox.SelectedItemProperty, bind);
}
}
}
Look Screen, please:
I have a TabControl binding to some items. Underneath it is a Button where I can add items dynamically. On adding an item, the new item should become the active Tab (works fine with TabControl.SelectedItem):
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:this="clr-namespace:WpfApplication1"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<TabControl ItemsSource="{Binding Items}"
SelectedItem="{Binding SelectedItem, Mode=OneWay}">
<TabControl.ContentTemplate>
<DataTemplate>
<this:UserControl1 />
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
<Button Content="Foo" Click="Button_Click"/>
</StackPanel>
</Window>
Code-behind:
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Windows;
namespace WpfApplication1
{
public partial class MainWindow : INotifyPropertyChanged
{
public ObservableCollection<Foo> Items { get; set; }
public Foo SelectedItem { get { return Items.Last(); } }
public event PropertyChangedEventHandler PropertyChanged;
public MainWindow()
{
Items = new ObservableCollection<Foo>();
Items.Add(new Foo {Bar = "bar"});
InitializeComponent();
DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Items.Add(new Foo {Bar = "bar"});
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("Items"));
PropertyChanged(this, new PropertyChangedEventArgs("SelectedItem"));
}
}
}
public class Foo { public string Bar { get; set; } }
}
The UserControl1 looks like this:
<UserControl x:Class="WpfApplication1.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel>
<TextBox/>
<TextBox x:Name="_textBox"
DataContextChanged="OnDataContextChanged"
Text="{Binding Bar}" />
</StackPanel>
</UserControl>
And the code-behind of it should focus _textBox and selectAll its text when the user clicks on the tab:
using System.Windows;
namespace WpfApplication1
{
public partial class UserControl1
{
public UserControl1()
{
InitializeComponent();
}
private void OnDataContextChanged(object sender,
DependencyPropertyChangedEventArgs e)
{
_textBox.Focus();
_textBox.SelectAll();
}
}
}
I try to achieve that with the DataContextChanged-event, but due to its unpredictability (s.f. http://msdn.microsoft.com/en-us/library/system.windows.frameworkelement.datacontextchanged.aspx), it doesn't work all the time. I also tried it with the Loaded-event, but this will be called only once when the DataTemplate is loaded.
So, I think I need to receive the Loaded-event every time the DataContext has changed and the data-binding engine has finished its job. Is there such an event?
Are you wanting to select the text when the user adds a tab AND when the user clicks on a different tab?
If this is the case you may want to to handle this with two event handlers - The tab changed event for the tab control - and then setting it in code when you add a new item.
The DataContext according to your code does not change. It is set to the main window and then inherited down to the child controls.
public MainWindow()
{
Items = new ObservableCollection<Foo>();
Items.Add(new Foo {Bar = "bar"});
InitializeComponent();
DataContext = this;
}
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.
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.
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.