2009-09-18 12 views
6

Je cette Markup:WPF MultiBinding échoue. Pourquoi?

<GroupBox BorderThickness="2"> 
    <GroupBox.BorderBrush> 
     <SolidColorBrush x:Name="Border"> 
      <SolidColorBrush.Color> 
       <MultiBinding Converter="{StaticResource ConnectionAndLoggedInToBorderBrush}"> 
        <Binding Path="IsConnected"/> 
        <Binding Path="IsLoggedIn"/> 
       </MultiBinding> 
      </SolidColorBrush.Color> 
     </SolidColorBrush> 
    </GroupBox.BorderBrush> 

Dans le code derrière je cette ligne dans la méthode Window_Loaded:

DataContext = uiManager; 

UIManager est de type UIManager qui a deux propriétés publiques nommées IsConnected et IsLoggedIn.

Ce code échoue au démarrage car le tableau de valeurs dans le convertisseur appelé par Multibinding n'est pas rempli avec des booléens mais avec la valeur DependencyProperty.UnsetValue. Lorsque j'utilise le balisage ci-dessous (et change le type de retour du convertisseur), cela fonctionne.

<GroupBox BorderThickness="2"> 
    <GroupBox.BorderBrush> 
     <MultiBinding Converter="{StaticResource ConnectionAndLoggedInToBorderBrush}"> 
       <Binding Path="IsConnected"/> 
       <Binding Path="IsLoggedIn"/> 
     </MultiBinding> 
    </GroupBox.BorderBrush> 

Il semble que l'ensemble de liaison à travers le DataContext dans le code ne derrière dans le premier exemple, mais travaille dans le second. Pourquoi?

Pour être complet au-dessous de la classe UIManager:

public class UIManager:IUIManager 
    { 

     #region Implementation of IUIManager 

     private const string IsLoggedInProperty = "IsLoggedIn"; 
     private bool loggedIn; 
     private readonly object loggedInLock = new object(); 
     public bool IsLoggedIn 
     { 
      get 
      { 
       lock (loggedInLock) 
       { 
        return loggedIn; 
       } 
      } 
      set 
      { 
       lock (loggedInLock) 
       { 
        if(value==loggedIn)return; 
        loggedIn = value; 
        OnPropertyChanged(IsLoggedInProperty); 
       } 
      } 
     } 

     private void OnPropertyChanged(string property) 
     { 
      if(PropertyChanged!=null)PropertyChanged(this,new PropertyChangedEventArgs(property)); 
     } 

     private const string IsConnectedProperty = "IsConnected"; 
     private bool isConnected; 
     private object isConnectedLock = new object(); 
     public bool IsConnected 
     { 
      get 
      { 
       lock (isConnectedLock) 
       { 
        return isConnected; 
       } 
      } 
      set 
      { 
       lock (isConnectedLock) 
       { 
        if(value==isConnected)return; 
        isConnected = value; 
        OnPropertyChanged(IsConnectedProperty); 
       } 
      } 
     } 

     #endregion 

     #region Implementation of INotifyPropertyChanged 

     public event PropertyChangedEventHandler PropertyChanged; 

     #endregion 
    } 

EDIT: Le procédé de conversion de l'XAML défaut (il ne parvient pas à la conversion à bool de valeurs [0]:

public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
     { 
      var is_connected = (bool) values[0]; 
      var is_loggedin = (bool) values[1]; 
      return is_loggedin 
         ? is_connected 
          ? Colors.YellowGreen 
          : Colors.Red 
         : Colors.Gray; 
     } 

pour le fonctionnement XAML:

public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) 
     { 
      var is_connected = (bool) values[0]; 
      var is_loggedin = (bool) values[1]; 
      return is_loggedin 
         ? is_connected 
          ? Brushes.YellowGreen 
          : Brushes.Red 
         : Brushes.Gray; 
     } 
+0

Pouvez-vous également joindre le code de votre convertisseur? Quel est le problème avec la définition de GroupBox à un pinceau au lieu de la propriété SolidColorBrush.Color? – bendewey

+0

J'ai ajouté la méthode du convertisseur. J'ai besoin d'exposer la propriété Color pour pouvoir implémenter une ColorAninmation. Cela fonctionne en soi avec le premier XAML lorsque je supprime le MultiBinding. – Dabblernl

Répondre

15

Le problème n'a rien à voir avec un MultiBinding ou votre convertisseur. DependencyProperty.UnsetValue signifie que la liaison n'a aucune valeur. Et en effet, si vous exécutez en mode débogage, vous pouvez voir des erreurs de liaison dans la fenêtre Output:

System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=IsConnected; DataItem=null; target element is 'SolidColorBrush' (HashCode=17654054); target property is 'Color' (type 'Color') 
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=IsLoggedIn; DataItem=null; target element is 'SolidColorBrush' (HashCode=17654054); target property is 'Color' (type 'Color') 

Alors simplifions le balisage un peu et appliquez des diagnostics:

<GroupBox> 
    <GroupBox.BorderBrush> 
     <SolidColorBrush> 
      <SolidColorBrush.Color> 
       <Binding Path="GroupColor" PresentationTraceSources.TraceLevel="High"/> 
      </SolidColorBrush.Color> 
     </SolidColorBrush> 
    </GroupBox.BorderBrush> 
</GroupBox> 

Application de la propriété de dépendance attachée PresentationTraceSources.TraceLevel donne un peu plus de sortie:

System.Windows.Data Warning: 52 : Created BindingExpression (hash=17654054) for Binding (hash=44624228) 
System.Windows.Data Warning: 54 : Path: 'GroupColor' 
System.Windows.Data Warning: 56 : BindingExpression (hash=17654054): Default mode resolved to OneWay 
System.Windows.Data Warning: 57 : BindingExpression (hash=17654054): Default update trigger resolved to PropertyChanged 
System.Windows.Data Warning: 58 : BindingExpression (hash=17654054): Attach to System.Windows.Media.SolidColorBrush.Color (hash=52727599) 
System.Windows.Data Warning: 60 : BindingExpression (hash=17654054): Use Framework mentor <null> 
System.Windows.Data Warning: 63 : BindingExpression (hash=17654054): Resolving source 
System.Windows.Data Warning: 65 : BindingExpression (hash=17654054): Framework mentor not found 
System.Windows.Data Warning: 61 : BindingExpression (hash=17654054): Resolve source deferred 
System.Windows.Data Warning: 91 : BindingExpression (hash=17654054): Got InheritanceContextChanged event from SolidColorBrush (hash=52727599) 
System.Windows.Data Warning: 63 : BindingExpression (hash=17654054): Resolving source 
System.Windows.Data Warning: 66 : BindingExpression (hash=17654054): Found data context element: GroupBox (hash=51393439) (OK) 
System.Windows.Data Warning: 67 : BindingExpression (hash=17654054): DataContext is null 
System.Windows.Data Warning: 91 : BindingExpression (hash=17654054): Got InheritanceContextChanged event from SolidColorBrush (hash=52727599) 
System.Windows.Data Warning: 63 : BindingExpression (hash=17654054): Resolving source 
System.Windows.Data Warning: 65 : BindingExpression (hash=17654054): Framework mentor not found 
System.Windows.Data Warning: 63 : BindingExpression (hash=17654054): Resolving source 
System.Windows.Data Warning: 65 : BindingExpression (hash=17654054): Framework mentor not found 
System.Windows.Data Warning: 63 : BindingExpression (hash=17654054): Resolving source (last chance) 
System.Windows.Data Warning: 65 : BindingExpression (hash=17654054): Framework mentor not found 
System.Windows.Data Error: 2 : Cannot find governing FrameworkElement or FrameworkContentElement for target element. BindingExpression:Path=GroupColor; DataItem=null; target element is 'SolidColorBrush' (HashCode=52727599); target property is 'Color' (type 'Color') 

On voit que la liaison ne trouve pas DataContext et la liaison échoue. Quand je change le constructeur de fenêtres de sorte que DataContext est réglé avant d'initialiser le contenu des travaux de liaison:

public Window1() 
{ 
    DataContext = ...; 
    InitializeComponent(); 
} 

Ce qui est bizarre puisque pour les liaisons dans d'autres endroits, cela ne fait rien. Je ne sais pas pourquoi cela ne fonctionne pas là-bas, je ne peux que proposer des solutions de contournement. Ce qui fonctionne par exemple, est la création de la brosse comme une ressource avec les fixations (cette ressource pourrait également être locale au GroupBox):

<GroupBox BorderBrush="{DynamicResource resbrush}"> 
    <GroupBox.Resources> 
     <SolidColorBrush x:Key="resbrush"> 
      <SolidColorBrush.Color> 
       <MultiBinding Converter="{StaticResource ConnectionAndLoggedInToBorderBrush}"> 
        <Binding Path="IsConnected"/> 
        <Binding Path="IsLoggedIn"/> 
       </MultiBinding> 
      </SolidColorBrush.Color> 
     </SolidColorBrush> 
    </GroupBox.Resources> 
</GroupBox> 

je suggère que de laisser tomber le MultiBinding et de faire des pré-traitement dans le DataContext si votre classe UIManager est une sorte de MVVMViewModel.

+0

Kudos, en définissant le DataContext dans le constructeur dans le code derrière a fait l'affaire. – Dabblernl

0

Pour des raisons comme celles-ci, vous pourriez envisager d'apprendre MVVM. Ce modèle vous aide à abstraire le modèle et les liaisons de sorte que vous n'ayez pas à compter si fortement sur les DP - vous pouvez simplement lier à une propriété notifiable dans un modèle de vue à la place.

Il existe plusieurs excellents articles sur MVVM, donc je vous suggère de commencer par lire les œuvres de Karl Shifflett, Josh Smith, Marlon Grech et Sacha Barber.

+2

Il est déjà lié à une classe INotifyPropertyChanged. – gix

1

Ma théorie. La couleur est struct (ne peut pas être nulle), donc SolidColorBrush.Color = null est faux. WPF ne peut pas créer SolidColorBrush et vous obtenez une exception. BorderBrush est un objet (peut être nul), donc GroupBox.BorderBrush = null est OK.

<GroupBox.BorderBrush> 
     <MultiBinding Converter="{StaticResource 
        ConnectionAndLoggedInToBorderBrush}"> 
      <Binding Path="IsConnected"/> 
      <Binding Path="IsLoggedIn"/> 
     </MultiBinding> 
</GroupBox.BorderBrush> 

Ce SolidColorBrush n'est pas un objet mais une USINE. Il est instancié uniquement en cas de besoin, et à ce stade, vous avez déjà attaché DataContext.

<GroupBox.Resources> 
     <SolidColorBrush x:Key="resbrush"> 
      <SolidColorBrush.Color> 
       <MultiBinding Converter="{StaticResource 
           ConnectionAndLoggedInToBorderBrush}"> 
        <Binding Path="IsConnected"/> 
        <Binding Path="IsLoggedIn"/> 
       </MultiBinding> 
      </SolidColorBrush.Color> 
     </SolidColorBrush> 
</GroupBox.Resources> 

Juste mes 2 cents.

Lire mon article, btw, pourrait être utile si vous avez besoin de liaisons bizarres ou d'animations avec des convertisseurs bizarres. http://www.codeproject.com/KB/WPF/BindingHub.aspx

Questions connexes