WPF : Binding to individual collection items (but not in a ItemsControl)

Well I had a great day yesterday, and quite a surprising one, I had an issue with an idea I was working on, so naturally I turned to Josh Smith.

And guess what I, yes me the mere mortal managed to teach Josh something about WPF…Ha Ha, check that.

So here is what I taught Josh…..(still cant believe I managed to teach Josh something 😉 )

I want to be able to bind multiple controls that are all stand alone controls to a collection. Sounds simple enough right.

Check this code behind

   1:  using System;
   2:  using System.Collections.Generic;
   3:  using System.Collections.ObjectModel;
   4:  using System.Linq;
   5:  using System.Text;
   6:  using System.Windows;
   7:  using System.Windows.Controls;
   8:  using System.Windows.Data;
   9:  using System.Windows.Documents;
  10:  using System.Windows.Input;
  11:  using System.Windows.Media;
  12:  using System.Windows.Media.Imaging;
  13:  using System.Windows.Navigation;
  14:  using System.Windows.Shapes;
  15:  
  16:  namespace WPF_test
  17:  {
  18:      public partial class Window1 : Window
  19:      {
  20:          public ObservableCollection<string>
  21:              MyValues { get; private set; }
  22:  
  23:          public Window1()
  24:          {
  25:              MyValues = new ObservableCollection<string>();
  26:              MyValues.Add("array");
  27:              MyValues.Add("element");
  28:              MyValues.Add("bindings");
  29:              //use the ObservableCollection<string> MyValues  as
  30:              //the DataContext for the Window
  31:              this.DataContext = MyValues;
  32:              InitializeComponent();
  33:          }
  34:  
  35:          private void Button_Click_0(
  36:              object sender, RoutedEventArgs e)
  37:          {
  38:              MyValues.Clear();
  39:              MyValues.Add("bindings");
  40:              MyValues.Add("element");
  41:              MyValues.Add("array");
  42:          }
  43:  
  44:          private void Button_Click_1(
  45:              object sender, RoutedEventArgs e)
  46:          {
  47:              MyValues.Clear();
  48:              MyValues.Add("element");
  49:              MyValues.Add("bindings");
  50:              MyValues.Add("array");
  51:          }
  52:  
  53:          private void Button_Click_2(
  54:              object sender, RoutedEventArgs e)
  55:          {
  56:              MyValues.Clear();
  57:              MyValues.Add("array");
  58:              MyValues.Add("bindings");
  59:              MyValues.Add("element");
  60:          }
  61:      }
  62:  }

.csharpcode, .csharpcode pre { font-size: small; color: black; font-family: consolas, “Courier New”, courier, monospace; background-color: #ffffff; /*white-space: pre;*/ } .csharpcode pre { margin: 0em; } .csharpcode .rem { color: #008000; } .csharpcode .kwrd { color: #0000ff; } .csharpcode .str { color: #006080; } .csharpcode .op { color: #0000c0; } .csharpcode .preproc { color: #cc6633; } .csharpcode .asp { background-color: #ffff00; } .csharpcode .html { color: #800000; } .csharpcode .attr { color: #ff0000; } .csharpcode .alt { background-color: #f4f4f4; width: 100%; margin: 0em; } .csharpcode .lnum { color: #606060; }And here is the XAML code. This is where the individual elements are bound to the MyValues collection. Lets see

   1:  <Window x:Class="WPF_test.Window1"
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      Title="Window1" Height="300" Width="300">
   5:  
   6:  
   7:      <Window.Resources>
   8:          <ContentPresenter x:Key="cont1" Content="{Binding Path=.[0], Mode=OneWay}"/>
   9:          <ContentPresenter x:Key="cont2" Content="{Binding Path=.[1], Mode=OneWay}"/>
  10:          <ContentPresenter x:Key="cont3" Content="{Binding Path=.[2], Mode=OneWay}"/>
  11:      </Window.Resources>
  12:  
  13:      <StackPanel Orientation="Vertical">
  14:          <Button Content="{DynamicResource cont1}" Click="Button_Click_0"/>
  15:          <Button Content="{DynamicResource cont2}" Click="Button_Click_1"/>
  16:          <Button Content="{DynamicResource cont3}" Click="Button_Click_2"/>
  17:      </StackPanel>
  18:  
  19:  
  20:  </Window>
And real guts of this post is the Content="{Binding Path=.[0], Mode=OneWay}" part, see how it uses the
. notation, which means current Binding, and the [0] indexer.
Neat huh. And here it is running

image


And here is a demo project : wpf_test.zip
Advertisements

25 Comments

  1. Hi Sacha!
    i like the ‘.’ notation, isn’t this just a shorthand way of specifying RelativeSource Self to get the context?

    Cheers!

    Rob
    (RobJH)

    Reply

  2. Not really, as the binding is the whole ObservableCollection. So we need to index a particular index into the ObservableCollection.

    Thats what this shows

    Reply

  3. How can I use a string key to get a value out of an observable collection. Particularly where the string key is the contents of another textbox>

    Reply

  4. Is the collection a dictionary, and if so you should simply be able to use the string as a index into the collection. If its not a IDictionary collection, good luck.

    But it sounds like you could use an attached property that you bind to the textbox current value (do this for all control you want to use this lookup on) and then when the property changes get the source within the DP and use that to lookup the value from the Dictionary that matches the DependencyObject whos attached property changed.

    Reply

  5. Hello,

    Could you please post some example with string indexer to collection element?

    To be something like this:
    Content=”{Binding Path=.[“key”], Mode=OneWay}”

    I have no luck since XAML syntax does not allow string with quotation within noncontent property.

    Reply

  6. My mistake 🙂

    You should simply use:
    Content=”{Binding Path=.[key], Mode=OneWay}”

    Reply

  7. Hi

    This article helped me a lot, thanks! But I have still some problems – I have a ListView with dynamicly generated Columns in GridView. Nr of this columns changes, so I can’t write a XAML code for CellTemplate like this:


    Instead, I’m generating them in C# code
    My question is how to replace [1],[2],[3], and so on indexers, to achieve the same results. I’ve tried many things, including multipleBinding and converters but still haven’t found good binding

    Reply

  8. I have never done this myself in code, but here are some ideas:

    You should be able to create the same in code using the PropertyPath class.

    Have a look at here http://www.codeproject.com/KB/WPF/codeVsXAML.aspx

    This may help you a bit more, Also read more about PropertyPath here

    http://msdn.microsoft.com/en-us/library/system.windows.propertypath.path.aspx and note this section under “Remarks”

    Items within collection properties are accessed with an indexer syntax, with the index within square brackets ([ and ]). The indexer is additive to the token representing the property. For example, the following is a two-step path, with the token combination in the first step specifying the second item from within the collection of that property: (0)[1].(1) . You cannot use an indexer on the last property in the chain; you cannot animate the actual collection position, you must animate a property on that object.

    So this implies that you should be using the

    public PropertyPath(
    string path,
    params Object[] pathParameters
    )

    constructor as talked about on http://msdn.microsoft.com/en-us/library/ms587924.aspx

    Thats the path I would pursue.

    Reply

  9. Bingo!!

    Thanks Sacha. This is what I was looking for and your trick did the work for me.

    Keep posting such tricks.

    At last, compliments for putting up such a nice bolg. I will try to visit this regularly.

    Reply

  10. Hi,

    what if I want to bind to an indexer somewhat like these

    Content=”{Binding Path=IndexedProperty.[“StringIndex”].SubProperty, Mode=OneWay}”

    Obviously, the code above doesnt work on my end. Any idea?

    Reply

  11. Cool. didn’t know about that.

    This is exactly what I need after spending 1/2 day on searching.

    Reply

  12. What would be the syntax if collection is not simply ObservableCollection, but ObservableCollection where myObject has multiple Dependency properties?

    Path=.[0].dpPropertyName — ?

    Thanks!

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s