Adorners and ScrollViewer don't get along
-
I am using Adorners to show input errors on my textbox controls, and those controls are hosted within a ScrollViewer. For some reason, my adorner UI is clipped when it tries to draw outside the ScrollViewer. I'm not the first to have this problem http://social.msdn.microsoft.com/forums/en-US/wpf/thread/e060205e-5bfc-4f21-bf80-dfa55c44eb8a However, the posted solution does not work for me... Has anyone else run into this problem? Here's the XAML from my example project: <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ScrollViewer}"> <Grid x:Name="Grid" Background="{TemplateBinding Background}"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Rectangle x:Name="Corner" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Grid.Column="1" Grid.Row="1"/> <AdornerDecorator Grid.Column="0" Grid.Row="0"> <ScrollContentPresenter x:Name="PART_ScrollContentPresenter" Margin="{TemplateBinding Padding}" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" CanVerticallyScroll="False"/> </AdornerDecorator> <ScrollBar x:Name="PART_VerticalScrollBar" Cursor="Arrow" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Grid.Column="1" Grid.Row="0" ViewportSize="{TemplateBinding ViewportHeight}" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Value="{Binding Path=VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" AutomationProperties.AutomationId="VerticalScrollBar"/> <ScrollBar x:Name="PART_HorizontalScrollBar" Cursor="Arrow" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Grid.Column="0" Grid.Row="1" Orientation="Horizontal" ViewportSize="{TemplateBinding ViewportWidth}" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Value="{Binding Path=HorizontalOffset, Mode=OneWay, Re</x-turndown>
-
I am using Adorners to show input errors on my textbox controls, and those controls are hosted within a ScrollViewer. For some reason, my adorner UI is clipped when it tries to draw outside the ScrollViewer. I'm not the first to have this problem http://social.msdn.microsoft.com/forums/en-US/wpf/thread/e060205e-5bfc-4f21-bf80-dfa55c44eb8a However, the posted solution does not work for me... Has anyone else run into this problem? Here's the XAML from my example project: <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ScrollViewer}"> <Grid x:Name="Grid" Background="{TemplateBinding Background}"> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Rectangle x:Name="Corner" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Grid.Column="1" Grid.Row="1"/> <AdornerDecorator Grid.Column="0" Grid.Row="0"> <ScrollContentPresenter x:Name="PART_ScrollContentPresenter" Margin="{TemplateBinding Padding}" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" CanVerticallyScroll="False"/> </AdornerDecorator> <ScrollBar x:Name="PART_VerticalScrollBar" Cursor="Arrow" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Grid.Column="1" Grid.Row="0" ViewportSize="{TemplateBinding ViewportHeight}" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Value="{Binding Path=VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" AutomationProperties.AutomationId="VerticalScrollBar"/> <ScrollBar x:Name="PART_HorizontalScrollBar" Cursor="Arrow" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Grid.Column="0" Grid.Row="1" Orientation="Horizontal" ViewportSize="{TemplateBinding ViewportWidth}" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Value="{Binding Path=HorizontalOffset, Mode=OneWay, Re</x-turndown>
Well, yeah, why wouldn't it? You aren't giving it anything bigger to draw on. The ScrollViewer is at the top.
-
Well, yeah, why wouldn't it? You aren't giving it anything bigger to draw on. The ScrollViewer is at the top.
Sorry if I'm not understanding. There is room outside the ScrollViewer for the adorner to draw on. Also, I don't have this problem with any other container type. If I host the textbox inside a Canvas or Grid, the adorner is not clipped and draws outside the container.
-
Sorry if I'm not understanding. There is room outside the ScrollViewer for the adorner to draw on. Also, I don't have this problem with any other container type. If I host the textbox inside a Canvas or Grid, the adorner is not clipped and draws outside the container.
GetAdornerLayer() returns the FIRST AdornerLayer it finds (from the bottom up). HOWEVER (and I neglected to mention this before :), sorry), in some situations it will find one lower then you expect. An AdornerDecorator is one situation, but, tee-hee... a ScrollViewer is another special case :). ScrollContentPresenter is another control that'll stop the adorner layer search. Here is GetAdornerLayer(): public static AdornerLayer GetAdornerLayer(Visual visual) { if (visual == null) { throw new ArgumentNullException("visual"); } for (Visual visual2 = VisualTreeHelper.GetParent(visual) as Visual; visual2 != null; visual2 = VisualTreeHelper.GetParent(visual2) as Visual) { if (visual2 is AdornerDecorator) { return ((AdornerDecorator) visual2).AdornerLayer; } if (visual2 is ScrollContentPresenter) { return ((ScrollContentPresenter) visual2).AdornerLayer; } } return null; } As you can see, it stops once it hits the ScrollContentPresenter :). If you want a larger adorner layer, you'd need to call GetAdornerLayer() on a Visual above the ScrollViewer.
-
GetAdornerLayer() returns the FIRST AdornerLayer it finds (from the bottom up). HOWEVER (and I neglected to mention this before :), sorry), in some situations it will find one lower then you expect. An AdornerDecorator is one situation, but, tee-hee... a ScrollViewer is another special case :). ScrollContentPresenter is another control that'll stop the adorner layer search. Here is GetAdornerLayer(): public static AdornerLayer GetAdornerLayer(Visual visual) { if (visual == null) { throw new ArgumentNullException("visual"); } for (Visual visual2 = VisualTreeHelper.GetParent(visual) as Visual; visual2 != null; visual2 = VisualTreeHelper.GetParent(visual2) as Visual) { if (visual2 is AdornerDecorator) { return ((AdornerDecorator) visual2).AdornerLayer; } if (visual2 is ScrollContentPresenter) { return ((ScrollContentPresenter) visual2).AdornerLayer; } } return null; } As you can see, it stops once it hits the ScrollContentPresenter :). If you want a larger adorner layer, you'd need to call GetAdornerLayer() on a Visual above the ScrollViewer.
You're a genius!! I rewrote the GetAdornerLayer function to be this: public AdornerLayer GetAdornerLayer(Visual visual) { if (visual == null) { throw new ArgumentNullException("visual"); } for (Visual visual2 = VisualTreeHelper.GetParent(visual) as Visual; visual2 != null; visual2 = VisualTreeHelper.GetParent(visual2) as Visual) { if (visual2 is AdornerDecorator) { return ((AdornerDecorator)visual2).AdornerLayer; } //if (visual2 is ScrollContentPresenter) //{ //return ((ScrollContentPresenter)visual2).AdornerLayer; //} } return null; } Works like a charm!! Thanks for your help. Aaron
-
You're a genius!! I rewrote the GetAdornerLayer function to be this: public AdornerLayer GetAdornerLayer(Visual visual) { if (visual == null) { throw new ArgumentNullException("visual"); } for (Visual visual2 = VisualTreeHelper.GetParent(visual) as Visual; visual2 != null; visual2 = VisualTreeHelper.GetParent(visual2) as Visual) { if (visual2 is AdornerDecorator) { return ((AdornerDecorator)visual2).AdornerLayer; } //if (visual2 is ScrollContentPresenter) //{ //return ((ScrollContentPresenter)visual2).AdornerLayer; //} } return null; } Works like a charm!! Thanks for your help. Aaron
Not what I would have done, but whatever works :) You could have just called GetAdornerLayer() on the ScrollViewer instead of the TextBlock.