= Data Binding =
If XAML markup is going to be used for data binding, you must use an ''ObjectDataProvider'' or ''XMLDataProvider''. If using an existing object, you can only use ''DataContext'' as your data binding. This requires the object manipulation to be done programmatically only.
== Attributes ==
=== Mode ===
To specify the direction and frequency of the binding.
* OneTime
* OneWay
* TwoWay
* OneWayToSource
=== Converter ===
To specify a converter to use in data conversion from the target to the source.
=== ConverterParameter ===
To specify additional parameters to the converter.
Example:
. . .
. . .
Custom type declared in Acme.App.Types.dll assembly:
public enum TSide
{
Left
Right
}
Custom ValueConverter declared in Acme.App.ViewModel.dll assembly (view model):
///============================================================================================
///
/// Class TInstrumentSideDetectedToVisibilityConverter
///
///============================================================================================
public class TInstrumentSideDetectedToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Visibility _visibility;
if (
((TInstrumentDetectedResults)value == TInstrumentDetectedResults.Left) && ((TSide)parameter == TSide.Left) ||
((TInstrumentDetectedResults)value == TInstrumentDetectedResults.Both) && ((TSide)parameter == TSide.Left) ||
((TInstrumentDetectedResults)value == TInstrumentDetectedResults.None) && ((TSide)parameter == TSide.Left) ||
((TInstrumentDetectedResults)value == TInstrumentDetectedResults.Right) && ((TSide)parameter == TSide.Right) ||
((TInstrumentDetectedResults)value == TInstrumentDetectedResults.Both) && ((TSide)parameter == TSide.Right) ||
((TInstrumentDetectedResults)value == TInstrumentDetectedResults.None) && ((TSide)parameter == TSide.Right)
)
{
_visibility = Visibility.Visible;
}
else
{
_visibility = Visibility.Collapsed;
}
return _visibility;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
==== UpdateSourceTrigger ====
To specify when the target property is updated.
* LostFocus
* PropertyChanged
* Explicit
=== FallbackValue ===
To specify a default value when the binding fails.
=== Source, RelativeSource, ElementName ===
Only one of these Binding attributes should be set:
* ''Source'', when referring to an object (see also [[http://msdn.microsoft.com/en-us/library/system.windows.data.binding.source.aspx|Binding.Source]]): Example:
...
...
C# examples:
// Examples
Binding my_binding1 = new Binding(); my_binding1.Source = my_data_object;
Binding my_binding2 = new Binding("PersonName"); my_binding2.Source = myDataSource;
* ''RelativeSource'', when referring to current element with ''Self'', ''FindAncestor'', and ''PreviousData'' (see also [[http://msdn.microsoft.com/en-us/library/system.windows.data.binding.relativesource.aspx|Binding.RelativeSource]]): Example: Using ''RelativeSource'' to find Ancestor:C# examples:
// Examples
Binding my_binding1 = new Binding(); my_binding1.RelativeSource = RelativeSource.Self;
Binding my_binding2 = new Binding(); my_binding2.RelativeSource = new RelativeSource(RelativeSourceMode.FinAncestor, typeof(StackPanel), 1);
Binding my_binding3 = new Binding(); my_binding3.RelativeSource = RelativeSource.TemplatedParent;
* ''ElementName'', when referring to a control (see also [[http://msdn.microsoft.com/en-us/library/system.windows.data.binding.elementname.aspx|Binding.ElementName]]): Example: GreenBlueRed Or an example using a ''Slider'': Alternatively: C# examples:
// Examples
Binding my_binding1 = new Binding(); my_binding1.ElementName = "btnStart1";
Binding my_binding2 = new Binding("Value"); my_binding2.ElementName = "Slider1";
References: [[http://wpfguy.blogspot.com/2009/01/what-is-difference-source-vs.html|What is the Difference Between ''Source'' and ''RelativeSource'']]
=== DataContext ===
To bind to the default ''DataContext'' of an object (or parent), use:
Programmatically in C#:
ccDetails.SetBinding(ContentControl.ContentProperty, new Binding());
If you need to include the path, then use:
ccDetails.SetBinding(ContentControl.ContentProperty, new Binding("Person.Name"));
If the path contains an index, then use:
ccDetails.SetBinding(ContentControl.ContentProperty, new Binding("Person.Address[0]")); // numeric-based index
ccDetails.SetBinding(ContentControl.ContentProperty, new Binding("Person.Address[Home]")); // key-based index
== Data Providers ==
=== DataContext ===
Create Binding programmatically in C#:
Product ProdDataSource = new Product("Bicycle");
Binding ProdBinding = new Binding("Name");
ProdBinding.Source = ProdDataSource;
// txtProdName is an instance of TextBlock
txtProdName.SetBinding(TextBlock.TextProperty, ProdBinding);
Alternatively, for existing object instance:
lstProducts.DataContext = aProdSpecsListObject;
=== Built-in (Primitive) Types ===
In XAML:
42Oui
...
* References: [[http://msdn.microsoft.com/en-us/library/ee792002.aspx|MSDN: Built-in Types for Common XAML Language Primitives]]
=== ObjectDataProvider ===
This provides binding to .NET data types.
To reference data types defined in a library, add the library reference (such as ''xmlns:local="clr-namespace:Acme.Products.Specifications'') to namespace in user control's XAML, then add ''ObjectDataProvider'' resource.
....
Alternatively, as an XAML element:
Declaring a data source with parameters vs no parameters:
Acme
Define the data type in library. Eg: Acme.Products.Specification.dll
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Acme.Products.Specifications
{
public class TProductSpecificationList : List
{
public TCustomCompany CustomCompany = TCustomCompany.Acme;
// Parameterless Constructor
public TProductSpecificationList(): this(TCustomCompany.Acme)
{
}
// Constructor with Parameters
public TProductSpecificationList(TCustomCompany aCustomCompany)
{
if (CustomCompany == TCustomCompany.Acme)
{
this.Add("Bicycle");
this.Add("Tricyle");
}
}
}
}
Perform data binding in user control's WPF:
=== XmlDataProvider ===
This provides binding to XML data (as a resource, or external file). Create an XML data island resource for the window o user control.
Bicycle2RedTricycle3Blue
....
Alternatively, put the data in an XML file.
...
Call the data locally:
Remotely:
Perform data binding in one of these 2 ways:
== Clearing Binding ==
To clear data binding programmatically (for a ''TextBox'' called ''txtName''):
BindingOperations.ClearBinding(txtName, TextBox.TextProperty);
= Examples =
== Syntax ==
Declaring an ''ObjectDataProvider'':
This notation will create an instance of that class, and you use the ''x:Key'' name to access it.
== Connecting to a user defined object ==
...
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Acme.Products.Specifications
{
public class TProductSpecification
{
public string Type { get; set; }
public string Name { get; set; }
public int Wheels{ get; set; }
public string Color { get; set; }
public TProductSpecification()
{
Type = "";
Name = "";
Wheels = 4;
Color = "";
}
public override string ToString()
{
return String.Format("{0}={1}", this.Name, this.Type);
}
}
public class TProductSpecificationList : List
{
private TProductSpecification prod;
public TProductSpecificationList()
{
prod = new TProductSpecification();
prod.Type = "prod_Bicycle";
prod.Name = "Bicycle";
prod.Wheels = 2;
prod.Color = "Red";
this.Add(prod);
prod = new TProductSpecification();
prod.Type = "prod_Tricycle";
prod.Name = "Tricycle";
prod.Wheels = 3;
prod.Color = "Blue";
this.Add(prod);
}
}
}
== Connecting to a XAML Object ==
...
ABC 123 DEF 456
...
...
Source: [[http://www.devx.com/codemag/Article/40833|DevX: Flexible and Powerful Data Binding with WPF, Part 2]]
And in C# you can access that instance as well:
void Window_Loaded(object sender, RoutedEventArgs args)
{
DataConnection dc1 = this.FindResource("emp") as DataConnection;
DataConnection dc2 = this.Resources["emp"] as DataConnection;
}
''FindResource("emp")'' will search the element's resource dictionary as well as any parent elements' resource dictionaries and the application resources.
''Resources["emp"]'' will search only the resource dictionary of that element.
The documentation recommends the first approach for normal resource lookups, but provides the second approach for when you are retrieving resources from a "known resource dictionary location ... to avoid the possible performance and scope implications of run-time key lookup.
Source: [[http://stackoverflow.com/questions/2039425/access-xaml-instantiated-object-from-c|Access XAML instantiated object from C#]]
== Connecting to a Local Property ==
You must use Dependency Properties to connect to a local property using data binding.
First, define the Dependency Property in C#:
using System.Windows;
namespace SOTCBindingValidation
{
public partial class Window1 : Window
{
public static readonly DependencyProperty IPAddressProperty =
DependencyProperty.Register("IPAddress", typeof(string),
typeof(Window1), new UIPropertyMetadata("1.1.1.1"));
public string IPAddress
{
get { return (string)GetValue(IPAddressProperty); }
set { SetValue(IPAddressProperty, value); }
}
public Window1()
{ InitializeComponent(); }
}
}
Then make a reference to the required property in XAML:
Enter An IPv4 Address:
Source: [[http://www.switchonthecode.com/tutorials/wpf-tutorial-binding-validation-rules|WPF - Binding Validation Rules]]
== Connecting to an XML File ==
XML File:
Acme's 12th Annual Continuing Education Workshop12AcmeWorkshop12Orlando Marriott Lake Mary1501 International ParkwayLake MaryFL32746407.995.1100800.380.7724407.995.1101www.marriotthotels.com2011/01/142011/01/15Premium100Standard200Other200Acme's 13th Annual Continuing Education Workshop13AcmeWorkshop13Orlando Marriott Lake Mary1501 International ParkwayLake MaryFL32746407.995.1100800.380.7724407.995.1101www.marriotthotels.com2012/01/242012/01/25Premium100Standard200Other200
Data binding for a ''DataGrid'':
...
...
Data binding for a ''ListBox'':
...
Workshops||
...
== Connecting to a ''DataTable'' ==
...
using System;
using System.Collections.Generic;
using System.Data;
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 Acme.Products.Specifications
{
///
/// Interaction logic for ProductSpecsControl.xaml
///
public partial class ProductSpecsControlClass : UserControl
{
private DataTable _ProdSpecDataTable;
public DataTable ProdSpecDataTable
{
get { return _ProdSpecDataTable; }
}
public ProductSpecsControl()
{
_ProdSpecDataTable= new DataTable();
_ProdSpecDataTable.Columns.Add(new DataColumn("Type", typeof(string)));
_ProdSpecDataTable.Columns.Add(new DataColumn("Name", typeof(string)));
_ProdSpecDataTable.Columns.Add(new DataColumn("Wheels", typeof(int)));
_ProdSpecDataTable.Columns.Add(new DataColumn("Color", typeof(string)));
//var row = _ProdSpecDataTable.NewRow();
DataRow row = _ProdSpecDataTable.NewRow();
_ProdSpecData.Rows.Add(row);
row["Type"] = "prod_Bicycle";
row["Name"] = "Bicycle";
row["Wheels"] = 2;
row["Color"] = "Red";
row = _ProdSpecDataTable.NewRow();
_ProdSpecData.Rows.Add(row);
row["Type"] = "prod_Tricycle";
row["Name"] = "Tricycle";
row["Wheels"] = 3;
row["Color"] = "Blue";
InitializeComponent();
}
}
}
== Connecting a ListBox to a List of Objects ==
Define the object list. ''PatientInfo.cs'':
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
namespace DataBinding.Patient
{
#region Patient Information record definition
///
/// Patient Information class
///
public class TPatientInfo
{
public string FirstName
{ get; set; }
public string LastName
{ get; set; }
public int ID
{ get; set; }
}
#endregion
#region Patient List using ObservableCollection
///
/// Patient List using ObservableCollection<> (instead of List<>).
/// This approach allows the ListBox to see any updates performed
/// on the actual data model.
///
public class TPatientListOC: ObservableCollection
{
public void PopulateList()
{
TPatientInfo pat = new TPatientInfo();
pat.LastName = "Doe";
pat.FirstName = "John";
pat.ID = 11;
this.Add(pat);
pat = new TPatientInfo();
pat.LastName = "Smith";
pat.FirstName = "John";
pat.ID = 22;
this.Add(pat);
pat = new TPatientInfo();
pat.LastName = "Casell";
pat.FirstName = "Mario";
pat.ID = 33;
this.Add(pat);
}
}
#endregion
}
=== Declarative Method ===
Declare an instance to list of objects, then databind a ''ListBox'' to it. Note that ''ListBox'' needs a ''DataContext'' and ''DataTemplate'' defined for it to work. ''ucPatientInfo.xaml'':
Patient ListRecord DetailsLast NameFirst NameID
''ucPatientInfo.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;
namespace DataBinding.Patient
{
///
/// Interaction logic for ucDataBinding.xaml
///
public partial class ucPatientInfo : UserControl
{
private TPatientListOC patlist = new TPatientListOC();
public ucPatientInfo()
{
InitializeComponent();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
patlist = (TPatientListOC)FindResource("PatientListOCDS");
//patlist.PopulateList();
}
private void btnLoadList_Click(object sender, RoutedEventArgs e)
{
patlist.PopulateList();
}
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
if (txtLastName.Text != "" && txtFirstName.Text != "" && txtID.Text != "")
{
TPatientInfo pat = new TPatientInfo();
pat.LastName = txtLastName.Text;
pat.FirstName = txtFirstName.Text;
pat.ID = Convert.ToInt32(txtID.Text);
patlist.Add(pat);
}
else
{
MessageBox.Show("Please fill in all fields with valid data before adding record.");
}
}
private void btnClearList_Click(object sender, RoutedEventArgs e)
{
patlist.Clear();
}
private void btnDeleteSelection_Click(object sender, RoutedEventArgs e)
{
patlist.RemoveAt(lstPatientList.SelectedIndex);
}
}
}
=== Programmatic Method ===
Programmaticaly create an instance to a list of objects, then programmatically databind a ''ListBox'' to it. Note that the data binding is set to the ''ListBox'''s ''DataContext'', and requires a ''DataTemplate'' defined for it to work.
''ucPatientInfo.xaml'':
Patient ListRecord DetailsLast NameFirst NameID
''ucPatientInfo.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;
namespace DataBinding.Patient
{
///
/// Interaction logic for ucDataBinding.xaml
///
public partial class ucPatientInfo : UserControl
{
private TPatientListOC patlist = new TPatientListOC();
public ucPatientInfo()
{
InitializeComponent();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
DataBinding();
}
//---------------------------------------------------------------------------
// Create Data Binding programmatically.
//---------------------------------------------------------------------------
private void DataBinding()
{
Binding bindListItems = new Binding();
bindListItems.Source = patlist;
bindListItems.Mode = BindingMode.OneWay; // TwoWay does not work for ListBox
lstPatientList.SetBinding(ListBox.ItemsSourceProperty, bindListItems);
}
private void btnLoadList_Click(object sender, RoutedEventArgs e)
{
patlist.PopulateList();
}
private void btnAdd_Click(object sender, RoutedEventArgs e)
{
if (txtLastName.Text != "" && txtFirstName.Text != "" && txtID.Text != "")
{
TPatientInfo pat = new TPatientInfo();
pat.LastName = txtLastName.Text;
pat.FirstName = txtFirstName.Text;
pat.ID = Convert.ToInt32(txtID.Text);
patlist.Add(pat);
}
else
{
MessageBox.Show("Please fill in all fields with valid data before adding record.");
}
}
private void btnClearList_Click(object sender, RoutedEventArgs e)
{
patlist.Clear();
}
private void btnDeleteSelection_Click(object sender, RoutedEventArgs e)
{
patlist.RemoveAt(lstPatientList.SelectedIndex);
}
}
}
== IValueConverter ==
Data binding to properties that do not match the type require a value converter (using ''IValueConverter''). For example, converting from ''Visibility'' (''Visible'', ''Hidden'', or ''Collapsed'') into ''bool'':
// using System.Windows.Data; // Found in PresentationFramework assembly
public class TBoolToVisibilityConverter: IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
Visibility _visibility;
if ((bool)value == true)
{
_visibility = Visibility.Visible;
}
else
{
_visibility = Visibility.Collapsed;
}
return _visibility;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
Use it as:
...
Bluetooth Support
...
A value converter for an image path that needs the full path prefixed to it:
// using System.Windows.Data; // Found in PresentationFramework assembly
public class TImgPathConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string str = (string)value;
return "pack://application:,,,/AHI.Products.Specifications;component/" + str;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}
...
Shell/Case View
...
Microsoft's example:
// using System.Windows; // Found in WindowsBase assembly
// using System.Windows.Data; // Found in PresentationFramework assembly
[ValueConversion(typeof(DateTime), typeof(String))]
public class DateConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
DateTime date = (DateTime)value;
return date.ToShortDateString();
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string strValue = value as string;
DateTime resultDateTime;
if (DateTime.TryParse(strValue, out resultDateTime))
{
return resultDateTime;
}
return DependencyProperty.UnsetValue;
}
}
XML:
Start Date:
Source: [[http://msdn.microsoft.com/en-us/library/system.windows.data.ivalueconverter.aspx|MSDN: IValueConverter Interface]]
=== IValueConverter using Parameters ===
==== Example 1 ====
XAML:
C#:
public class StringFormatConverter:IValueConverter
{
#region IValueConverter Members
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string formatString = parameter.ToString();
return String.Format("{0:" + formatString + "}", value);
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException("This Convert supports only OneWay binding");
}
#endregion
}
==== Example 2 ====
XAML:
Customizing the PropertyViewer is also easy:
And the C# code behind:
public class PropertyViewer : IValueConverter
{
///
/// Implements IValueConverter.Convert.
///
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
// Fetch parameters
var propertyInfos = (null != value) ? value.GetType().GetProperties() : new PropertyInfo[0];
var propertyNames = (null != parameter) ? parameter.ToString().Split() : new string[0];
// Prepare a collection to return
var propertyDetails = new List();
// No names specified (or no value/properties available)
if ((0 == propertyNames.Length) || (0 == propertyInfos.Length))
{
// Add all properties to the collection
foreach (var propertyInfo in propertyInfos)
{
propertyDetails.Add(new PropertyDetails(GetPropertyName(propertyInfo), GetPropertyValue(propertyInfo, value)));
}
}
else
{
// For each property name specified
foreach (var propertyName in propertyNames)
{
// Try to match the name against each property in turn
bool found = false;
foreach (var propertyInfo in propertyInfos)
{
if (propertyName == propertyInfo.Name)
{
// Match; add the property to the collection
propertyDetails.Add(new PropertyDetails(GetPropertyName(propertyInfo), GetPropertyValue(propertyInfo, value)));
found = true;
break;
}
}
if (!found && (0 < propertyName.Length))
{
// No matches (and not an empty name); throw an exception
throw new ArgumentException("Property \"" + propertyName + "\" does not exist on object " + value + ".");
}
}
}
// Return the collection
return propertyDetails;
}
///
/// Implements IValueConverter.ConvertBack.
///
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException("PropertyViewer does not use ConvertBack.");
}
///
/// Gets the name of the specified property.
///
/// property
/// name
private static string GetPropertyName(PropertyInfo propertyInfo)
{
// Look for DisplayNameAttribute
var displayNameAttributes = propertyInfo.GetCustomAttributes(typeof(DisplayNameAttribute), true);
// Return first DisplayNameAttribute.DisplayName or property name if none present
return (0 < displayNameAttributes.Length) ? ((DisplayNameAttribute)(displayNameAttributes[0])).DisplayName : propertyInfo.Name;
}
///
/// Gets the value of the specified property for an instance.
///
/// property
/// instance
/// value
private static object GetPropertyValue(PropertyInfo propertyInfo, object instance)
{
try
{
// Return instance's value
return propertyInfo.GetValue(instance, null);
}
catch (TargetParameterCountException)
{
// Likely an indexer (ex: Array.Items); skip complex properties here
return null;
}
}
}
Source: [[http://blogs.msdn.com/b/delay/archive/2008/05/04/ivalueconverter-the-swiss-army-knife-of-bindings-propertyviewer-sample-is-a-wpf-silverlight-visualization-and-debugging-aid.aspx|IValueConverter: The Swiss Army Knife of Bindings...]]
== Multi-Binding ==
To display 2 different variables in a same field (eg: 3.14 = May 5, 2012):
= Debugging =
== Add Tracing to Output Window ==
1. Add ''diag:PresentationTraceSources.TraceLevel=High'' to your binding. For example:
2. Add a value converter to the binding, to be able to put a break point in debugger.
See more:
* [[https://spin.atomicobject.com/2013/12/11/wpf-data-binding-debug/|How To Debug Data Binding Issues in WPF]]
== Enable Debug Output ==
* Enable WPF debug output. In Visual Studio, Options > Debugging > Output Window > WPF Trace Settings > Data Binding > All.
* Add a high TraceLevel to your binding: PresentationTraceSources.SetTraceLevel(NewBinding, PresentationTraceLevel.High);
* Run application, and review log in "Output" tab in Visual Studio.
* Add ''System.Diagnostics'' entry to ''app.exe.config''. See:
* [[swdev:dotnet:debugging|Debugging for .NET]]
* [[http://www.thejoyofcode.com/System.Diagnostics_hidden_SourceLevels.aspx|The Joy of Code: System.Diagnostics Hidden SourceLevels]]
* [[http://msdn.microsoft.com/en-us/library/aa345169.aspx|MSDN: PresentationTraceSources Class]]
* [[http://msdn.microsoft.com/en-us/library/system.diagnostics.presentationtracesources.settracelevel.aspx|MSDN: SetTraceLevels]]
* [[http://msdn.microsoft.com/en-us/library/6w20x90k.aspx|MSDN: EventLog.WriteEntry method]]
* [[http://stackoverflow.com/questions/4026543/is-there-a-good-tool-for-debugging-xamls-databinding-behavior-errors-at-runtim|Tools for Debugging Data Binding at runtime]]
* [[http://bea.stollnitz.com/blog/?p=52|Bea Stollnitz: How can I debug WPF bindings?]]
* [[https://spin.atomicobject.com/2013/12/11/wpf-data-binding-debug/|How To Debug Data Binding Issues in WPF]]
= See Also =
* [[http://msdn2.microsoft.com/en-us/library/ms752347(VS.85).aspx|MSDN Data Binding Overview]]
* [[http://msdn.microsoft.com/en-us/library/ms752039.aspx|MSDN Data Binding How-to Topics]]
* [[http://msdn.microsoft.com/en-us/library/system.windows.data.objectdataprovider.aspx|MSDN ObjectDataProvider Class]]
* [[http://msdn.microsoft.com/en-us/library/system.windows.data.xmldataprovider.aspx|MSDN XmlDataProvider Class]]
* [[http://msdn.microsoft.com/en-us/library/aa480224.aspx|MSDN WPF Data Binding (Part 1)]]
* [[http://www.wpfdude.com/articles/BindingToElement.aspx|WPF Basics - Binding to a Property of Another Element]]
* [[http://blogs.msdn.com/b/wpfsdk/archive/2006/10/19/wpf-basic-data-binding-faq.aspx|WPF Basic Data Binding]]
* [[http://bea.stollnitz.com/blog/?p=22|Why Should I Use ObjectDataProvider?]]
* [[http://bea.stollnitz.com/blog/?p=5|Binding and DataTemplates]]
* [[http://www.codeproject.com/KB/WPF/GuidedTourWPF_3.aspx|A Guided Tour of WPF - (Part 3) Data Binding]]
* [[http://www.devx.com/codemag/Article/40833|DevX: Flexible and Powerful Data Binding in WPF (Part 2)]]
* [[http://odetocode.com/Articles/740.aspx|OdeToCode: Databinding in Silverlight]]
* [[http://joshsmithonwpf.wordpress.com/2007/06/04/binding-to-xml|John Smith on WPF: Binding to XML]]
* ObservableCollection and Item PropertyChanged
* [[http://stackoverflow.com/questions/901921/observablecollection-and-item-propertychanged|StackOverflow: ObservableCollection and Item PropertyChanged]]
* [[http://stackoverflow.com/questions/1003344/observablecollection-propertychanged-event|StackOverflow: ObservableCollection PropertyChanged event]]
* [[http://forums.silverlight.net/forums/p/221473/530822.aspx#530822|Implement INotifyCollectionChanged with MVVM]]