none
The way to access binding source in ConvertBack()?

    Question

  • Hi All,

    I have a case that needs to access the binding source in ConvertBack. Any idea?

    xaml:
    ...

    <localconverter:ConverterA x:Key="ConverterA" />

    ...
    <DataTemplate>
    <
    control:TextEditorBox Margin="3, 0, 3, 0" x:Name="ValueText" Text="{Binding Path=., Converter={StaticResource ConverterA}, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
    </DataTemplate>
    ...

    convertA.cs
    ...
    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)

        //Need to access binding source to decide the convert back algorithm
    }

    The data template will be binded to a itemlist control's individual item. What I want to accomplish here is that I can use the different convertion logic depending on the item's binding source data.

    Appreciate your helps
    Henry


    Fantastic dotNET
    Tuesday, August 26, 2008 5:24 AM

Answers

  • There's a few ways to go about it.

    You could create a separate converter instance for each binding and expose a property on the converter that points to the source.  This might mean creating the different converters in code (or getting creative and making them freezables so you can use bindings on them).  That is probably overkill for your scenario.

    An easier solution would be to drop my ObjectReference markup extension into your project.  Then you could supply the binding target as the converter parameter, like so:

     
    <TextBox dw:ObjectReference.Declaration="{dw:ObjectReference tb}" 
        Text="{Binding Path=SomePropertyConverter={StaticResource MyConverter},   
            ConverterParameter={dw:ObjectReference tb}}" /> 
     

    From within your ConvertBack routine, you could access the binding source object by casting the converter parameter to a FrameworkElement and then accessing its DataContext property.

     
    object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)  
    {  
        object source = (parameter as FrameworkElement).DataContext;  
        // . . .  
    }  
     

    Dr. WPF - Online Office at http://drwpf.com/blog/
    • Marked as answer by HenryW_AU Wednesday, August 27, 2008 2:32 AM
    Wednesday, August 27, 2008 2:05 AM

All replies

  •  I can use the different convertion logic depending on the item's binding source data.???
    Can you give an example of your different source data?

    In this scenerio can you think of making use of Converter parameter.
    Tuesday, August 26, 2008 6:56 AM
  • Hi,

    Thanks for reply. Here is more xaml code for further explaination:
    <ListView ItemsSource="MaterialView">
      <ListView.View>      
        <GridView>
          <GridViewColumn Header="Measurement" 
                   CellTemplate="{StaticResource dataTemp}"/>
        </GridView>
      </ListView.View>   
    </ListView>

    The dataTemp is the data template defined in previous mail. What I want to do is tha the ConverterA can convert back the column's value with different ways depending on the list item's data source. For instance, DataView is a list collection view of List<Material>,  each material will have different measurement way, Length/Weight/Mass/Density, etc, the converter will try to work out the correct parsing for the input string depending on Material.Type. Anyway, because we already implemented a group of Helpers to parse string with its Material.Type parameter, here I'd just like to invoke the Helpers and dont want to rewrite the parser in ConverterBack again. So need to access the binding source: Material in ConverterBack().

    The ConverterParameter didnot work in this case, because it only accept static value. Here the Material.Type information is dynamic.

    regards



    Fantastic dotNET
    • Edited by HenryW_AU Tuesday, August 26, 2008 11:44 PM add more comments
    Tuesday, August 26, 2008 11:22 PM
  •  The solution is to use a MultiBinding with a MultiValueConverter.  I discuss this here.
    Dr. WPF - Online Office at http://drwpf.com/blog/
    • Proposed as answer by Dr. WPF Wednesday, August 27, 2008 12:43 AM
    Wednesday, August 27, 2008 12:08 AM
  • Hi Dr.WPF,

    You are right, we can access binding source in Convert(). Unfortunately, looks it's not true in ConvertBack(). That's my pain here. Any idea?
    Thanks

    regards
    Fantastic dotNET
    Wednesday, August 27, 2008 1:19 AM
  • There's a few ways to go about it.

    You could create a separate converter instance for each binding and expose a property on the converter that points to the source.  This might mean creating the different converters in code (or getting creative and making them freezables so you can use bindings on them).  That is probably overkill for your scenario.

    An easier solution would be to drop my ObjectReference markup extension into your project.  Then you could supply the binding target as the converter parameter, like so:

     
    <TextBox dw:ObjectReference.Declaration="{dw:ObjectReference tb}" 
        Text="{Binding Path=SomePropertyConverter={StaticResource MyConverter},   
            ConverterParameter={dw:ObjectReference tb}}" /> 
     

    From within your ConvertBack routine, you could access the binding source object by casting the converter parameter to a FrameworkElement and then accessing its DataContext property.

     
    object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)  
    {  
        object source = (parameter as FrameworkElement).DataContext;  
        // . . .  
    }  
     

    Dr. WPF - Online Office at http://drwpf.com/blog/
    • Marked as answer by HenryW_AU Wednesday, August 27, 2008 2:32 AM
    Wednesday, August 27, 2008 2:05 AM
  • Thanks Dr.WPF,

    Your markup extension sounds great. I will take a try. Thank you so much

    Anyway, can I bother you to discuss the way to "Create separate converter instance for each binding"? Are they same converter's different instance? How to create them dynamically in terms of the case to binding it to ItemList control's template?

    Henry
    Fantastic dotNET
    Wednesday, August 27, 2008 2:32 AM
  • Hey Henry,

    A converter is simply an object that implements IValueConverter.  You can have multiple instances of a converter.  In your scenario, you could use a distinct instance with each binding.

    It is pretty common to expose properties on your converter class so that you can tweak the way a particular instance performs conversion.  In your case, you might choose to have a property called Source (of type Object).  The tricky part is initializing this property.  If you create the converter and set up the binding in code, then its easy.  However, most often bindings are established in markup.  Similarly, converters are typically instantiated in markup by adding them to a resource dictionary and referencing them in a binding via a StaticResource reference.  You won't want to do that, since you need a separate instance for each binding.

    Probably the easiest solution for your scenario would be to use the Loaded event of your object to establish the binding on the object.  That event handler could then dynamically create the converter and pass a reference to the source object to its constructor.  (You should maintain a weak reference to the object within the converter so that you don't lock it in memory.)  It can then use the converter in the binding.

    Does this make sense?

    Cheers,
    -dw
    Dr. WPF - Online Office at http://drwpf.com/blog/
    Wednesday, August 27, 2008 5:55 AM
  • Hi Dr.WPF,

    I tried your markup extension, but not sure if I used it properly. As you suggested, I added your extension code into my view project and used the markup as your sample. The problem is that looks the ObjectReference only be initialized twice(once for each markup: <TextBox dw:ObjectReference.Declareation={} />, and ConverterParameter={}), but not as my expecting that initialized for each binding item.

    I used to add a dependency property into my converter(it derived from DependencyObject) and set the binding for this property to spread in dynamic object. But as you already mentioned, converter need to be declared as static resource in xaml, so only one instance for my converter(only be able to set the dependency property when initialize this single instance) and not fulfill the requirement to use dynamic data in converter.

    And for the proposal to binding different converters(or different converter instances) for every TextBox, not using ListItemsControl, it looks out of scope we discussed.  Also I tried to ignore the ConvertBack() and leave the convertion logic to BE's Setter. It works in this case, but will bring out issues for other code.

    I am not sure the exact machanism WPF to manage the converters. Looks the ConverterParameter is likely sort of readonly field, and can only be set value once with binding definition, doesnot matter it's a referenced object or a static value. 

    Appreciate your further helps

    Henry
    Fantastic dotNET
    • Edited by HenryW_AU Wednesday, August 27, 2008 11:38 PM adding comments
    Wednesday, August 27, 2008 11:18 PM
  • HenryW_AU said:

    I tried your markup extension, but not sure if I used it properly. As you suggested, I added your extension code into my view project and used the markup as your sample. The problem is that looks the ObjectReference only be initialized twice(once for each markup: <TextBox dw:ObjectReference.Declareation={} />, and ConverterParameter={}), but not as my expecting that initialized for each binding item.


    You can certainly use the markup extension repeatedly.  Just make sure you include the declaration prior to the reference (because parse order matters).  For example, you can do this:

    <TextBox dw:ObjectReference.Declaration="{dw:ObjectReference tb}" Name="tb1" 
        Text="{Binding Path=NameConverter={StaticResource MyConverter},   
            ConverterParameter={dw:ObjectReference tb}}" /> 
    <TextBox dw:ObjectReference.Declaration="{dw:ObjectReference tb}" Name="tb2" 
        Text="{Binding Path=NameConverter={StaticResource MyConverter},  
            ConverterParameter={dw:ObjectReference tb}}" /> 
     

    When the source is updated for the first binding, your ConvertBack method will receive tb1 as its parameter.  And when the source is updated for the second binding, your ConvertBack method will receive tb2 as its parameter.


    HenryW_AU said:

    I used to add a dependency property into my converter(it derived from DependencyObject) and set the binding for this property to spread in dynamic object. But as you already mentioned, converter need to be declared as static resource in xaml, so only one instance for my converter(only be able to set the dependency property when initialize this single instance) and not fulfill the requirement to use dynamic data in converter.


    A binding on the converter will not resolve because the converter object is not in the element tree... its in a resource dictionary.  It has no inheritance context through which to resolve the binding.  This is why creating the binding in code makes more sense for your scenario.  (But the ObjectReference approach still seems more convenient to me.)

    Dr. WPF - Online Office at http://drwpf.com/blog/
    Wednesday, August 27, 2008 11:38 PM
  • Hi Dr.,

    I thought you are suggesting me to create individual TextBox instead of using ListItemsControl here, in order to instance a respective ObjectReference object via your markup extension which including information I need for the Converter for that TextBox.

    If this is the case, I'd rather create it in code, in which I can decide what the ConverterParameter value is. The xaml is not dynamic, I cannot know how many TextBox I need to create to accomodate the dynamic data source.

    I'm not sure whether my understanding for your solution is correct.
    Thanks

    Henry
    Fantastic dotNET
    • Edited by HenryW_AU Thursday, August 28, 2008 12:26 AM correct
    Thursday, August 28, 2008 12:22 AM
  • I just tried your approach and got a null reference error for DataContext

    using the following ...

     

     

    <TextBox Grid.Row="1" Grid.RowSpan="2"
    SpellCheck.IsEnabled="True"
    local:ObjectReference.Declaration="{local:ObjectReference okey}"
    Name="txtPrompt"
    Text="{Binding Path=promptID,
    Converter={StaticResource ItemPromptValueConverter},
    ConverterParameter={local:ObjectReference okey}}" />

     

     

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
               object source = (parameter as FrameworkElement).DataContext; 
               .......
    }

    The converter quieries the db for a prompt string, if it changes, I want to update the db.

    any suggestions

    Thursday, May 28, 2009 9:37 PM
  • There is a problem with the Object Reference Markup Extension code from Dr WPF which means it won't work with a DataTemplate as requested. There is a fix though, which I have included in the comment. Otherwise great code. Thanks Dr WPF!

    Dr Max

    Friday, July 22, 2011 10:31 AM