I have problem and i don't know how to solve this simple, i have many points like this, then solution should be not complicated.
I have main project with Settings and main XAML.
I have dependency project with Binding Converter and XAML File looks like:
<TextBlock Text="{Binding X.Y.Z,
Converter={StaticResource ProbabilityConverter},
ConverterParameter=??????????????, Mode=OneWay}"
/>
This XAML file is loading by main XAML file from main project.
I must pass value of one property from Setting's to ConverterParameter
, this parameter can be changing at runtime, then this is must be Binding
, Binding
i can do only for DependencyProperty
in this case.
I must do DependencyProperty
wrapper for this Setting property to solve this problem?
When i try set Binding
in ConverterParameter
i will get this exception at runtime:
A 'Binding' cannot be set on the 'ConverterParameter' property of type 'Binding'. A 'Binding' can only be set on a DependencyProperty of a DependencyObject.
The ConverterParameter property can not be bound because it is not a dependency property. Since Binding is not derived from DependencyObject none of its properties can be dependency properties. As a consequence, a Binding can never be the target object of another Binding.
Multibinding takes multiple values and combines them into another value. There are two ways to do multibinding, either using StringFormat or by a converter. The StringFormat is simple compared to a converter, so we will start with that first. <TextBlock> <TextBlock.Text>
You can bind to any property, it doesn't have to be a dependency property. But if you want your UI to reflect changes in the property immediately when they happen, you have two options:
INotifyPropertyChanged
on the type that holds the property and raise the PropertyChanged
event when the property changes.EDIT:
As pointed out in the edit to the question, it's not possible to bind to ConverterParameter
. But you can use MultiBinding
. For example, assume you want to bind to a date and give the converter culture specification as a parameter and refresh the binding when the culture changes (I'm not sure this is a good idea, but it serves well as an example). You could do it like this:
<TextBlock>
<TextBlock.Resources>
<local:DateCultureConverter x:Key="converter" />
</TextBlock.Resources>
<TextBlock.Text>
<MultiBinding Converter="{StaticResource converter}">
<Binding Path="Date" />
<Binding Path="Settings.Culture" />
</MultiBinding>
</TextBlock.Text>
</TextBlock>
Here, both Date
and Settings
are properties on the current DataContext
. DateCultureConverter
implements IMultiValueConverter
and you would probably put it in resources few levels up the hierarchy in real application.
You can use one of the following solutions:
https://marlongrech.wordpress.com/2008/08/03/my-wish-came-true-i-can-now-use-databinding-in-a-converterparameter/
You must integrate the class BindableParameter and BindableParameterExtension (see below) and then you can use it as follows:
In XAML:
xmlns:local="clr-namespace:BindableParameterExtension"
<local:SampleConverter x:Key="sampleConverter" />
<StackPanel Orientation="Vertical">
<TextBox Name="txtContent" Text="Text from txtContent" />
<TextBox Name="txtParameter" Text="Text from txtParameter" />
<TextBox Name="txtBindingSample"
Text="{Binding ElementName=txtContent, Path=Text, Converter={StaticResource sampleConverter}}"
local:BindableParameter.BindParameter="{local:BindableParameter TargetProperty=TextBox.Text,
Binding={Binding ElementName=txtParameter, Path=Text} }" />
</StackPanel>
The Property "TargetProperty":
TargetProperty=TextBox.Text
of the BindableParamerter must be set to the original bound property (in this case "TextBox.Text").
The Sample-Converter:
using System;
using System.Windows.Data;
namespace BindableParameterExtension
{
public class SampleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null && parameter != null)
{
return value.ToString() + ", " + parameter.ToString();
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is string && parameter is string)
{
string text1 = value as string;
string textParamter = parameter as string;
return text1.Replace(textParamter, "");
}
return value;
}
}
}
The parameter could be used in the "Convert" and the "ConvertBack" method (useful to bind to an view model).
Class BindableParameter and BindableParameterExtension (URL see above (not my code))
/*
* Copyright - Everyone can use this code for any reason yet if you find a bug, I do not hold myself responsable :D
*/
using System.Windows.Data;
using System.Windows.Markup;
namespace BindableParameterExtension
{
/// <summary>
/// BindableParameter is the class that changes the ConverterParameter Value
/// This must inherit from freezable so that it can be in the inheritance context and thus be able to use the DataContext and to specify ElementName binding as a ConverterParameter
/// http://www.drwpf.com/Blog/Default.aspx?tabid=36&EntryID=36
/// </summary>
public class BindableParameter : Freezable
{
#region fields
//this is a hack to trick the WPF platform in thining that the binding is not sealed yet and then change the value of the converter parameter
private static FieldInfo isSealedFieldInfo;
#endregion
#region Properties
#region Parameter
/// <summary>
/// Parameter Dependency Property
/// </summary>
public static readonly DependencyProperty ParameterProperty =
DependencyProperty.Register("Parameter", typeof(object), typeof(BindableParameter),
new FrameworkPropertyMetadata((object)null,
(d, e) =>
{
BindableParameter param = (BindableParameter)d;
//set the ConverterParameterValue before calling invalidate because the invalidate uses that value to sett the converter paramter
param.ConverterParameterValue = e.NewValue;
//update the converter parameter
InvalidateBinding(param);
}
));
/// <summary>
/// Gets or sets the Parameter property. This dependency property
/// indicates ....
/// </summary>
public object Parameter
{
get { return (object)GetValue(ParameterProperty); }
set { SetValue(ParameterProperty, value); }
}
#endregion
#region BindParameter
/// <summary>
/// BindParameter Attached Dependency Property
/// </summary>
public static readonly DependencyProperty BindParameterProperty =
DependencyProperty.RegisterAttached("BindParameter", typeof(BindableParameter), typeof(BindableParameter),
new FrameworkPropertyMetadata((BindableParameter)null,
new PropertyChangedCallback(OnBindParameterChanged)));
/// <summary>
/// Gets the BindParameter property. This dependency property
/// indicates ....
/// </summary>
public static BindableParameter GetBindParameter(DependencyObject d)
{
return (BindableParameter)d.GetValue(BindParameterProperty);
}
/// <summary>
/// Sets the BindParameter property. This dependency property
/// indicates ....
/// </summary>
public static void SetBindParameter(DependencyObject d, BindableParameter value)
{
d.SetValue(BindParameterProperty, value);
}
/// <summary>
/// Handles changes to the BindParameter property.
/// </summary>
private static void OnBindParameterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement element = d as FrameworkElement;
if (element == null)
throw new InvalidOperationException("BindableParameter can be applied to a FrameworkElement only");
BindableParameter parameter = (BindableParameter)e.NewValue;
element.Initialized += delegate
{
parameter.TargetExpression = BindingOperations.GetBindingExpression(element, parameter.TargetProperty);
parameter.TargetBinding = BindingOperations.GetBinding(element, parameter.TargetProperty);
//update the converter parameter
InvalidateBinding(parameter);
};
}
#endregion
public object ConverterParameterValue { get; set; }
public BindingExpression TargetExpression { get; set; }
public Binding TargetBinding { get; private set; }
/// <summary>
/// Gets the object being bound
/// </summary>
public DependencyObject TargetObject { get; private set; }
/// <summary>
/// Gets the dependency property being bound
/// </summary>
public DependencyProperty TargetProperty { get; internal set; }
#endregion
/// <summary>
/// Static constructor to get the FieldInfo meta data for the _isSealed field of the BindingBase class
/// </summary>
static BindableParameter()
{
//initialize the field info once
isSealedFieldInfo =
typeof(BindingBase).GetField("_isSealed", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
if (isSealedFieldInfo == null)
throw new InvalidOperationException("Oops, we have a problem, it seems like the WPF team decided to change the name of the _isSealed field of the BindingBase class.");
}
private static void InvalidateBinding(BindableParameter param)
{
if (param.TargetBinding != null && param.TargetExpression != null)
{
//this is a hack to trick the WPF platform in thining that the binding is not sealed yet and then change the value of the converter parameter
bool isSealed = (bool)isSealedFieldInfo.GetValue(param.TargetBinding);
if (isSealed)//change the is sealed value
isSealedFieldInfo.SetValue(param.TargetBinding, false);
param.TargetBinding.ConverterParameter = param.ConverterParameterValue;
if (isSealed)//put the is sealed value back as it was...
isSealedFieldInfo.SetValue(param.TargetBinding, true);
//force an update to the binding
param.TargetExpression.UpdateTarget();
}
}
#region Freezable Stuff
protected override Freezable CreateInstanceCore()
{
//throw new NotImplementedException();
//return _bindableParam;
return this;
}
#endregion
}
/// <summary>
/// Markup extension so that it is easier to create an instance of the BindableParameter from XAML
/// </summary>
[MarkupExtensionReturnType(typeof(BindableParameter))]
public class BindableParameterExtension : MarkupExtension
{
/// <summary>
/// Gets or sets the Dependency property you want to change the binding's ConverterParameter
/// </summary>
public DependencyProperty TargetProperty { get; set; }
/// <summary>
/// Gets or sets the Binding that you want to use for the converter parameter
/// </summary>
public Binding Binding { get; set; }
/// <summary>
/// constructor that accepts a Dependency Property so that you do not need to specify TargetProperty
/// </summary>
/// <param name="property">The Dependency property you want to change the binding's ConverterParameter</param>
public BindableParameterExtension(DependencyProperty property)
{
TargetProperty = property;
}
public BindableParameterExtension()
{ }
public override object ProvideValue(IServiceProvider serviceProvider)
{
_bindableParam = new BindableParameter();
//set the binding of the parameter
BindingOperations.SetBinding(_bindableParam, BindableParameter.ParameterProperty, Binding);
_bindableParam.TargetProperty = TargetProperty;
return _bindableParam;
}
private BindableParameter _bindableParam;
}
}
http://drwpf.com/blog/2007/09/02/supplying-an-object-reference-in-the-constructorparameters-collection-of-an-objectdataprovider/
You must integrate the class ObjectReference:
http://www.drwpf.com/blog/Portals/0/Code/ObjectReference.cs.txt
In XAML:
xmlns:local="clr-namespace:WpfMarkupExtension"
<local:SampleConverter x:Key="sampleConverter" />
<StackPanel Orientation="Vertical">
<TextBox Name="txtContent" Text="Text from txtContent" />
<TextBox Name="txtParameter" Text="Text from txtParameter" local:ObjectReference.Declaration="{local:ObjectReference txtParam}" />
<TextBox Name="txtBindingSample"
Text="{Binding ElementName=txtContent, Path=Text,
Converter={StaticResource sampleConverter},
ConverterParameter={local:ObjectReference txtParam}}" />
</StackPanel>
The snipped:
local:ObjectReference.Declaration="{local:ObjectReference txtParam}"
creates the reference in an static dictionary and the part:
ConverterParameter={local:ObjectReference txtParam}}"
takes this object reference from the Dictionary --> no binding here, the dictionary is filled on parse time.
The Sample-Converter:
using System;
using System.Windows.Controls;
using System.Windows.Data;
namespace WpfMarkupExtension
{
public class SampleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null && parameter is TextBox)
{
return value.ToString() + ", " + ((TextBox)parameter).Text;
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is string && parameter is TextBox)
{
string text1 = value as string;
string textParamter = ((TextBox)parameter).Text;
return text1.Replace(textParamter, "");
}
return value;
}
}
}
http://www.codeproject.com/Articles/456589/Bindable-Converter-Parameter
In XAML:
xmlns:local="clr-namespace:BcpBindingExtension"
<local:SampleConverter x:Key="sampleConverter" />
<StackPanel Orientation="Vertical">
<TextBox Name="txtContent" Text="Text from txtContent" />
<TextBox Name="txtParameter" Text="Text from txtParameter" />
<TextBox Name="txtBindingSample">
<TextBox.Text>
<local:BcpBinding Path="Text" ElementName="txtContent"
Converter="{StaticResource sampleConverter}"
ConverterParameters="Binding Path=Text ElementName=txtParameter"
Mode="TwoWay"/>
</TextBox.Text>
</TextBox>
</StackPanel>
The Sample-Converter:
using System;
using System.Windows.Data;
namespace BcpBindingExtension
{
public class SampleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value != null && parameter is object[] && ((object[])parameter).Length > 0)
{
return value.ToString() + ", " + ((object[])parameter)[0].ToString();
}
return null;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is string && parameter is object[] && ((object[])parameter).Length > 0)
{
string text1 = value as string;
string textParamter = ((object[])parameter)[0] as string;
return text1.Replace(textParamter, "");
}
return value;
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With