2016-07-08 4 views
0

バインドの間接を許可するために、いくつかの添付プロパティを作成しました。これは、XAMLでリテラルとして指定するのではなく、添付プロパティによって名前が付けられた値にバインドすることを意味します。部分的に構築されたオブジェクトのバインディングエラー

一部のAPはオプションです(たとえば、DataContextを無効にするものなど)。これは、すべてのAPが設定されていない場合にバインディングを作成しようとしていることを意味します(PropertyChangedCallback他の人がセットになるかどうかは分かりません)。

結論は、バインディングを複数回作成することができ、時には失敗することがあり、その結果、より良い単語が必要なために「見苦しい」バインディングエラーが発生することです。

要素のすべてのAPが割り当てられるまでバインディングエラーを抑制する方法がありますか、それともこの要素に含まれるクラスの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() "は3つのAPすべてに対してpropertyChangedCallbackです。バインディングの作成を試みるのに十分な情報が与えられている場合は、APが最初に作成するために使用されていた以前のバインディングを破棄します。

解決策ではないことは、バインディングが最初に成功するか、バインディングエンジンによって生成されたエラーをキャッチするか(それを行うことはできますか?)です。バインディングエラーが表示されるのを抑制しないようにすることは難しいかもしれません(エンドユーザーがダフ情報などを提供しているためです)。ここ

一ユースケースの例である:

<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項目を編集しているオブジェクトの異なるプロパティのエディタを生成します)。

「TextTemplate」でここで使用されているAPの目的は、わかりやすく(彼らはプロパティのバインディングを作成しますが)、ここで3つは必要ですが、少なくともBindingSourceContextをオプションにする必要があります。これによって問題が発生します。BindingChanged()は、設定するAPの数がわからないため、バインディングをいつ作成するのか分かりません。その結果、十分な情報があればプロパティが変更されるたびに変更が行われます。もっと来る場合は、バインディングエラーが生成されます。

+0

「見苦しい」とはどういう意味ですか、これらのバインディングエラーはVSsの出力ウィンドウにあり、気になるのですか?それ以外に、あなたがやろうとしていることのいくつかのコードと正確に何が違うのかを示すことができますか? –

+0

はい、私はVS出力ウィンドウを意味します。私の経験上のエラーでは、通常、バインディングが間違って指定されていることを意味しています。実際にビルドしている 'UserControl'をコース(現在行っていること)として生成し、誤検出で出力を汚染します。エラーは 'System.Windows.Data Error:40:BindingExpression path error'であり、Bindingエンジンが継承された' DataContext'上のパスを見つけようとしているため(この場合は間違っています - これを修正するために 'Binding.Source'を設定します)。 –

+0

よろしくお願いします。コードを教えてください。 –

答えて

2

バインディングでFallbackValueを使用すると、これらの例外を抑制できます。例:

<Grid Visibility="{Binding SomeProperty, FallbackValue=Collapsed}"/> 
+0

Timに感謝しますが、 'Binding'sはコード内に作成され、システムは一般化されて任意の要素の任意のDPに対するバインディングを作成します。コードでは、エラーを回避するために適切なフォールバック値(またはそのタイプ)が何であるかわからず、使用する時点ですべての場合に有効な値を指定することは可能ではないとも考えません。 –

関連する問題