Get at Binding from inside IValueConverter?
I am writing an IValueConverter that needs access to more than just the value to convert. Specifically, I need access to the DataContext (source object) and Path (property name) so I can check to see whether the user is authorized to view the data. If they aren't, I want the converter to return "n/a" or some similar neutral value.
Obviously this sort of thing is somewhat possible, because the ExceptionValidationRule is aware of the data flow and whether an exception occurred.
But it isn't obvious to me, or google, how you might get at these details from inside the Convert() or ConvertBack() methods.
Anyone have any ideas?
Answers
Use an IMultiValueConverter whose inputs are (a) the real data, (b) the target element, and (c) the target property. The last input is constant, so it can be transmitted as a converter parameter. Use the target element to get the DataContext, and use the target property to look up the binding path. Here's a sample that you should be able to adapt to your scenario:
XAML:
<TextBlock DataContext="{Binding ElementName=tb, Path=Text}">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource MyConverter}" ConverterParameter="{x:Static TextBlock.TextProperty}">
<Binding/> <!-- this is the binding to the "real" data - in your scenario it may have its own path, converter, etc. -->
<Binding RelativeSource="{x:Static RelativeSource.Self}"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>Code:
public class MyConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string rawValue = (string)values[0];
DependencyObject targetElement = (DependencyObject)values[1];
DependencyProperty dp = (DependencyProperty)parameter;object dataContext = targetElement.GetValue(FrameworkElement.DataContextProperty);
MultiBindingExpression mbe = targetElement.ReadLocalValue(dp) as MultiBindingExpression;
MultiBinding mb = mbe.ParentMultiBinding;
Binding binding = mb.Bindings[0] as Binding;return (IsAccessAllowed(dataContext, binding.Path.Path)) ? rawValue : "n/a";
}public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException("The method or operation is not implemented.");
}private bool IsAccessAllowed(object dataContext, string path)
{
string s = dataContext as String;
return (s != null && s.StartsWith("abc")); // your logic goes here
}
#endregion
}The code to get the MultiBinding should really be written as
MultiBinding mb = BindingOperations.GetMultiBinding(targetElement, dp);
but that didn't work for some reason (it returned null). I fear it's a bug, but I can't tell for sure from here (I'm not in the office). The code in the sample works for local bindings, but not for bindings defined in a style or template. GetMultiBinding is supposed to work in all cases.I'm kind of embarassed that this works, since converters are supposed to be stateless and ignorant of their context, in order to reduce the possiblity of side-effects that make the app difficult to debug and maintain. Your scenario seems fine, but I could imagine this technique being abused. Oh well, too late now.
All Replies
Use an IMultiValueConverter whose inputs are (a) the real data, (b) the target element, and (c) the target property. The last input is constant, so it can be transmitted as a converter parameter. Use the target element to get the DataContext, and use the target property to look up the binding path. Here's a sample that you should be able to adapt to your scenario:
XAML:
<TextBlock DataContext="{Binding ElementName=tb, Path=Text}">
<TextBlock.Text>
<MultiBinding Converter="{StaticResource MyConverter}" ConverterParameter="{x:Static TextBlock.TextProperty}">
<Binding/> <!-- this is the binding to the "real" data - in your scenario it may have its own path, converter, etc. -->
<Binding RelativeSource="{x:Static RelativeSource.Self}"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>Code:
public class MyConverter : IMultiValueConverter
{
#region IMultiValueConverter Members
public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
string rawValue = (string)values[0];
DependencyObject targetElement = (DependencyObject)values[1];
DependencyProperty dp = (DependencyProperty)parameter;object dataContext = targetElement.GetValue(FrameworkElement.DataContextProperty);
MultiBindingExpression mbe = targetElement.ReadLocalValue(dp) as MultiBindingExpression;
MultiBinding mb = mbe.ParentMultiBinding;
Binding binding = mb.Bindings[0] as Binding;return (IsAccessAllowed(dataContext, binding.Path.Path)) ? rawValue : "n/a";
}public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
{
throw new NotSupportedException("The method or operation is not implemented.");
}private bool IsAccessAllowed(object dataContext, string path)
{
string s = dataContext as String;
return (s != null && s.StartsWith("abc")); // your logic goes here
}
#endregion
}The code to get the MultiBinding should really be written as
MultiBinding mb = BindingOperations.GetMultiBinding(targetElement, dp);
but that didn't work for some reason (it returned null). I fear it's a bug, but I can't tell for sure from here (I'm not in the office). The code in the sample works for local bindings, but not for bindings defined in a style or template. GetMultiBinding is supposed to work in all cases.I'm kind of embarassed that this works, since converters are supposed to be stateless and ignorant of their context, in order to reduce the possiblity of side-effects that make the app difficult to debug and maintain. Your scenario seems fine, but I could imagine this technique being abused. Oh well, too late now.
- Thanks Sam! This is so sweet.

Thank you Sam, I appreciate the help and this appears to be what I'm after.
However, I understand your concern about misusing a converter like this.
Given my requirement, which is to prevent returning data if it is not authorized, do you have a better suggestion for a way to accomplish this goal?
Perhaps I could do it in the code-behind if there's a way to enable/disable a binding at runtime? In other words, perhaps I could just have code in the page that checks the authorization and if the user isn't authorized it could just temporarily disable the binding to prevent the value from appearing?
Obviously I'd need to be able to dynamically reenable the binding later, because the user could log in, or switch identities to a more priviledged one that could see the value.
- My initial thought is that you can use the attached DPs to achieve this, imagine that you can name this attached DPs as IsAuthorizedProperty, and place a CLR IsAuthroized property wrapper around it, then in the xaml, you can use the property trigger to enable and disable binding based on the true/false value of this property:
<Trigger Property="AuthroizationManager.IsAuthorized" Value="True">
<Setter Property="DataContext" TargetName="fooElement" Value="{Binding Path=dataSource}"/>
</Trigger>
<Trigger Property="AuthroizationManager.IsAuthorized" Value="False">
<Setter Property="DataContext" TargetName="fooElement" Value="{x:Null}"/>
</Trigger>
Sheva There's no way to disable a binding at runtime. You can remove it and reinstall it later. You can tweak its inputs as Sheva suggested. (Of course if there's an IsAuthorized property available, you can simply include it in the inputs to the MultiValueConverter; this is more efficient than DataTrigger.) But you can't leave the binding lurking silently - if it exists, it will contribute to the computation of its target property.
I gather that your authorization check depends on which source property is being queried, so that a single "IsAuthorized" property isn't granular enough. In that case, I don't have a better idea. Your use of my converter technique is perfectly safe. It's a clean function with no side-effects (i.e. it doesn't alter the tree, change source properties, add/remove bindings, etc.), so go ahead and use it.
- If you want a value converter to "not return anything" you can make it return Binding.DoNothing. Then the binding system will not transfer the binding source value to the target (this is different from returning null from the Convert method). Does that help you?
Another way to do it is using the ConverterParameter property of a binding. Something like:
Binding b = new Binding();
b.Converter = new MyConverter();
b.ConverterParameter = b;
...You can then use it directly inside the converter:
public class MyConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
Binding b = (Binding)parameter;
if (b.Path == "abc") {
// custom logic
}
...
}
...
}
P.S.,
I totally agree that a converter should not depend on state, but this is not related to whether or not parameters are passed to the converter. You can create a converter that depends on state even if it doesn't get any parameters other then the value.
I don't think that passing parameters to converters is wrong. It can actually help in reducing the number of converters you have to write in many cases.

