How to place adorner over two child controls?

Mar 29, 2011 at 8:29 PM
Edited Mar 29, 2011 at 8:30 PM

I have a two controls: a date picker, and a time up/down control. When the user enters a date that is before DateTime.Now or if the user enters a date of today but the Time is before the current time, I want one adorner to enclose both controls. This is because the user can either change the date to later day or the time to a later time and there would be no error.  How do I get one adorner to have two child controls?  Ideally, I would like the adorner to be on the enclosing stackpanel.  THANKS!!!!

   <StackPanel
             Orientation="Horizontal"             
             Grid.Row="7"
             Grid.Column="5">

            <DatePicker            
                    x:Name="EndDate"   
                    KeyboardNavigation.TabIndex="0"   
                    Text="{Binding Path=EndDate, ValidatesOnDataErrors=True, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"
                    DisplayDate="{Binding Path=EndDate, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"  
                    HorizontalAlignment="Left"
                    PreviewMouseUp="DateEntryPreviewMouseUp"
                    Width="Auto" 
                    Margin="0,3,0,3"            
                    Height="Auto"            
                    />
            <CommonUtilities:TimeUpDownControl
                Margin="8,2,0,2"
                Time="{Binding Path=EndTime, ValidatesOnDataErrors=True, Mode=TwoWay, UpdateSourceTrigger=LostFocus}"                               
                />
         
        </StackPanel>
Apr 4, 2011 at 1:48 AM

Hi,

May I first refer you to the following URL: http://marlongrech.wordpress.com/2008/02/28/wpf-overlays-or-better-adorner/ , that may help you to build a Customer Adorner using the decorator class, parent and child controls. Alternatively you could create a generic class such as GenAdoner<T> and include in your code a list collection of controls, and add them to the layer as child controls.

As an aside, would you not be better off customizing your existing controls with a WPF Custom Template. As you probably know, a WPF control template allows you to change the composition of your controls; and being lookless, a.k.a. A control’s visual appearance is separated from its behaviour; you would be able to produce a single customised control that housed both of your controls. Add the necessary triggers (i.e. events for your controls) and style resources as required. If you are not in the business of restyling your controls then use the controls standard templates.

Using Adorner Layering in your scenario would be beneficial if you used <Binding.ValidationRules> for your controls and wanted to provide some visual indication over the controls to warn or inform your users.

I presume you are getting a NULL error or something along those lines. The AdornerLayer is created using the Decorator and ScrollContentPresenter classes. Both these items need to be in your visual tree that parents your control. If neither is present then your controls will not have a related AdonerLayer, consequently NULL error. Simply adding an AdonerDecorator to your controls will maintain your Adoners on top of your controls. As the default ControlTemplate for the Window Area includes an AdornerDecorator, if you add your controls to the Window area, then you will get the AdornerLayer also. The point I am making is each control will have its own Adorner Layer and the Z-Order does not appear to be alterable by coding unless controls are specifically ordered using a Collection manually and add accordingly. But it may be possible to alter the z-order on a canvas directly, have to look at that.  As I suggested early the Control Template route looks preferable for your scenario.

However the following MSDN Reference might be useful to you:

AdornerDecorator can contain only one child element. That element can contain multiple elements that can be adorned. The AdornerDecorator specifies the position of the AdornerLayer in the visual tree. It is typically used in a ControlTemplate for a control that might host Adorner objects. For example, the ControlTemplate of a Windowcontains an AdornerDecorator so that the child elements of the window can be adorned. The GetAdornerLayermethod returns null if you pass in an element that does not have an AdornerDecorator as an ancestor in its visual tree.

 Examples

The following example creates a ControlTemplate for a Window. The example adds a ContentPresenter as the child element of an AdornerDecorator. The logical child elements of the window can be adorned because theAdornerDecorator is in their visual tree.

 

XAML

 

<Style x:Key="{x:Type Window}" TargetType="{x:Type Window}">
 <Setter Property="SnapsToDevicePixels" Value="true"/>
 <Setter Property="Template">
   <Setter.Value>
     <ControlTemplate TargetType="{x:Type Window}">
      <Grid Background="{StaticResource WindowBackgroundBrush}">
         <AdornerDecorator>
           <ContentPresenter/>
         </AdornerDecorator>
         <ResizeGrip x:Name="WindowResizeGrip"
           HorizontalAlignment="Right"
           VerticalAlignment="Bottom"
           Visibility="Collapsed" 
           IsTabStop="false"/>
       </Grid>
       <ControlTemplate.Triggers>
         <Trigger Property="ResizeMode" Value="CanResizeWithGrip">
           <Setter TargetName="WindowResizeGrip" Property="Visibility" Value="Visible"/>
         </Trigger>
       </ControlTemplate.Triggers>
     </ControlTemplate>
   </Setter.Value>
 </Setter>
</Style>

Anyway, hope this helps
Quen Wilson - IOW,UK

 

Apr 4, 2011 at 8:20 PM

Awesome response! Thank you for your time and giving me the options.  Yes, a custom template is my best option. But I sincerely appreciate all of the input ... and it all helps to get a better understanding of WPF's power!

Apr 5, 2011 at 12:24 AM

Glad it was of use to you. There's always that chance one has missed the point. Quen Wilson - IOW, UK