我創建了一些附加屬性以允許間接綁定(我的意思是綁定到名稱由附加屬性給出,而不是在XAML中指定爲文字的值)。在部分構造的對象上綁定錯誤
一些AP是可選的(例如覆蓋DataContext
否則會生效),這意味着我在嘗試創建綁定時沒有設置所有AP(因爲在PropertyChangedCallback
I不知道其他人是否會被設置)。
結果是,綁定可以創建多次,有時不成功,這導致綁定錯誤,「不美觀」,因爲想要一個更好的單詞。
有沒有一種方法可以抑制綁定錯誤,直到一個元素的所有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個附加屬性,也包含一個方法,「 BindingChanged()「,這是所有三個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>
「CurrentEditing」是視圖模型對象的各種ListBox
項編輯(從ColumnCollection
每個ListBox
項產生針對該對象的一個不同的屬性的編輯器)。
希望AP的用途(這裏用在「TextTemplate」中)是不言自明的(他們創建了TextBox
的Text
屬性的綁定),但請注意,儘管這三個都是必要的,但我希望在至少BindingSourceContext
是可選的......並且這會產生問題:BindingChanged()
不知道將設置多少接入點,因此它不知道何時創建綁定。因此,如果有足夠的信息來改變財產,那麼每次財產變更時都會有所變化。如果還有更多,則會產生綁定錯誤。
你是什麼意思「難看」,這些綁定錯誤只是在VSs的輸出窗口,他們打擾你?除此之外,你可以展示一些你想要做什麼的代碼,以及錯誤是什麼? –
是的,我的意思是VS輸出窗口。在我的經驗中,錯誤通常意味着一個綁定被錯誤地指定了,我真的不想讓我建立的'UserControl'生成它們(當前正在執行的)並以誤報污染輸出。錯誤是'System.Windows.Data Error:40:BindingExpression path error'並且在這種情況下發生,因爲Binding引擎試圖在繼承的'DataContext'上找到路徑(錯誤地 - 它不在那裏,另一個AP設置'Binding.Source'來解決這個問題)。 –
好的,你能告訴我們一些代碼嗎? –