WPFマルチバインディングが失敗します。どうして?
-
22-07-2019 - |
質問
このマークアップがあります:
<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>
背後のコードでは、window_loadedメソッドに次の行があります。
DataContext = uiManager;
uiManagerは、IsConnectedおよびIsLoggedInという名前の2つのパブリックプロパティを持つUIManager型です。
Multibindingによって呼び出されるConverterの値の配列にはブール値が入力されず、DependencyProperty.UnsetValueの値があるため、このコードは起動時に失敗します。
以下のマークアップを使用(およびコンバーターのreturntypeを変更)すると機能します。
<GroupBox BorderThickness="2">
<GroupBox.BorderBrush>
<MultiBinding Converter="{StaticResource ConnectionAndLoggedInToBorderBrush}">
<Binding Path="IsConnected"/>
<Binding Path="IsLoggedIn"/>
</MultiBinding>
</GroupBox.BorderBrush>
コードビハインドのDataContextを介して設定されたバインディングは、最初の例では失敗しますが、2番目の例では機能するようです。なぜですか?
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
}
編集: 失敗したXAMLの変換方法(values [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;
}
作業中の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;
}
解決
この問題は、 MultiBinding
またはコンバーターとは関係ありません。 DependencyProperty.UnsetValue
は、バインディングに値がなかったことを意味します。実際、デバッグモードで実行すると、 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')
それでは、マークアップを少し単純化し、いくつかの診断を適用しましょう:
<GroupBox>
<GroupBox.BorderBrush>
<SolidColorBrush>
<SolidColorBrush.Color>
<Binding Path="GroupColor" PresentationTraceSources.TraceLevel="High"/>
</SolidColorBrush.Color>
</SolidColorBrush>
</GroupBox.BorderBrush>
</GroupBox>
添付の依存関係プロパティ PresentationTraceSources.TraceLevel
を適用すると、さらに出力が得られます。
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')
バインディングが DataContext
を見つけられず、バインディングが失敗することがわかります。コンテンツを初期化する前に DataContext
が設定されるようにウィンドウのコンストラクターを変更すると、バインディングが機能します:
public Window1()
{
DataContext = ...;
InitializeComponent();
}
他の場所でのバインディングの場合、これは重要ではないため、どちらが奇妙です。なぜそこで動作しないのかわからないため、回避策しか提供できません。たとえば、ブラシをバインディング付きのリソースとして作成します(そのリソースは 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>
UIManager
クラスが何らかの種類である場合、 MultiBinding
をドロップし、 DataContext
で前処理を行うことをお勧めします MVVM
ViewModel
。
他のヒント
私の理論。色は構造体(nullは不可)なので、SolidColorBrush.Color = nullは間違っています。 WPFはSolidColorBrushを作成できず、例外が発生します。
<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はオブジェクト(nullでも可)なので、GroupBox.BorderBrush = nullはOKです。
<GroupBox.BorderBrush>
<MultiBinding Converter="{StaticResource
ConnectionAndLoggedInToBorderBrush}">
<Binding Path="IsConnected"/>
<Binding Path="IsLoggedIn"/>
</MultiBinding>
</GroupBox.BorderBrush>
このSolidColorBrushはオブジェクトではなく、FACTORYです。必要な場合にのみインスタンス化され、その時点で既に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>
ちょうど2セント。
私の記事btwを読んで、奇妙なコンバータや奇妙なバインディングやアニメーションが必要な場合に役立つかもしれません。 http://www.codeproject.com/KB/WPF/BindingHub.aspx
このような理由により、MVVMの学習を検討することをお勧めします。このパターンは、モデルとバインディングを抽象化するのに役立ち、DPにそれほど依存する必要がなくなります。代わりに、ビューモデルの通知可能なプロパティに単純にバインドできます。
MVVMにはいくつかの優れた記事がありますので、Karl Shifflett、Josh Smith、Marlon Grech、Sacha Barberの作品を読むことから始めることをお勧めします。