Yesterday, I have some ideas in my mind on how to give out ValidationRule errors after the values passes the converter, and I was trying to see if I can use Attached Properties for that.
When trying to do this, one of the things I’d like to do was set a control’s text to the Attached Property’s value and that’s when I realized that I don’t know the syntax to do that.
A quick Google search yields some results; someone asked the same question and the answer suggested using parentheses. The following example where the TextBox will display the row index the textbox is residing show how this works:
Bind to Grid.Row attached property
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBox Text="{Binding (Grid.Row),RelativeSource={RelativeSource Self}}"
Height="23" Grid.Column="0" Grid.Row="0" VerticalAlignment="Top" />
<TextBox Text="{Binding (Grid.Row),RelativeSource={RelativeSource Self}}"
Height="23" Grid.Column="0" Grid.Row="1" VerticalAlignment="Top" />
<TextBox Text="{Binding (Grid.Row),RelativeSource={RelativeSource Self}}"
Height="23" Grid.Column="0" Grid.Row="2" VerticalAlignment="Top" />
<TextBox Text="{Binding (Grid.Row),RelativeSource={RelativeSource Self}}"
Height="23" Grid.Column="0" Grid.Row="3" VerticalAlignment="Top"
Name=”textBox4” />
</Grid>
You can also see that I didn’t use Path=(Grid.Row), by default the first parameter is assigned to the Path property of the Binding class.
What about if we want to do it in code; it’s very similar:
Binding b = new Binding("(Grid.Row)");
b.Source = textBox4;
BindingOperations.SetBinding(textBox4, TextBox.TextProperty, b);
The binding path is the same; (Grid.Row) and the other important tidbit is that the source is the textbox itself. This is logical since when assigning a value to an Attached Property, you’re assigning that value for a given DependencyObject. In this case, we want the text to be set to the Grid.Row attached property for that particular textbox too.
Verifying that it works, now I continue on with my class that implements the Attached Property. However, since I need to refer to my class using XML namespace, how would the Path look? The path has to use the namespace too, as the sample below shows:
<Grid xmlns:local="clr-namespace:BindingToAttachedProperty">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBox Text="{Binding Path=(local:LocalClass.SomeValue),RelativeSource={RelativeSource Self}}"
local:LocalClass.SomeValue="1"
Height="23" Grid.Column="0" Grid.Row="0" Vertical.Alignment="Top" />
<TextBox Text="{Binding (local:LocalClass.SomeValue),RelativeSource={RelativeSource Self}}"
local:LocalClass.SomeValue="2"
Name="textBox2"
Height="23" Grid.Column="0" Grid.Row="1" VerticalAlignment="Top" />
</Grid>
I found a weird condition in WPF; you can see that for the first TextBox, the Binding has the Path=, while the second one doesn’t. The sample above works, the first textbox will display 1, and the second textbox will display 2. However, if you take out that Path= string, the example will not work; the BindingExpression specifies PathError. I believe this is because without the first Path=, WPF doesn’t know what is (local:LocalClass.SomeValue). By specifying Path=, WPF compiles it correctly, and the subsequent encounter of (local:LocalClass.SomeValue) in textBox2 results in a correct interpretation.
OK, the XAML works; how to do it in code? I tried all kinds of combination for the path, and could not get anything to work – it took me about 2 hours trying around and finally I got a solution.
Binding b = new Binding();
b.Path = new PropertyPath(LocalClass.SomeValueProperty);
b.Source = textBox2;
BindingOperations.SetBinding(textBox2, TextBox.TextProperty, b);
Most of the time, people will just create a new Binding instance, passing the string path in its constructor. I could not get this to work; all types of string values that I tried results in PathError. Finally, I took a good look at the Path property in the Binding class and found that one of the PropertyPath constructor accepts an object parameter that can be a DependencyProperty.
This would be my first ‘New Thing I Learned’ to document . I’m sure I will look at this information again in the future, and I hope other people can benefit from this.