2009-09-18 10 views
6

tengo este marcado:WPF MultiBinding Fails. ¿Por qué?

<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> 

En el código detrás tengo esta línea en el método window_loaded:

DataContext = uiManager; 

UIManager es de UIManager tipo que tiene dos propiedades públicas nombradas isConnected y IsLoggedIn.

Este código falla al inicio porque la matriz de valores en el convertidor que llama Multibinding no está llena de booleanos, pero tiene un valor de DependencyProperty.UnsetValue.

Cuando uso el marcado a continuación (y cambio el retornante del convertidor) funciona.

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

Parece que el conjunto de unión a través de la DataContext en el código falla atrás en el primer ejemplo, pero trabaja en el segundo. ¿Por qué?

Para completar por debajo de la clase 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: El método de conversión para el XAML no (se produce un error en la conversión a bool de valores [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; 
     } 

para el XAML de trabajo:

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

¿Puede adjuntar el código para su convertidor también? ¿Qué hay de malo en configurar GroupBox en un pincel en lugar de la propiedad SolidColorBrush.Color? – bendewey

+0

Agregué el método del convertidor. Necesito exponer la propiedad Color para poder implementar una ColorAninmation. Esto en sí mismo funciona con el primer XAML cuando elimino el enlace múltiple. – Dabblernl

Respuesta

15

El problema no tiene nada que ver con un MultiBinding o su convertidor. DependencyProperty.UnsetValue significa que el enlace no tiene valor. Y, en efecto si se ejecuta en modo de depuración se puede ver errores de enlace en la ventana 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') 

Así que vamos a simplificar el marcado un poco y se aplican algunos diagnósticos:

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

Aplicando la propiedad de dependencia adjunta PresentationTraceSources.TraceLevel reduce un poco más de potencia:

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') 

vemos que la unión no encontrar un DataContext y la unión falla Cuando cambio constructor de las ventanas para que DataContext se establece antes de inicializar el contenido de los trabajos de encuadernación:

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

lo cual es raro ya que para fijaciones en otros lugares, esto no importa. No estoy seguro de por qué no funciona allí, así que solo puedo ofrecer soluciones. Lo que funciona, por ejemplo, es crear el pincel como un recurso con las fijaciones (ese recurso también podría ser local en el 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> 

Yo sugeriría a pesar de dejar caer el MultiBinding y hacer un poco de pre-procesamiento en el DataContext si su clase UIManager es algún tipo de MVVMViewModel.

+0

Felicitaciones, configurar el DataContext en el constructor en el código de detrás hizo el truco. – Dabblernl

0

Es por razones como esta que es posible que desee considerar el aprendizaje de MVVM. Este patrón le ayuda a abstraer el modelo y los enlaces para que no tenga que depender demasiado de los DP, sino que simplemente puede vincularse a una propiedad de notificación obligatoria en un modelo de visualización.

Hay varios artículos excelentes sobre MVVM, así que sugiero que empiece leyendo las obras de Karl Shifflett, Josh Smith, Marlon Grech y Sacha Barber.

+2

Él ya está vinculando a una clase INotifyPropertyChanged. – gix

1

Mi teoría. Color es struct (no puede ser nulo), por lo que SolidColorBrush.Color = null es incorrecto. WPF no puede crear SolidColorBrush, y obtienes una excepción.

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

BorderBrush es objeto (puede ser nulo), de modo GroupBox.BorderBrush = null es OK.

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

Este SolidColorBrush no es un objeto sino una FÁBRICA. Se crea una instancia solo cuando es necesario, y en ese punto ya ha adjuntado 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> 

Just my 2 centavos.

Leer mi artículo, por cierto, podría ser útil si necesita algunas vinculaciones extrañas o animaciones con convertidores extraños. http://www.codeproject.com/KB/WPF/BindingHub.aspx