- Find all properties with an explicit Bindable(false) attribute
- Find all bindings
- Find all bindings whose targets are in the list from 1.
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:FindNonBindableBindings"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
mc:Ignorable="d"
Title="Find NonBindable Bindings" Height="450" Width="800">
<StackPanel>
<TextBlock Text="{Binding BindableProperty}"/>
<TextBlock Text="{Binding NonBindableProperty}"/>
</StackPanel>
</Window>
Let's start the code-behind with just the properties and the constructor.
namespace FindNonBindableBindings
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string _BindableProperty;
get { return _BindableProperty; }
[Bindable(false)]
[Bindable(false)]
public MainWindow()
InitializeComponent();
BindableProperty = "Hello";
}
if (!Object.Equals(storage, value))
storage = value;
if (PropertyChanged != null)
}
If you run this you can see that modifying NonBindableProperty after InitializeComponent does not update the textblock. Even though we marked the property as Binding(False), WPF did nothing to let us know we had bound to it.
Let's start by creating a simple class to hold source and target details about a binding so we can generate more useful error messages. Add this class directly in the FindNonBindableBindings namespace. As the original problem was caused by multiple identically named properties, we have to track the DataContext of the target properties.
{
public String TargetName { get; set; }
Now we can add some local variables to the constructor.
List<PropertyInfo> NonBindableProperties;
List<String> Errors;
{
List<PropertyInfo> NonBindableProperties = new List<PropertyInfo>();
foreach (PropertyInfo PI in fe.GetType().GetProperties())
{
BindableAttribute BA = PI.GetCustomAttribute<BindableAttribute>(true);
if (BA != null && !BA.Bindable)
{
NonBindableProperties.Add(PI);
}
}
return NonBindableProperties;
}
List<BindingInfo> Bindings = new List<BindingInfo>();
foreach (MarkupProperty mp in markupObject.Properties)
if (mp.DependencyProperty != null)
BindingBase b = BindingOperations.GetBindingBase(fe, mp.DependencyProperty);
}
}
foreach (object child in LogicalTreeHelper.GetChildren(fe))
return Bindings;
Step 3 compares the results of steps 1 and 2, returning a list of errors.
private List<String> CheckForNonBindableBindings(List<PropertyInfo> NonBindableProperties, List<BindingInfo> Bindings)
List<String> Errors = new List<string>();
foreach (PropertyInfo PI in NonBindableProperties)
{
BindingInfo binding = Bindings.FirstOrDefault(b => b.TargetName == PI.Name && b.DataContextName == PI.DeclaringType.FullName);
Errors.Add(String.Format("Non-Bindable property '{3}.{0}' is the target of binding from the {1} property of a {2} element",
return Errors;
Lastly we tie it all together with a few lines at the end of the constructor.
Bindings = FindAllBindings(this);
Errors = CheckForNonBindableBindings(NonBindableProperties, Bindings);
MessageBox.Show(string.Join(Environment.NewLine, Errors));
Let's run the application again. Now we see a dialog box telling us we bound to a property we didn't intend to use for binding.
No comments:
Post a Comment