私は問題を抱えており、私はこれを簡単に解決する方法がわかりません、私はこのような多くの点を持っているので、ソリューションは複雑ではないはずです。
設定とメインXAMLを使用したメインプロジェクトがあります。
Binding Converterを使用した依存関係プロジェクトがあり、XAMLファイルは次のようになります。
<TextBlock Text="{Binding X.Y.Z,
Converter={StaticResource ProbabilityConverter},
ConverterParameter=??????????????, Mode=OneWay}"
/>
このXAMLファイルは、メインプロジェクトのメインXAMLファイルによってロードされています。
1つのプロパティの値を設定からConverterParameter
に渡す必要があります。このパラメーターは実行時に変更できます。これはBinding
でなければなりません。この場合、Binding
はDependencyProperty
に対してのみ実行できます。
この問題を解決するには、この設定プロパティのDependencyProperty
ラッパーを実行する必要がありますか?
Binding
にConverterParameter
を設定しようとすると、実行時にこの例外が発生します。
タイプ 'Binding'の 'ConverterParameter'プロパティに 'Binding'を設定できません。 'Binding'は、DependencyObjectのDependencyPropertyでのみ設定できます。
任意のプロパティにバインドできます。依存関係プロパティである必要はありません。ただし、プロパティの変更が発生したときにすぐにUIに反映させるには、2つのオプションがあります。
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
を実装しているため、実際のアプリケーションでは、階層の数レベル上のリソースに配置することになります。
次のいずれかのソリューションを使用できます。
クラス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":
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;
}
}
}
このパラメーターは、「Convert」および「ConvertBack」メソッドで使用できます(ビューモデルにバインドするのに役立ちます)。
クラス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;
}
}
クラス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;
}
}
}