如何简单地将其绑定到转换器参数
本文关键字:绑定 转换器 参数 何简单 简单 | 更新日期: 2023-09-27 17:55:08
我有问题,我不知道如何解决这个简单,我有很多这样的点,那么解决方案应该不复杂。
我有带有设置和主 XAML 的主项目。
我有绑定转换器和 XAML 文件的依赖项项目,如下所示:
<TextBlock Text="{Binding X.Y.Z,
Converter={StaticResource ProbabilityConverter},
ConverterParameter=??????????????, Mode=OneWay}"
/>
此 XAML 文件由主 XAML 文件从主项目加载。
我必须将一个属性的值从 Set 传递给 ConverterParameter
,这个参数可以在运行时更改,那么这必须是Binding
的,Binding
在这种情况下我只能对DependencyProperty
执行。
我必须为此设置属性做DependencyProperty
包装器才能解决此问题?
当我尝试在 ConverterParameter
中设置Binding
时,我将在运行时收到此异常:
无法在 类型的"转换器参数"属性 "绑定"。只能设置"绑定" 在依赖项属性上 依赖对象。
您可以绑定到任何属性,它不必是依赖项属性。但是,如果您希望 UI 在发生更改时立即反映属性中的更改,则有两种选择:
- 使该属性成为依赖项属性。
- 在保存属性的类型上实现
INotifyPropertyChanged
,并在属性更改时引发PropertyChanged
事件。
编辑:
正如在对问题的编辑中指出的那样,无法绑定到ConverterParameter
。但是您可以使用MultiBinding
.例如,假设您要绑定到日期并将转换器区域性规范作为参数,并在区域性更改时刷新绑定(我不确定这是个好主意,但它可以很好地用作示例)。你可以这样做:
<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>
在这里,Date
和 Settings
都是当前DataContext
的属性。 DateCultureConverter
实现IMultiValueConverter
,您可能会将其放在实际应用程序中层次结构上几级的资源中。
您可以使用以下解决方案之一:
- 可绑定参数(使用普通绑定 + 附加属性和标记扩展)
https://marlongrech.wordpress.com/2008/08/03/my-wish-came-true-i-can-now-use-databinding-in-a-converterparameter/
您必须集成类 BindableParameter 和 BindableParameterExtension(见下文),然后您可以按如下方式使用它:
在 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>
属性"目标属性":
TargetProperty=TextBox.Text
的 BindableParamerter 必须设置为原始绑定属性(在本例中为"TextBox.Text")。
采样转换器:
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;
}
}
}
该参数可用于"转换"和"转换回来"方法(对于绑定到视图模型很有用)。
类 BindableParameter 和 BindableParameterExtension(URL 见上文(不是我的代码))
/*
* 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/
您必须集成类 ObjectReference:
http://www.drwpf.com/blog/Portals/0/Code/ObjectReference.cs.txt
在 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>
剪断:
local:ObjectReference.Declaration="{local:ObjectReference txtParam}"
在静态字典中创建引用,并创建以下部分:
ConverterParameter={local:ObjectReference txtParam}}"
从字典中获取此对象引用 -->此处没有绑定,字典在解析时填充。
采样转换器:
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
在 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>
采样转换器:
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;
}
}
}