This is an old revision of the document!


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:

<UserControl...
   xmlns:viewmodel="clr-namespace:Acme.App.ViewModel;assembly=Acme.App.ViewModel"
   xmlns:local="clr-namespace:Acme.App.Types;assembly=Acme.App.Types"
   ...>
 
<UserControl.Resources>
  <viewmodel:TInstrumentSideDetectedToVisibilityConverter x:Key="InstrumentSideDetectedToVisibilityConverter"/>
</UserControl.Resources>
. . .
<ToggleButton 
  Visibility="{Binding Path=Fitting.DetectedSide, 
    Converter={StaticResource InstrumentSideDetectedToVisibilityConverter}, 
    ConverterParameter={x:Static local:TSide.Right}}"
  Content="An Environment">
. . .
</UserControl>  

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):

///============================================================================================
/// <summary>
/// Class TInstrumentSideDetectedToVisibilityConverter
/// </summary>
///============================================================================================
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 Binding.Source): Example:
    <Window
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:src="clr-namespace:SDKSample"
      SizeToContent="WidthAndHeight"
      Title="Simple Data Binding Sample">
     
      <Window.Resources>
        <!-- Instantiate an object -->
        <src:Person x:Key="myDataSource" PersonName="Joe"/>
        ...
      </Window.Resources>
      ...
      <TextBlock Text="{Binding Source={StaticResource myDataSource}, Path=PersonName}"/>
    </Window>

    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 Binding.RelativeSource): Example:
    <Style x:Key="textBoxInError" TargetType="{x:Type TextBox}">
      <Style.Triggers>
        <Trigger Property="Validation.HasError" Value="true">
          <Setter Property="ToolTip"
            Value="{Binding RelativeSource={x:Static RelativeSource.Self},
                            Path=(Validation.Errors)[0].ErrorContent}"/>
        </Trigger>
      </Style.Triggers>
    </Style>

    Using RelativeSource to find Ancestor:

    <DockPanel Background='Blue'>
       <DockPanel Background='Green'>
           <TextBox Margin='20'
             DockPanel.Dock="Top"
             Background='{Binding Background ,
             RelativeSource={RelativeSource FindAncestor,
             AncestorType=DockPanel ,
             AncestorLevel=2}}' />
       </DockPanel>
    </DockPanel>

    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 Binding.ElementName): Example:
    <ComboBox Name="myComboBox" SelectedIndex="0">
        <ComboBoxItem>Green</ComboBoxItem>
        <ComboBoxItem>Blue</ComboBoxItem>
        <ComboBoxItem>Red</ComboBoxItem>
    </ComboBox>
    <Canvas>
      <Canvas.Background>
        <Binding ElementName="myComboBox" Path="SelectedItem.Content"/>
      </Canvas.Background>
    </Canvas>

    Or an example using a Slider:

    <Slider Name="Slider1" />
    <TextBox Name="TextBox1" Text="{Binding ElementName=Slider1, Path=Value}" />

    Alternatively:

    <Slider Name="Slider1" />
    <TextBox Name="TextBox1">
        <TextBox.Text>
            <Binding ElementName="Slider1" Path="Value" />
        </TextBox.Text>
    </TextBox>

    C# examples:

    // Examples 
    Binding my_binding1 = new Binding(); my_binding1.ElementName = "btnStart1";
    Binding my_binding2 = new Binding("Value"); my_binding2.ElementName = "Slider1";

References: What is the Difference Between ''Source'' and ''RelativeSource''

DataContext

To bind to the default DataContext of an object (or parent), use:

<ContentControl Name="ccDetails" Content="{Binding}" />

Programmatically in C#:

ccDetails.SetBinding(ContentControl.ContentProperty, new Binding());

If you need to include the path, then use:

<ContentControl Name="ccDetails" Content="{Binding Path=Person.Name}" />
ccDetails.SetBinding(ContentControl.ContentProperty, new Binding("Person.Name"));

If the path contains an index, then use:

<ContentControl Name="ccDetails" Content="{Binding Path=Person.Address[0]}" />    <!-- numeric-based index -->
<ContentControl Name="ccDetails" Content="{Binding Path=Person.Address[Home]}" /> <!-- key index -->
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:

<UserControl xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
             xmlns:sys="clr-namespace:System;assembly=mscorlib">
  <UserControl.Resources>
    <sys:Int32 x:Key="intAge">42</sys:Int32>
    <sys:String x:Key="strYes">Oui</sys:String>
    ...
    <sys:Object .../>
    <sys:Boolean .../>
    <sys:Char .../>
    <sys:String.../>
    <sys:Decimal .../>
    <sys:Single .../>
    <sys:Double .../>
    <sys:Int16 .../>
    <sys:Int32 .../>
    <sys:Int64 .../>                    
    <sys:TimeSpan .../>
    <sys:Uri .../>
    <sys:Byte .../>
    <sys:Array .../>
  </UserControl.Resources>
</UserControl>

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.

<UserControl x:Class="Acme.Products.Specifications.ProductSpecsControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Acme.Products.Specifications
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="600">
  <UserControl.Resources>
      <ObjectDataProvider x:Key="objDataProviderProductSpecs" ObjectType="{x:Type local:TProductSpecificationList}" />
  </UserControl.Resources>
....
</UserControl>

Alternatively, as an XAML element:

  <UserControl.Resources>
    <local:TProductSpecificationList x:Key="objDataProviderProductSpecs" />  
  </UserControl.Resources>  

Declaring a data source with parameters vs no parameters:

<!--Data Source-->
<!--Creates a simple data source object (but requires parameterless constructor)-->
<!--This Method creates the object, then later it assigns the field (ie. field is not assign during construction)-->
<prodspecs:TProductSpecificationList x:Key="ProdListDataSource" CustomCompany="Acme" />
 
<!--Creates data source object, and allow passing data into a constructor -->
<ObjectDataProvider x:Key="ProdListDataSource" ObjectType="{x:Type prodspecs:TProductSpecificationList}">
    <ObjectDataProvider.ConstructorParameters>
        <custom:TCustomCompany>Acme</custom:TCustomCompany>
    </ObjectDataProvider.ConstructorParameters>
</ObjectDataProvider>

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<string>
  {
    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:

  <ListBox ItemsSource="{Binding Source={StaticResource objDataProviderProductSpecs}}"/>

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.

<UserControl x:Class="Acme.Products.Specifications.ProductSpecsControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="600">
<UserControl.Resources>
    <!--<XmlDataProvider x:Key="dataProviderProductSpecs" XPath="">-->
    <XmlDataProvider x:Key="dataProviderProductSpecs" XPath="ProductSpecs">
        <x:XData>
            <ProductSpecs xmlns="">
                <ProductSpec Type="prod_Bicycle">
                    <Name>Bicycle</Name>
                    <Wheels>2</Wheels>
                    <Color>Red</Color>
                </ProductSpec>
                <ProductSpec Type="prod_Tricycle">
                    <Name>Tricycle</Name>
                    <Wheels>3</Wheels>
                    <Color>Blue</Color>
                </ProductSpec>
            </ProductSpecs>
        </x:XData>
    </XmlDataProvider>
</UserControl.Resources>
....
</UserControl>

Alternatively, put the data in an XML file.

<ProductSpecs xmlns="">
...
</ProductSpecs>

Call the data locally:

<XmlDataProvider x:Key="dataProviderProductSpecs" Source="data\prodspecs.xml" XPath="ProductSpecs"/>

Remotely:

<XmlDataProvider x:Key="dataProviderProductSpecs" Source="http://server/prodspecs.xml" XPath="ProductSpecs"/>

Perform data binding in one of these 2 ways:

<Grid>
   <!--Method 1-->
   <ListBox ItemsSource="{Binding Source={StaticResource dataProviderProductSpecs}, XPath=ProductSpec/Name}"/>
   <!--Method 2-->
   <ListBox >
      <ListBox.ItemsSource>
          <Binding Source="{StaticResource dataProviderProductSpecs}" XPath="ProductSpec/Name"></Binding>
      </ListBox.ItemsSource>
   </ListBox>
</Grid>
Examples
Syntax

Declaring an ObjectDataProvider:

<ObjectDataProvider x:Key="ObjDataProviderNAMEHERE" ObjectType="{x:Type local:ClassNAMEHERE}" />

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
<UserControl.Resources>
    <ObjectDataProvider x:Key="objDataProviderTProductSpecificationList" ObjectType="{x:Type local:TProductSpecificationList}" />
...        
</UserControl.Resources>
<Grid>
  <DataGrid ItemsSource="{Binding Source={StaticResource objDataProviderTProductSpecificationList}}" Margin="5" AutoGenerateColumns="False">
      <DataGrid.Columns>
          <DataGridTextColumn Binding="{Binding Type}" Header="Type" />
          <DataGridTextColumn Binding="{Binding Name}" Header="Name" />
          <DataGridTextColumn Binding="{Binding Wheels}" Header="Wheels" />
          <DataGridTextColumn Binding="{Binding Color}" Header="Color" />
      </DataGrid.Columns>
  </DataGrid>
</Grid>
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<TProductSpecification>
    {
        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
<Page
  xmlns:sys='clr-namespace:System;assembly=mscorlib'
  xmlns:local='clr-namespace:MyLib' />
  ...
<Page.Resources>
  <!-- Instantiate a string
       and load into resource dictionary -->
  <sys:String x:Key='sample'>ABC 123 DEF 456
  </sys:String>
  <!-- Instantiate an employee
       and load into resource dictionary -->
  <local:Employee x:Key='emp'
    FirstName='Jimmy'
    LastName='Crickett'
    Salary='2500'
  />
</Page.Resources>
...
   <!-- XAML Binding to string array -->
   <TextBlock Text="{Binding Source= {StaticResource sample}}" />
   <TextBlock Text="{Binding Source= {StaticResource sample},Path=[2]}" />
...
   <!-- XAML Binding to Employee instance -->
   <TextBlock Text="{Binding Source={StaticResource emp},Path=LastName}" />

Source: 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: 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:

<Window x:Class="SOTCBindingValidation.Window1" x:Name="This"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:local="clr-namespace:SOTCBindingValidation"
   Title="SOTC Validation Test" Height="150" Width="400">
  <Window.Resources>
    <local:ErrorsToMessageConverter x:Key="eToMConverter" />
  </Window.Resources>
  <StackPanel Margin="5">
    <TextBlock Margin="2">Enter An IPv4 Address:</TextBlock>
    <TextBox x:Name="AddressBox">
      <TextBox.Text>
        <Binding ElementName="This" Path="IPAddress"
                UpdateSourceTrigger="PropertyChanged">
          <Binding.ValidationRules>
            <local:IPv4ValidationRule />
          </Binding.ValidationRules>
        </Binding>
      </TextBox.Text>
    </TextBox>
    <TextBlock Margin="2" Foreground="Red" FontWeight="Bold"
              Text="{Binding ElementName=AddressBox,
                             Path=(Validation.Errors),
                             Converter={StaticResource eToMConverter}}" />
  </StackPanel>
</Window>

Source: WPF - Binding Validation Rules

Connecting to an XML File

XML File:

<?xml version="1.0"?>
<Workshops xmlns="">
  <Workshop>
    <Name>Acme's 12th Annual Continuing Education Workshop</Name>
    <RecID>12</RecID>
    <WorkshopID>AcmeWorkshop12</WorkshopID>
    <VenueInfo>
      <Organization>Orlando Marriott Lake Mary</Organization>
      <AddressLn1>1501 International Parkway</AddressLn1>
      <City>Lake Mary</City>
      <StateProv>FL</StateProv>
      <PostalCode>32746</PostalCode>
      <PhoneHome>407.995.1100</PhoneHome>
      <PhoneWork>800.380.7724</PhoneWork>
      <Fax>407.995.1101</Fax>
      <Website>www.marriotthotels.com</Website>
    </VenueInfo>
    <Days>
      <DateTime>2011/01/14</DateTime>
      <DateTime>2011/01/15</DateTime>
    </Days>
    <PricingDailyPackages>
      <PricingPackage>
        <Name>Premium</Name>
        <Pricing>100</Pricing>
      </PricingPackage>
      <PricingPackage>
        <Name>Standard</Name>
        <Pricing>200</Pricing>
      </PricingPackage>
      <PricingPackage>
        <Name>Other</Name>
        <Pricing>200</Pricing>
      </PricingPackage>
    </PricingDailyPackages>
  </Workshop>
  <Workshop>
    <Name>Acme's 13th Annual Continuing Education Workshop</Name>
    <RecID>13</RecID>
    <WorkshopID>AcmeWorkshop13</WorkshopID>
    <VenueInfo>
      <Organization>Orlando Marriott Lake Mary</Organization>
      <AddressLn1>1501 International Parkway</AddressLn1>
      <City>Lake Mary</City>
      <StateProv>FL</StateProv>
      <PostalCode>32746</PostalCode>
      <PhoneHome>407.995.1100</PhoneHome>
      <PhoneWork>800.380.7724</PhoneWork>
      <Fax>407.995.1101</Fax>
      <Website>www.marriotthotels.com</Website>
    </VenueInfo>
    <Days>
      <DateTime>2012/01/24</DateTime>
      <DateTime>2012/01/25</DateTime>
    </Days>
    <PricingDailyPackages>
      <PricingPackage>
        <Name>Premium</Name>
        <Pricing>100</Pricing>
      </PricingPackage>
      <PricingPackage>
        <Name>Standard</Name>
        <Pricing>200</Pricing>
      </PricingPackage>
      <PricingPackage>
        <Name>Other</Name>
        <Pricing>200</Pricing>
      </PricingPackage>
    </PricingDailyPackages>
  </Workshop>
</Workshops>

Data binding for a DataGrid:

<UserControl ...>
<UserControl.Resources>
    <XmlDataProvider x:Key="dataProviderWorkshopList" Source="Data\Workshops.xml" XPath="Workshops/Workshop"/>
</UserControl.Resources>
...
<DataGrid Name="datagridWorkshopList" 
		  ItemsSource="{Binding Source={StaticResource dataProviderWorkshopList}}"
		  CanUserAddRows="False" 
		  IsReadOnly="True" 
		  FrozenColumnCount="1"  
		  IsSynchronizedWithCurrentItem="True"
		  AlternatingRowBackground="AliceBlue" 
		  AlternationCount="2" 
		  Margin="5" 
		  AutoGenerateColumns="False" >
	<DataGrid.Columns>
		<DataGridTemplateColumn Header="Name"  Width="120">
			<DataGridTemplateColumn.CellTemplate>
				<DataTemplate>
					<Button Content="{Binding XPath=WorkshopID}" />
				</DataTemplate>
			</DataGridTemplateColumn.CellTemplate>
			<DataGridTemplateColumn.CellEditingTemplate>
				<DataTemplate>
					<TextBox Text="{Binding Path=Name, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
				</DataTemplate>
			</DataGridTemplateColumn.CellEditingTemplate>
		</DataGridTemplateColumn>
		<DataGridTextColumn Binding="{Binding XPath=Name}" Header="Name" />
		<DataGridTextColumn Binding="{Binding XPath=RecID}" Header="Rec ID" />
		<DataGridTextColumn Binding="{Binding XPath=WorkshopID}" Header="Workshop ID" />
		<DataGridTextColumn Binding="{Binding XPath=VenueInfo/Organization}" Header="Venue" Width="100"/>
		<DataGridTextColumn Binding="{Binding XPath=VenueInfo/AddressLn1}" Header="Address Line 1" />
		<DataGridTextColumn Binding="{Binding XPath=VenueInfo/AddressLn2}" Header="Address Line 2" />
		<DataGridTextColumn Binding="{Binding XPath=VenueInfo/AddressLn3}" Header="Address Line 3" />
		<DataGridTextColumn Binding="{Binding XPath=VenueInfo/City}" Header="City" />
		<DataGridTextColumn Binding="{Binding XPath=VenueInfo/StateProv}" Header="State" />
		<DataGridTextColumn Binding="{Binding XPath=VenueInfo/PostalCode}" Header="ZIP Code" />
		<DataGridTextColumn Binding="{Binding XPath=VenueInfo/Website}" Header="Website" />
	</DataGrid.Columns>
</DataGrid>
...
</UserControl>

Data binding for a ListBox:

<UserControl ...>
<UserControl.Resources>
    <XmlDataProvider x:Key="dataProviderWorkshopList" Source="Data\Workshops.xml" XPath="Workshops/Workshop"/>
</UserControl.Resources>
...
<TextBlock>Workshops</TextBlock>
	<!--<ListBox Name="lstWorkshops" ItemsSource="{Binding Source={StaticResource dataProviderWorkshopList}}" IsSynchronizedWithCurrentItem="True">-->
	<ListBox Name="lstWorkshops" IsSynchronizedWithCurrentItem="True">
		<ListBox.ItemsSource>
			<Binding Source="{StaticResource dataProviderWorkshopList}" />
		</ListBox.ItemsSource>
		<ListBox.ItemTemplate>
			<DataTemplate>
				<StackPanel Orientation="Horizontal">
					<TextBlock Text="{Binding XPath=WorkshopID}" Margin="0,0,5,0" />
					<TextBlock>|</TextBlock>
					<TextBlock Text="{Binding XPath=RecID}" Margin="0,0,5,0" />
					<TextBlock>|</TextBlock>
					<TextBlock Text="{Binding XPath=Name}" />
				</StackPanel>
			</DataTemplate>
		</ListBox.ItemTemplate>
	</ListBox>
</StackPanel>
...
</UserControl>
Connecting to a ''DataTable''
<UserControl.Resources>
    <ObjectDataProvider x:Key="objDataProvProdSpecDataTable" ObjectType="{x:Type local:ProductSpecsControlClass}" />
...        
</UserControl.Resources>
<Grid>
  <DataGrid ItemsSource="{Binding Source={StaticResource objDataProvProdSpecDataTable}, Path=ProdSpecDataTable}" Margin="5" AutoGenerateColumns="False">
      <DataGrid.Columns>
          <DataGridTextColumn Binding="{Binding Type}" Header="Type" />
          <DataGridTextColumn Binding="{Binding Name}" Header="Name" />
          <DataGridTextColumn Binding="{Binding Wheels}" Header="Wheels" />
          <DataGridTextColumn Binding="{Binding Color}" Header="Color" />
      </DataGrid.Columns>
  </DataGrid>
</Grid>
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
{
    /// <summary>
    /// Interaction logic for ProductSpecsControl.xaml
    /// </summary>
    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
    /// <summary>
    /// Patient Information class
    /// </summary>
    public class TPatientInfo
    {
        public string FirstName
        { get; set; }
        public string LastName
        { get; set; }
        public int ID
        { get; set; }
    }
    #endregion
 
    #region Patient List using ObservableCollection
    /// <summary>
    /// Patient List using ObservableCollection<> (instead of List<>).
    /// This approach allows the ListBox to see any updates performed 
    /// on the actual data model.
    /// </summary>
    public class TPatientListOC: ObservableCollection<TPatientInfo>
    {
        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:

<UserControl x:Class="DataBinding.Patient.ucPatientInfo"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:patient="clr-namespace:DataBinding.Patient"
             mc:Ignorable="d"
              Loaded="UserControl_Loaded" 
             d:DesignHeight="300" d:DesignWidth="500">
    <UserControl.Resources>
        <patient:TPatientListOC x:Key="PatientListOCDS"/>
    </UserControl.Resources>
    <Grid>
        <StackPanel Orientation="Vertical">
            <!--Patient List-->
            <TextBlock FontWeight="Bold">Patient List</TextBlock>
            <StackPanel Orientation="Horizontal">
                <!--<ListBox ItemsSource="{Binding Source={StaticResource PatientListOCDS}}" >-->
                <ListBox Name="lstPatientList" IsSynchronizedWithCurrentItem="True">
                    <ListBox.ItemsSource>
                        <Binding Source="{StaticResource PatientListOCDS}" />
                    </ListBox.ItemsSource>
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="{Binding Path=LastName}" Margin="0,0,5,0" />
                                <TextBlock Text="{Binding Path=FirstName}" Margin="0,0,5,0" />
                                <TextBlock Text="{Binding Path=ID}"/>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
                <StackPanel Orientation="Horizontal">
                    <Button Name="btnLoadList" Width="100" Click="btnLoadList_Click">Load List</Button>
                    <Button Name="btnClearList" Width="100" Click="btnClearList_Click">Clear List</Button>
                    <Button Name="btnDeleteSelection" Width="100" Click="btnDeleteSelection_Click">Delete Selection</Button>
                </StackPanel>
            </StackPanel>
 
            <!--Record Details-->
            <StackPanel DataContext="{Binding Source={StaticResource PatientListOCDS}}">
                <TextBlock></TextBlock>
                <TextBlock FontWeight="Bold">Record Details</TextBlock>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Width="100">Last Name</TextBlock>
                    <TextBox Name="txtLastName" Width="300" Text="{Binding Path=LastName}"/>
                </StackPanel>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Width="100">First Name</TextBlock>
                    <TextBox Name="txtFirstName" Width="300" Text="{Binding Path=FirstName}"/>
                </StackPanel>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Width="100">ID</TextBlock>
                    <TextBox Name="txtID" Width="300" Text="{Binding Path=ID}"/>
                </StackPanel>
                <!--Buttons-->
                <StackPanel Orientation="Horizontal">
                    <Button Name="btnAdd" Width="100" Click="btnAdd_Click">Add</Button>
                </StackPanel>
            </StackPanel>
        </StackPanel>
    </Grid>
</UserControl>

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
{
    /// <summary>
    /// Interaction logic for ucDataBinding.xaml
    /// </summary>
    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:

<UserControl x:Class="DataBinding.Patient.ucPatientInfo"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:patient="clr-namespace:DataBinding.Patient"
             mc:Ignorable="d"
             Loaded="UserControl_Loaded" 
             d:DesignHeight="300" d:DesignWidth="500">
    <Grid>
        <StackPanel Orientation="Vertical">
            <!--Patient List-->
            <TextBlock FontWeight="Bold">Patient List</TextBlock>
            <StackPanel Orientation="Horizontal">
                <ListBox Name="lstPatientList" IsSynchronizedWithCurrentItem="True">
                    <ListBox.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <TextBlock Text="{Binding Path=LastName}" Margin="0,0,5,0" />
                                <TextBlock Text="{Binding Path=FirstName}" Margin="0,0,5,0" />
                                <TextBlock Text="{Binding Path=ID}"/>
                            </StackPanel>
                        </DataTemplate>
                    </ListBox.ItemTemplate>
                </ListBox>
                <StackPanel Orientation="Horizontal">
                    <Button Name="btnLoadList" Width="100" Click="btnLoadList_Click">Load List</Button>
                    <Button Name="btnClearList" Width="100" Click="btnClearList_Click">Clear List</Button>
                    <Button Name="btnDeleteSelection" Width="100" Click="btnDeleteSelection_Click">Delete Selection</Button>
                </StackPanel>
            </StackPanel>
 
            <!--Record Details-->
            <StackPanel>
                <TextBlock></TextBlock>
                <TextBlock FontWeight="Bold">Record Details</TextBlock>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Width="100">Last Name</TextBlock>
                    <TextBox Name="txtLastName" Width="300" Text="{Binding Path=LastName}"/>
                </StackPanel>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Width="100">First Name</TextBlock>
                    <TextBox Name="txtFirstName" Width="300" Text="{Binding Path=FirstName}"/>
                </StackPanel>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Width="100">ID</TextBlock>
                    <TextBox Name="txtID" Width="300" Text="{Binding Path=ID}"/>
                </StackPanel>
                <!--Buttons-->
                <StackPanel Orientation="Horizontal">
                    <Button Name="btnAdd" Width="100" Click="btnAdd_Click">Add</Button>
                </StackPanel>
            </StackPanel>
        </StackPanel>
    </Grid>
</UserControl>

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
{
    /// <summary>
    /// Interaction logic for ucDataBinding.xaml
    /// </summary>
    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:

<UserControl ...>
  <UserControl.Resources>
        <!--IValueConverter-->
        <local:TBoolToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
        ...
  </UserControl.Resources>
  <StackPanel Name="pnlBluetoothCompatible" 
          Orientation="Horizontal" 
          Visibility="{Binding Path=BluetoothCompatible, Converter={StaticResource BoolToVisibilityConverter}}">
      <TextBlock FontWeight="Bold" Width="120" HorizontalAlignment="Left">Bluetooth Support</TextBlock>
      <CheckBox IsChecked="{Binding Path=BluetoothCompatible}"  SourceUpdated="CheckBox_SourceUpdated"/>
  </StackPanel>
  ...
</UserControl>

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();
    }
}  
<UserControl ...>
  <UserControl.Resources>
        <!--IValueConverter-->
        <local:TImgPathConverter x:Key="ImgPathConverter"/>
        ...
  </UserControl.Resources>
  <StackPanel Orientation="Horizontal">
    <TextBlock FontWeight="Bold" Width="120" HorizontalAlignment="Left">Shell/Case View</TextBlock>
    <Image Source="{Binding Path=HousingFilename, Converter={StaticResource ImgPathConverter}}" Width="40" />
  </StackPanel>
  ...
</UserControl>

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:

<TextBlock Grid.Row="2" Grid.Column="0" Margin="0,0,8,0"
           Name="startDateTitle"
           Style="{StaticResource smallTitleStyle}">Start Date:</TextBlock>
<TextBlock Name="StartDateDTKey" Grid.Row="2" Grid.Column="1" 
    Text="{Binding Path=StartDate, Converter={StaticResource dateConverter}}" 
    Style="{StaticResource textStyleTextBlock}"/>

Source: MSDN: IValueConverter Interface

IValueConverter using Parameters

Example 1

XAML:

<UserControl.Resources>
      <local:StringFormatConverter x:Name="stringFormatter"/>
</UserControl.Resources>
<StackPanel Orientation="Vertical">
      <StackPanel.DataContext>
          <SilverlightApplication3:Person/>
      </StackPanel.DataContext>
      <TextBlock HorizontalAlignment="Left" VerticalAlignment="Top" Text="{Binding Mode=OneWay, Path=Name}" TextWrapping="Wrap"/>
      <TextBlock HorizontalAlignment="Left" VerticalAlignment="Top" Text="{Binding Mode=OneWay, Path=LastName}" TextWrapping="Wrap"/>
      <TextBlock HorizontalAlignment="Left" VerticalAlignment="Top" Text='{Binding Mode=OneWay, Path=DOB, Converter={StaticResource stringFormatter}, ConverterParameter="d"}' TextWrapping="Wrap"/>
      <TextBlock HorizontalAlignment="Left" VerticalAlignment="Top" Text='{Binding Mode=OneWay, Path=Age, Converter={StaticResource stringFormatter}, ConverterParameter="c"}' TextWrapping="Wrap"/>
      <TextBlock HorizontalAlignment="Left" VerticalAlignment="Top" Text='{Binding Mode=OneWay, Path=AnualIncome, Converter={StaticResource stringFormatter}, ConverterParameter="0.00"}' TextWrapping="Wrap"/>
</StackPanel>

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:

<ItemsControl ItemsSource="{Binding Converter={StaticResource PropertyViewer}}"/>

Customizing the PropertyViewer is also easy:

<ItemsControl ItemsSource="{Binding Converter={StaticResource PropertyViewer},
    ConverterParameter='Species EatsBugs RelativeMass'}">

And the C# code behind:

public class PropertyViewer : IValueConverter
{
    /// <summary>
    /// Implements IValueConverter.Convert.
    /// </summary>
    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<PropertyDetails>();
        // 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;
    }
 
    /// <summary>
    /// Implements IValueConverter.ConvertBack.
    /// </summary>
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException("PropertyViewer does not use ConvertBack.");
    }
 
    /// <summary>
    /// Gets the name of the specified property.
    /// </summary>
    /// <param name="propertyInfo">property</param>
    /// <returns>name</returns>
    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;
    }
 
    /// <summary>
    /// Gets the value of the specified property for an instance.
    /// </summary>
    /// <param name="propertyInfo">property</param>
    /// <param name="instance">instance</param>
    /// <returns>value</returns>
    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: IValueConverter: The Swiss Army Knife of Bindings...

Multi-Binding

To display 2 different variables in a same field (eg: 3.14 = May 5, 2012):

<TextBox>
  <TextBox.Text>
    <MultiBinding StringFormat="{}{0:F2} = {1:D}">
      <Binding Path="Double" />
      <Binding Path="Date"/>
    </MultiBinding>
  </TextBox.Text>
</TextBox>
Debugging
See Also