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 thoughts on “WPF : Binding to individual collection items (but not in a ItemsControl)

  1. Cornel says:

    I didn’t knew that too. How many other things I don’t know yet about WPF? 🙂

  2. sacha barber says:

    Yeah right. Hidden treasure that one

  3. Rob says:

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

    Cheers!

    Rob
    (RobJH)

  4. sacha says:

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

    Thats what this shows

  5. Molon Labe says:

    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>

  6. sacha says:

    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.

  7. kosa says:

    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.

  8. kosa says:

    My mistake 🙂

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

  9. sacha says:

    Kosa

    thats not possible as far as I know

  10. omeyer says:

    Just leave await the “”
    Content=”{Binding Path=.[key], Mode=OneWay}”

  11. bartek says:

    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

  12. sacha says:

    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.

  13. Krunal says:

    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.

  14. sacha says:

    Cool

  15. LeeW says:

    Good stuff! This got me out of a real fix. Thanks.

  16. sacha says:

    LeeW it’s all good.

  17. blackjack games says:

    Nice page. It’s good to have kids who can use this medium to find you

  18. 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?

  19. bing says:

    Cool. didn’t know about that.

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

  20. James says:

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

    Path=.[0].dpPropertyName — ?

    Thanks!

  21. James says:

    oops, some bracketed chars got chopped from my comment on submit, hope its not unclear.

  22. Wolfgang says:

    This one saved my life – thanks ‘n hugs!
    Kepp up posting!
    Regards

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s

%d bloggers like this: