none
Binding Dictionary Value to Dependency Property of a UserControl WPF RRS feed

  • Question

  • Hi,

    I'm trying to bind a Value from a Dictionary set as an itemsource of a tabcontrol to a DependencyProperty of a UserControl which is held within my TabControl.ContentTemplate.

    For the life of me I can't get it to bind, I have a strong feeling it may have someting to do with the DataContext of the userControl 'EnvironmentStateView' which in this instance is a viewmodel.

    In a separate TextBlock the binding to the dictionary key works for each item in the collection but unfortunately thats it.

    EnvironmentCollectionView.xaml

    <TabControl Name="EnvironmentCollectionTabControl" ItemsSource="{Binding environmentCollection}">
          <TabControl.ItemTemplate>
                <DataTemplate>
                    <TextBlock Text="{Binding Key}"></TextBlock>
                </DataTemplate>
          </TabControl.ItemTemplate>
          <TabControl.ContentTemplate>
              <DataTemplate>
                    <local:EnvironmentStateView Grid.Column="0" Grid.Row="0" EnvironmentObject="{Binding Value}" EnvironmentKey="{Binding Key}"></local:EnvironmentStateView>
              </DataTemplate>
          </TabControl.ContentTemplate>
    </TabControl>

    EnvironmentStateView.xaml

    /// <summary>
    /// Interaction logic for EnvironmentStateView.xaml
    /// </summary>
    [Export(typeof(EnvironmentStateView))]
    public partial class EnvironmentStateView : UserControl
    {
        /// <summary>
        /// Environment object dependency property
        /// </summary>
        public static readonly DependencyProperty EnvironmentObjectProperty =
            DependencyProperty.Register(
                "EnvironmentObject",
                typeof(Framework.Environment),
                typeof(EnvironmentStateView),
                new PropertyMetadata(null));

        /// <summary>
        /// Environment key dependency property
        /// </summary>
        public static readonly DependencyProperty EnvironmentKeyProperty =
            DependencyProperty.Register(
                "EnvironmentKey",
                typeof(string),
                typeof(EnvironmentStateView),
                new PropertyMetadata(null));

        [ImportingConstructor]
        public EnvironmentStateView()
        {
            // Set data context
            this.DataContext = new EnvironmentStateViewModel();
            // Initialise
            InitializeComponent();
        }
        /// <summary>
        /// Gets or sets the environment object
        /// </summary>
        public Framework.Environment EnvironmentObject
        {
            get { return (Framework.Environment)GetValue(EnvironmentObjectProperty); }
            set { SetValue(EnvironmentObjectProperty, value); }
        }
        /// <summary>
        /// Gets or sets the environment key
        /// </summary>
        public string EnvironmentKey
        {
            get { return (string)GetValue(
    EnvironmentKeyProperty); }
            set { SetValue(
    EnvironmentKeyProperty, value); }
        }
    }

    The DataContext for the environmentstateview is set to a view model, though removing this means it datacontext is null so I guess it is not inheriting anything to begin with.

    My goal was to have the environmentCollectionView use an environmentCollection Observable dictionary in its viewmodel, bind it as an itemsource to a tabctronol and then pass on the individual environment objects within the collection to separate usercontrols to handle specific views / visuals associated with the environment objects themselves.

    The only way i thought i could do this was to pass the dictionary Value as a dependency property so each usercontrol can do its thing :), Granted this might be a terrible way to do so.

    Some binding errors that appear in the output

    System.Windows.Data Error: 40 : BindingExpression path error: 'Key' property not found on 'object' ''EnvironmentStateViewModel' (HashCode=63276897)'. BindingExpression:Path=Key; DataItem='EnvironmentStateViewModel' (HashCode=63276897); target element is 'EnvironmentStateView' (Name=''); target property is 'EnvironmentKey' (type 'String')

    System.Windows.Data Error: 40 : BindingExpression path error: 'Value' property not found on 'object' ''EnvironmentStateViewModel' (HashCode=32159097)'. BindingExpression:Path=Value; DataItem='EnvironmentStateViewModel' (HashCode=32159097); target element is 'EnvironmentStateView' (Name=''); target property is 'EnvironmentObject' (type 'Environment')

    Now Key and Value are obviously not in the EnvironmentStateViewModel which i guess is the current data context.

    I intentionally misspelled the EnvironmentTagTextBlock binding to Keyy which gave me the binding error:

    System.Windows.Data Error: 40 : BindingExpression path error: 'Keyy' property not found on 'object' ''KeyValuePair`2' (HashCode=-459631492)'. BindingExpression:Path=Keyy; DataItem='KeyValuePair`2' (HashCode=-459631492); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')

    So my guess is that i need to somehow change my DataItem from EnvironmentStateViewModel to KeyValuePair`2 perhaps but I have no idea how or whether I probably should lol.

    Any help is appreciated

    Regards Wolfe


    • Edited by CmdrWolfe Tuesday, August 21, 2012 9:52 AM
    Monday, August 20, 2012 11:24 PM

Answers

  • Actually, as you replied, I was just re-doing it, so grab another copy if you got it before THIS post.

    I figured what you was trying to do with DependencyProperties and ViewModel was fair enough too, so I recreated what I think YOU want it to be. Either way, it shows several ways to bind and consume now :)

    Best regards,
    Pete


    #PEJL


    Wednesday, August 22, 2012 9:01 PM
    Moderator

All replies

  • Hi Wolfe

    I fear this may have been a cut and paste error.

    I think you copied the getter and setter for EnvironmentObject into EnvironmentKey, but forgot to change the GetValue & SetValue:

        public string EnvironmentKey 
        {
            get { return (string)GetValue(EnvironmentKeyProperty); }
            set { SetValue(EnvironmentKeyProperty, value); }
        }
     

    In Visual Studio, you can type "propdp" to get the snippet for DependencyProperty, it's quick and fail-safe.

    Regards,
    Pete


    #PEJL



    Monday, August 20, 2012 11:46 PM
    Moderator
  • Ahh, Thanks.

    Fixed the mistake.

    Haven't heard of these snippets before seem to be a good idea :).

    Binding still doesn't work though :(

    Regards Wolfe

    Tuesday, August 21, 2012 9:57 AM
  • I think you need the key contents in the binding.

    So here:

    <TextBlock Text="{Binding [Test].OPCTag}" />
    

    The Key is "Test".

    And I've used Dr WPF's code here previously:

    http://drwpf.com/blog/2007/09/16/can-i-bind-my-itemscontrol-to-a-dictionary/

    Tuesday, August 21, 2012 10:49 AM
    Moderator
  • Thanks,

    Hmmm, I'm not so sure this is the case. Changing it to [] throws up a binding error in output:

    '[]' property not found on 'object' ''EnvironmentStateViewModel'

    Which makes sense if this is a datacontext problem and it is looking in the wrong place. Also the keys are dynamic so currently it is not possible to add to the XAML.

    Also in Dr WPFs code he uses the same 'Key' and 'Value' bindings the only differance is that mine are in a usercontrol and bound to a propertydependency.

    I did try another 'Bodge' in code by changing it to:

    EnvironmentObject="{Binding Value, ElementName=EnvironmentCollectionTabControl, Path=Items.CurrentItem}"

    this gives a binding error in output:

    System.Windows.Data Error: 23 : Cannot convert '[302fc60032, SoftMod.Framework.Environment]' from type 'KeyValuePair`2' to type 'SoftMod.Framework.Environment' for 'en-US' culture with default conversions; consider using Converter property of Binding. NotSupportedException:'System.NotSupportedException: CollectionConverter cannot convert from System.Collections.Generic.KeyValuePair`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[SoftMod.Framework.Environment, SoftMod.Framework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].
       at System.ComponentModel.TypeConverter.GetConvertFromException(Object value)
       at System.ComponentModel.TypeConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, Object value)
       at MS.Internal.Data.DefaultValueConverter.ConvertHelper(Object o, Type destinationType, DependencyObject targetElement, CultureInfo culture, Boolean isForward)'
    System.Windows.Data Error: 6 : 'ObjectSourceConverter' converter failed to convert value '[302fc60032, SoftMod.Framework.Environment]' (type 'KeyValuePair`2'); fallback value will be used, if available. BindingExpression:Path=Items.CurrentItem; DataItem='TabControl' (Name='EnvironmentCollectionTabControl'); target element is 'EnvironmentStateView' (Name=''); target property is 'EnvironmentObject' (type 'Environment') NotSupportedException:'System.NotSupportedException: CollectionConverter cannot convert from System.Collections.Generic.KeyValuePair`2[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[SoftMod.Framework.Environment, SoftMod.Framework, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]].
       at MS.Internal.Data.DefaultValueConverter.ConvertHelper(Object o, Type destinationType, DependencyObject targetElement, CultureInfo culture, Boolean isForward)
       at MS.Internal.Data.ObjectSourceConverter.Convert(Object o, Type type, Object parameter, CultureInfo culture)
       at System.Windows.Data.BindingExpression.ConvertHelper(IValueConverter converter, Object value, Type targetType, Object parameter, CultureInfo culture)'

    So close to getting the KeyValuePair as a dataitem yet so far :D.

    Regards Wolfe

    Tuesday, August 21, 2012 11:13 AM
  • Anyone have any ideas?

    I've been exploring tryin to bind the selected item to a property though currently with little success :S

    Regards Wolfe

    Wednesday, August 22, 2012 6:17 PM
  • Hi Wolfe,

    I've found some time to rebuild your scenario from the snippets you provided, and was instantly able to reproduce your error messages.

    When you change the DataContext of the EnvironmentStateView, you lose it's original DataContext (the item being passed in). Remove that, and your properties will bind fine, because the DataContext of the Sub-View is ALREADY the Key/Value pair you wanted.

    Furthermore, without knowing more of your implementation, I am not sure you need to create the dependency properties anyway. You know it is an "Environment" class that is coming in, and you know it's from a "Value", so you can just bind to that and cast back for further code manipulation.

    Rather than pasting here, I dumped my copy onto MSDN Samples, as an example of TabControl TabItem generation. I have stripped it down to what I think it should be, with some dummy data and a few controls to make the TabItem Content that consumes the data.

    http://code.msdn.microsoft.com/TabItems-From-Dictionary-22fb37c5

    Try that and let me know if that solves your initial question.

    If not, we have a reproducable example to work with.

    Either way, please rate it if you like it ;)

     

    Kind regards,
    Pete


    #PEJL



    Wednesday, August 22, 2012 7:38 PM
    Moderator
  • Cheers GUY of XAML :).

    I'll load it up and check it out, fingers crossed.

    Regards WOlfe

    Wednesday, August 22, 2012 8:29 PM
  • Actually, as you replied, I was just re-doing it, so grab another copy if you got it before THIS post.

    I figured what you was trying to do with DependencyProperties and ViewModel was fair enough too, so I recreated what I think YOU want it to be. Either way, it shows several ways to bind and consume now :)

    Best regards,
    Pete


    #PEJL


    Wednesday, August 22, 2012 9:01 PM
    Moderator
  • First off a very big thank you, I've spent short of a week foraging through the internet with little success, though I did learn a few things on the way. I would still be lost though with out your help.

    I have looked at both and implemented the dependency version into my framework and so far so good.

    I made a cross post on stackoverflow and I would like to incorporate your answer over there for others to use, it will point to this post aswell though, is that ok?

    Once again thanks and take care.

    Regards Wolfe

    Wednesday, August 22, 2012 11:19 PM
  • Glad to have helped.

    All I ask is that you rate (star) the MSDN Sample.

    And in this forum thread, mark as answers and/or helpful any posts that helped.

     

    Very good luck with your project.

     

    Best regards,
    Pete


    #PEJL

    Wednesday, August 22, 2012 11:59 PM
    Moderator
  • Some updates,

    I added another usercontrol EnvironmentDataView to the EnvironmentStateView tabcontrol. So basically there is a EnvironmentCollecitonView which holds a tabcontrol bound through itemsource to an EnvironmentCollection ObsDictionary.

    This loads up individual EnvironmentStateViews for each environment in  the collection.

    And each stateview contains a TabControl which loads up EnvironmentDataView to focus upon certain data within the environment object

    Two things arose as a result:

    Maintaing the DataContext can be a B%$£!.

    Originally I wanted to handle eveyrthing through ViewModels assocaited with each view, in this case State and Data.Through a dependency property I could than keep a copy of the environment during the creation of each view and work of that.

            /// <summary>
            /// Loaded handler for environment state view
            /// </summary>
            void EnvironmentStateView_Loaded(object sender, RoutedEventArgs e)
            {
                // Leaving the UserControl DataContext as original bound data, so DependencyProperties are populated, set view model data context to grid
                EnvironmentStateGrid.DataContext = new EnvironmentStateViewModel(EnvironmentObject);
            }

    Using the load handler from XAMLGUYS cool code helped in setting the environment object in the view model however I didn't realise that it would then propogate the data context to all children, in this case EnvironmentDataView loaded in the StateView tabcontrol.

    This can be overcome by simply applying the above again in the EnvironmentDataView as below:

      /// <summary>
            /// Loaded handler for environment data view
            /// </summary>
            void EnvironmentDataView_Loaded(object sender, RoutedEventArgs e)
            {
                // Leaving the UserControl DataContext as original bound data, so DependencyProperties are populated, set view model data context to grid
                EnvironmentDataGrid.DataContext = new EnvironmentDataViewModel(EnvironmentObject);
            }

    Though it is funny to watch the EnvironmentDataVIew DataContext do the funky chicken and screw with the bindings as it changes from the original itemsource 'KeyValuePair2' to EnvironmentStateViewModel and than finally back to EnvironmentDataViewModel :D.

    This seemed to work as every time you click off and back on again to the EnvData tab is called the EnvironmentDataView_Loaded handler which reset to view model and as long is the bindings were set to the a local environment object in the view model it was fine.

    Except,...... dun dun duuun.

    It seemed that when i went up a level so to speak and began changing from the higher EnvironmentCollectionView Tabcontrol through each environment the environment data was not filtering back through all the way down to the lower EnvironmentDataView.

    Why?

    Well I probably should have bothered to look to bind to a local environment object in the first place and focused on binding to the KeyValuePair2 datacontext. This way every time this is changed as you click through tabcontrol it seems to filter all the way through to anything bound to it, understandibly :).

    This piece of text is probably not worth much on its own but who knows.

    Regards Wolfe

    Thursday, August 23, 2012 11:29 PM
  • Hi again big bad wolfe

    What you''re doing doesn't sound quite right.

    To keep the question title targetted for future hunters and 'binding adventurers' like yourself, would you mind starting a new thread?

    And ideally, if you could recreate the problem from a copy of that msdn sample project, I (and I'm sure others) would be happy to have a look.

     

    Regards,
    Pete


    #PEJL

    Friday, August 24, 2012 12:19 AM
    Moderator