我创建了一些附加属性以允许间接绑定(我指的是绑定到名称由附加属性赋予的值,而不是在XAML中指定为文字)。
某些AP是可选的(例如,一个覆盖原本会生效的DataContext
)并且这意味着我在尝试创建绑定时,并非所有AP都已生效设置(因为PropertyChangedCallback
我不知道是否会设置其他人。)
结果是绑定可以多次创建,有时不成功,这会导致绑定错误,因为缺少更好的单词,所以会出现不愉快的情况。
是否有一种方法可以在分配元素的所有AP之前抑制绑定错误,或者在PropertyChangedCallback
内解决是否会包含任何更多的包含AP的内容在这个元素上设置?
修改
我被要求提供代码。我希望没有这样做(因为它让问题变得相当长!),但这是我要问的课程:
public static class BindingIndirector
{
public static string GetBindingSource(DependencyObject dob)
{
return (string)dob.GetValue(BindingSourceProperty);
}
public static void SetBindingSource(DependencyObject dob, string value)
{
dob.SetValue(BindingSourceProperty, value);
}
/// <summary>
/// The "source" to be set on the binding.
/// Must be specified.
/// </summary>
public static readonly DependencyProperty BindingSourceProperty =
DependencyProperty.RegisterAttached(
"BindingSource",
typeof(String),
typeof(BindingIndirector),
new PropertyMetadata(null, BindingChanged));
public static object GetBindingSourceContext(DependencyObject dob)
{
return dob.GetValue(BindingSourceContextProperty);
}
public static void SetBindingSourceContext(DependencyObject dob, object value)
{
dob.SetValue(BindingSourceContextProperty, value);
}
/// <summary>
/// A DataContext type property. This overrides the inherited DataContext that would otherwise be
/// used for the binding.
/// Optional.
/// </summary>
public static readonly DependencyProperty BindingSourceContextProperty =
DependencyProperty.RegisterAttached(
"BindingSourceContext",
typeof(object),
typeof(BindingIndirector),
new PropertyMetadata(null, BindingChanged));
public static string GetBindingTarget(DependencyObject dob)
{
return (string)dob.GetValue(BindingTargetProperty);
}
public static void SetBindingTarget(DependencyObject dob, string value)
{
dob.SetValue(BindingTargetProperty, value);
}
/// <summary>
/// The binding target property.
/// Optional (defaults to "Content" if not specified
/// </summary>
public static readonly DependencyProperty BindingTargetProperty =
DependencyProperty.RegisterAttached(
"BindingTarget",
typeof(String),
typeof(BindingIndirector),
new PropertyMetadata("Content", BindingChanged));
private static void BindingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (!(e.Property == BindingSourceContextProperty || e.NewValue is string))
throw new ArgumentException("Property can only be set to string values", e.Property.ToString());
// Check rules for attempting to set the binding are met
string source = GetBindingSource(d) as string;
string target = GetBindingTarget(d) as string;
object context = GetBindingSourceContext(d);
if (source == null) // Source needs to be set - don't interfere with binding if it isn't
return;
// Clear any existing binding
var originalName = e.Property ==
BindingSourceProperty ?
target :
e.OldValue as string;
if (originalName != null)
{
var existingDescriptor =
DependencyPropertyDescriptor.FromName(
originalName,
d.GetType(),
d.GetType());
if (existingDescriptor != null)
d.ClearValue(existingDescriptor.DependencyProperty);
}
// Create and assign new binding
var targetDescriptor =
DependencyPropertyDescriptor.FromName(
target,
d.GetType(),
d.GetType());
if (targetDescriptor != null) // don't interfere with binding if target invalid
{
Binding newBinding = new Binding(source) { Mode = BindingMode.TwoWay };
if (context != null) // Will fall back to DataContext of element in this case
newBinding.Source = context;
BindingOperations.SetBinding(d, targetDescriptor.DependencyProperty, newBinding);
}
}
}
这个静态类创建了3个附加属性,并且还包含一个方法,&#34; BindingChanged()&#34;这是所有三个AP的propertyChangedCallback
。如果已经给出了足够的信息来尝试创建绑定,则会这样做,丢弃先前用于创建AP的任何先前绑定。
它没有做什么(可能是一个解决方案)是找出绑定是先成功还是捕获绑定引擎产生的任何错误(你能这样做吗?)。在不抑制应该显示的绑定错误方面可能存在挑战(例如,因为最终用户提供了duff信息)。
以下是一个用例的示例:
<UserControl x:Class="UtilityControls.ListEditor"
...>
<Grid x:Name="ControlContainer">
<Grid.DataContext>
<local:LeViewModel x:Name="vm" />
</Grid.DataContext>
<ListBox
x:Name="EditingArea"
ItemsSource="{Binding ColumnCollection, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:ListEditor}}}"
>
<ListBox.Resources>
<DataTemplate x:Key="TextTemplate">
<StackPanel>
<TextBlock Text="{Binding DisplayName}" />
<TextBox
local:BindingIndirector.BindingSourceContext="{Binding DataContext.CurrentEditing, ElementName=ControlContainer}"
local:BindingIndirector.BindingSource="{Binding PathName}"
local:BindingIndirector.BindingTarget="Text"
/>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="PickListTemplate" .. />
<DataTemplate x:Key="BooleanTemplate" ... />
</ListBox.Resources>
<ListBox.ItemTemplateSelector>
<local:DataTypeSelector
TextTemplate="{StaticResource TextTemplate}"
PickListTemplate="{StaticResource PickListTemplate}"
BooleanTemplate="{StaticResource BooleanTemplate}"
/>
</ListBox.ItemTemplateSelector>
</ListBox>
</Grid>
</UserControl>
&#34; CurrentEditing&#34;是各种ListBox
项正在编辑的ViewModel对象(来自ListBox
的每个ColumnCollection
项生成对象的不同属性的编辑器。)
希望AP的目的(在#34; TextTemplate&#34;中使用)是不言自明的(它们为Text
的{{1}}属性创建了一个绑定),但请注意虽然这里有三个都是必需的,但我希望至少TextBox
是可选的...这会产生问题:BindingSourceContext
不知道将设置多少个AP,所以它不知道何时创建绑定。因此,如果有足够的信息,每次更改属性时都会有一个go。如果还有更多,那么就会产生绑定错误。
答案 0 :(得分:2)
您可以在Binding上使用FallbackValue来抑制这些异常。例如:
<Grid Visibility="{Binding SomeProperty, FallbackValue=Collapsed}"/>