I have a custom control that is used in a form, this control combines a label and a textbox. When the form is in read only mode the control is changed to show a label instead of the textbox. To do this, I have defined a Content Template for my control, as follows:
<Style TargetType="{x:Type Controls:FormFieldControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<AdornerDecorator Grid.Column="1"
Visibility="{Binding RelativeSource={RelativeSource AncestorType=Controls:FormFieldControl},
Path=IsReadOnly,
Converter={StaticResource BooleanToInverseVisibilityConverter},
Mode=OneWay}">
<ContentPresenter Name="ContentPresenter" />
</AdornerDecorator>
<AdornerDecorator Grid.Column="1"
Visibility="{Binding RelativeSource={RelativeSource AncestorType=FormFieldControl},
Path=IsReadOnly, Converter={StaticResource BooleanToVisibilityConverter},
Mode=OneWay}">
<ContentPresenter Name="ReadOnlyContentPresenter" />
</AdornerDecorator>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
ReadOnlyContentPresenter
's content is a label that's been applied the following style:
<Style x:Key="ReadOnlyFormFieldControl" TargetType="{x:Type Label}">
<Setter Property="Width" Value="400" />
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=Controls:FormFieldControl},
Path=ReadOnlyWidth,
Converter={StaticResource IsSetConverter}}"
Value="True">
<Setter Property="Width"
Value="{Binding RelativeSource={RelativeSource AncestorType=Controls:FormFieldControl}, Path=ReadOnlyWidth}" />
</DataTrigger>
</Style.Triggers>
</Style>
This works fine for about 80 controls, but when I add more controls to the form then the binding in the label style fails to find the source (error 4):
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'RelativeSource FindAncestor, AncestorType='MyProject.Controls.FormFieldControl', AncestorLevel='1''. BindingExpression:Path=ReadOnlyWidth; DataItem=null; target element is 'Label' (Name=''); target property is 'ReadOnlyWidth' (type 'Double?')
The same happens with the style applied to the editable part of the control. I think the issue is related to the amount of controls being processed, if I move the code of control no. 81 (failing) right above control no. 80 (working), the binding in control no. 81 works now, but the one in control no. 80 fails. I did this for several controls, and the behaviour is consistent.
I'm still not clear as to how the AdornerDecorator
works exactly, and while investigating I found this question that mentions a problem with having more than 144 adorners in an adornerlayer, so I removed the AdornerDecorator
's in my code only to find that the binding issue that was happening to controls no. 81 and over is now happening to all my FormFieldControl
's.
Finally, I decided to try with ContentControl
's instead of ContentPresenter
's in my control template. This works fine for all the controls in the form. I know that ContentPresenter
is a lightweight form of ContentControl
and that it's more suitable to use inside a content template than theContentControl
is. However, I don't quite understand why the bindings fail when using a ContentPresenter
and work when using a ContentControl
.
I also find it odd that only a limited set of controls work, and when not using the AdornerDecorator none of them will find the binding source.
Has anyone come across something similar? Is there something I'm doing wrong with the ContentPresenter
? Or if this is expected behaviour, could someone help me understand what's going on?
Any ideas are appreciated.