none
Adorners visible for elements in collapsed expander RRS feed

  • Question

  • I'm having a problem where adorners that adorn elements in an expander are visible when the expander is collapsed (the element is no longer visible). This is pretty easily reproducible. Take any one of the SDK samples that use an adorner, and put the main part of a page in an expander - make sure to mark the expander as IsExpanded=True, otherwise most of them will throw errors (I used the AddRemoveAdorner sample to reproduce it). Is there something special that needs to be done for elements that use adorners and are in an expander? I tried using the AdornerDecorator, but that didn't help.
    Thursday, April 10, 2008 8:35 PM

Answers

  • Another method is using data binding as follows:
    public class MyAdorner : Adorner
    {
        public MyAdorner(UIElement adornedElement)
            : base(adornedElement)
        {
            Binding binding = new Binding("IsVisible");
            binding.Source = adornedElement;
            binding.Converter = new BooleanToVisibilityConverter();
            base.SetBinding(VisibilityProperty, binding);
        }
    }

    Hope this helps
    Monday, April 14, 2008 5:06 AM

All replies

  • The first idea that comes to mind would be to add and remove your Adorners as needed in the Expander's Expanded and Collapsed events.

     

    Hope this helps,

     

    Keith

    Thursday, April 10, 2008 11:10 PM
  • Another method is using data binding as follows:
    public class MyAdorner : Adorner
    {
        public MyAdorner(UIElement adornedElement)
            : base(adornedElement)
        {
            Binding binding = new Binding("IsVisible");
            binding.Source = adornedElement;
            binding.Converter = new BooleanToVisibilityConverter();
            base.SetBinding(VisibilityProperty, binding);
        }
    }

    Hope this helps
    Monday, April 14, 2008 5:06 AM
  • Why wouldn't this just be handled by the rendering engine instead of us having to do this programmatically? It works fine for TabItems, when you switch to another TabItem the Adorners for elements on the first TabItem go away, just not with Expanders.

     

    Monday, April 14, 2008 2:20 PM
  •  ADurkin wrote:

    Why wouldn't this just be handled by the rendering engine instead of us having to do this programmatically?


    Because the rendering system is not responsible for doing this type of things, the rendering system cannot erase a content if its IsVisible property is true or should it do?

     ADurkin wrote:

    It works fine for TabItems, when you switch to another TabItem the Adorners for elements on the first TabItem go away, just not with Expanders.


    Could you please provide a small, complete and ready-to-run example to illustrate the problem you are encountering?

    Thanks
    Tuesday, April 15, 2008 5:56 AM
  • Here's an example - using the AddRemoveAdorners from the SDK...

     

    Window1.xaml:

    Code Snippet

    <Window x:Class="SDKSample.Window1"

    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

    Height="600" Width="800" Loaded="WindowLoaded"

    Title="Add and Remove Adorners Sample"

    >

    <TabControl>

    <TabItem Header="Tab 1">

    <StackPanel>

    <AdornerDecorator>

    <Expander Header="Collapse me, adorner still visible" IsExpanded="True">

    <Grid>

    <Grid.ColumnDefinitions>

    <ColumnDefinition Width="*"/>

    <ColumnDefinition Width="200"/>

    </< FONT>Grid.ColumnDefinitions>

    <Grid.RowDefinitions>

    <RowDefinition Height="200"/>

    <RowDefinition Height="*"/>

    </< FONT>Grid.RowDefinitions>

    <TextBox Grid.Column="0" Grid.Row="0"

    Name="loneTextBox"

    Height="100" Width="200"

    HorizontalAlignment="Center" VerticalAlignment="Center"

    Text="TextBox"/>

    </< FONT>Grid>

    </< FONT>Expander>

    </< FONT>AdornerDecorator>

    <StackPanel>

    <TextBlock Text="hello"/>

    <TextBox/>

    <CheckBox/>

    </< FONT>StackPanel>

    </< FONT>StackPanel>

    </< FONT>TabItem>

    <TabItem Header="Tab 2">

    <StackPanel>

    <TextBlock Text="When on this tab, don't see the adorner from the first tab"/>

    <TextBox/>

    <CheckBox/>

    </< FONT>StackPanel>

    </< FONT>TabItem>

    </< FONT>TabControl>

    </< FONT>Window>

     

     

    Window1.xaml.cs:

    Code Snippet

    using System;

    using System.Windows;

    using System.Windows.Controls;

    using System.Windows.Data;

    using System.Windows.Documents;

    using System.Windows.Media;

    using System.Windows.Shapes;

     

    namespace SDKSample

    {

    public partial class Window1 : Window

    {

    public Window1()

    {

    InitializeComponent();

    }

    private void WindowLoaded(object sender, RoutedEventArgs e)

    {

    AdornerLayer alSingle = AdornerLayer.GetAdornerLayer(loneTextBox);

    alSingle.Add(new SimpleCircleAdorner(loneTextBox));

    }

    }

    // Adorners must subclass the abstract base class Adorner.

    public class SimpleCircleAdorner : Adorner

    {

    // Be sure to call the base class constructor.

    public SimpleCircleAdorner(UIElement adornedElement)

    : base(adornedElement)

    {

    // Any constructor implementation...

    }

    // A common way to implement an adorner's rendering behavior is to override the OnRender

    // method, which is called by the layout subsystem as part of a rendering pass.

    protected override void OnRender(DrawingContext drawingContext)

    {

    // Get a rectangle that represents the desired size of the rendered element

    // after the rendering pass. This will be used to draw at the corners of the

    // adorned element.

    Rect adornedElementRect = new Rect(this.AdornedElement.DesiredSize);

    // Some arbitrary drawing implements.

    SolidColorBrush renderBrush = new SolidColorBrush(Colors.Green);

    renderBrush.Opacity = 0.2;

    Pen renderPen = new Pen(new SolidColorBrush(Colors.Navy), 1.5);

    double renderRadius = 5.0;

    // Just draw a circle at each corner.

    drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.TopLeft, renderRadius, renderRadius);

    drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.TopRight, renderRadius, renderRadius);

    drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.BottomLeft, renderRadius, renderRadius);

    drawingContext.DrawEllipse(renderBrush, renderPen, adornedElementRect.BottomRight, renderRadius, renderRadius);

    }

    }

    }

     

     

    Collapse the expander - the adorners are still on top even though the controls aren't "visible".

    Switch Tabs, the adorners are not visible.

    I assume there is a difference between switching tabs and expanding/collapsing, but I would think the use cases should be the same - if you have a collapsed Expander, why would you want to see the adorners for the elements inside? Same as you wouldn't want to see the adorners for elements on a different TabItem.

     

    I did try both suggestions and got both working, Marco's is definitely easier to implement, instead of traversing the Visual Tree when the expander is expanded and collapsed.

    Tuesday, April 15, 2008 12:12 PM
  • Have you tried defining the AdornerDecorator inside of the Expander? I was having a similar problem with Elements I was collapsing and expanding but fixed it by defining the decorator so that it's parent element was getting collapsed. So instead of this:

    <StackPanel>

        <AdornerDecorator>

            <Expander Header="Collapse me, adorner still visible" IsExpanded="True">

                ...

            </Expander>

        </AdornerDecorator>

     <StackPanel>


    You would do this:

    <StackPanel>

        <Expander Header="Collapse me, adorner still visible" IsExpanded="True">

            <AdornerDecorator>

                ...

            </AdornerDecorator>

        </Expander>

     <StackPanel>

    I don't really have time to experiment right now, but my guess is that you could even just wrap the TextBox being adorned with an AdornerDecorator tag and achieve the same effect, it's just going to work up the tree until it finds an AdornerLayer to draw to. I don't know what the performance implications are of having multiple Adorner layers...

    Developer
    Monday, September 29, 2008 11:49 PM
  • Simple and elegant, thank you for this great solution :)
    Tuesday, October 14, 2014 6:39 PM