WPF Multibinding schlägt fehl. Warum?
-
22-07-2019 - |
Frage
Ich habe dieses Markup:
<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>
Im Code hinter ich diese Zeile in der Window_Loaded Methode habe:
DataContext = uiManager;
UIManager ist vom Typ UIManager, die zwei öffentliche Eigenschaften genannt IsConnected und IsLoggedIn hat.
Dieser Code nicht bei beim Start, da die Werte Array in dem Konverter, die von der mehrbindigen genannt wird mit booleans nicht gefüllt ist, aber einen Wert von DependencyProperty.UnsetValue.
Wenn ich das Markup unten (und den Rückgabetyp des Wandlers ändern) es funktioniert.
<GroupBox BorderThickness="2">
<GroupBox.BorderBrush>
<MultiBinding Converter="{StaticResource ConnectionAndLoggedInToBorderBrush}">
<Binding Path="IsConnected"/>
<Binding Path="IsLoggedIn"/>
</MultiBinding>
</GroupBox.BorderBrush>
Es scheint, dass die Bindung Satz durch den Datacontext in der Code-behind im ersten Beispiel ausfällt, funktioniert aber in dem zweiten. Warum?
Für Vollständigkeit unter der UIManager Klasse:
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: Das Umwandlungsverfahren für das fehlerhafte XAML (es nicht auf der Umstellung auf Bool von Werten [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;
}
für das Arbeits 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;
}
Lösung
Das Problem hat nichts mit einem MultiBinding
oder Ihren Konverter zu tun. DependencyProperty.UnsetValue
bedeutet, dass die Bindung keinen Wert hätte. Und in der Tat, wenn Sie im Debug-Modus ausführen können Sie Bindung Fehler im Output
Fenster:
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')
Also lassen Sie uns das Markup ein wenig vereinfachen und einige Diagnose anwenden:
<GroupBox>
<GroupBox.BorderBrush>
<SolidColorBrush>
<SolidColorBrush.Color>
<Binding Path="GroupColor" PresentationTraceSources.TraceLevel="High"/>
</SolidColorBrush.Color>
</SolidColorBrush>
</GroupBox.BorderBrush>
</GroupBox>
Sie die beigefügte Abhängigkeitseigenschaft PresentationTraceSources.TraceLevel
Anwendung ergibt etwas mehr Leistung:
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')
Wir sehen, dass die Bindung versagt keine DataContext
und die Bindung finden. Wenn ich die Konstrukteurs-Fenster ändern, so dass DataContext
vor der Initialisierung des Inhalts der Bindung ab Werk festgelegt:
public Window1()
{
DataContext = ...;
InitializeComponent();
}
Was da für Bindungen an anderen Orten seltsam ist dies keine Rolle spielt. Nicht sicher, warum es nicht, dass es funktioniert, damit ich nur Abhilfen anbieten kann. Was zum Beispiel funktioniert, ist die Bürste als Ressource mit den Bindungen zu schaffen (die Ressource auch lokal auf den GroupBox
sein könnte):
<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>
Ich würde vorschlagen, obwohl die MultiBinding
fallen zu lassen und eine Vorbearbeitung im DataContext
wenn Ihre UIManager
Klasse ist eine Art von MVVM
ViewModel
zu tun.
Andere Tipps
Meine Theorie. Die Farbe ist struct (kann nicht null sein), so SolidColorBrush.Color = null falsch ist. WPF können SolidColorBrush nicht erstellen, und Sie Ausnahme erhalten.
<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 ist Objekt (kann null sein), so GroupBox.BorderBrush = null ist OK.
<GroupBox.BorderBrush>
<MultiBinding Converter="{StaticResource
ConnectionAndLoggedInToBorderBrush}">
<Binding Path="IsConnected"/>
<Binding Path="IsLoggedIn"/>
</MultiBinding>
</GroupBox.BorderBrush>
Dieses SolidColorBrush ist kein Objekt, sondern eine Fabrik. Es wird nur instanziiert, wenn nötig, und an diesem Punkt haben Sie bereits Datacontext angebracht ist.
<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 cents.
Lesen Sie meinen Artikel könnte btw, nützlich sein, wenn Sie einige seltsame Bindungen oder Animationen mit seltsamen Converters müssen. http://www.codeproject.com/KB/WPF/BindingHub.aspx
Es ist aus Gründen wie diese, die Sie vielleicht lernen MVVM zu betrachten. Dieses Muster hilft Ihnen, zu abstrahieren das Modell und Bindungen, so dass Sie müssen nicht verlassen so stark auf DPs -. Sie einfach auf eine meldepflichtig Eigenschaft in einem View-Modell binden können stattdessen
Es gibt mehrere ausgezeichnete Artikel über MVVM, also würde ich Sie durch das Lesen der Werke von Karl Shifflett, Josh Smith, Marlon Grech und Sacha Barber beginnen vor.