As I mentioned back in January, I created a collapsible section for use in a flow document. In my case, I was removing a treeView and replacing it with indented paragraphs in a flow document (see the previous post as to why). Some of the data that was now shown to the user all the time was rather useless in most cases. So, I had some suggestions to put back the tree. Well, I just didn’t want to lose my beautiful new FlowDocument, so I started experimenting. I noticed that a Section could contain paragraphs or any other type of Block. Naturally, I wondered how hard it would be to create a section that only showed its contained children when some property was changed. So, I wrote the code and this is how it turned out…

internal class CollapsibleSection : Section
{
    public CollapsibleSection()
    {
        CollapsibleBlocks = new List<Block>();
        Header = new Paragraph();

        m_expandCollapseToggleButton = new ToggleButton();
        m_expandCollapseToggleButton.Click += new RoutedEventHandler(ExpandCollapseToggleButton_Click);
        m_inlineUIContainer = new InlineUIContainer(m_expandCollapseToggleButton);
        m_inlineUIContainer.BaselineAlignment = BaselineAlignment.Center;
        m_inlineUIContainer.Cursor = Cursors.Arrow;

        Header.Inlines.Add(m_inlineUIContainer);
    }

    private void ExpandCollapseToggleButton_Click(object sender, RoutedEventArgs e)
    {
        Invalidate();
    }

    public Paragraph Header { get; private set; }
    public List<Block> CollapsibleBlocks { get; private set; }

    public bool IsCollapsed
    {
        get
        {
            return !(m_expandCollapseToggleButton.IsChecked ?? false);
        }
        set
        {
            m_expandCollapseToggleButton.IsChecked = !value;
        }
    }

    public void Invalidate()
    {
        Blocks.Clear();

        if (CollapsibleBlocks.Count == 0)
        {
            m_expandCollapseToggleButton.IsChecked = null;
        }

        Blocks.Add(Header);

        if (!IsCollapsed)
        {
            Blocks.AddRange(CollapsibleBlocks);
        }
    }

    private ToggleButton m_expandCollapseToggleButton;
    private InlineUIContainer m_inlineUIContainer;
}

As you can see, it was fairly easy to create this class. Not very much code at all. The class has a Header property that is the one paragraph that you see all the time and contains the toggle button. It also has a CollapsibleBlocks property that you can add any type of Block to. When the user clicks the button the IsCollapsed property is toggled and the Header and CollapsibleBlocks are used to modify the built-in Blocks property of the section.

I left out all the work I did after the fact to make the toggle button look like a triangle and rotate when clicked, but you should be able to figure that stuff out the same way I did, by looking at the way a TreeView works.

And here’s how I used the class:

   CollapsibleSection section = new CollapsibleSection();
   section.Margin = new Thickness(parentIndent, StandardPadding, 0, 0);
   section.IsCollapsed = false;

   . . .

   section.Header.Inlines.Add(header);

   . . .

   section.CollapsibleBlocks.Add(new Paragraph(new Run(“test paragraph”));

   section.Invalidate();

I don’t like having to invalidate the section after I have added everything, but I didn’t want to spend a lot of time trying to figure out a clever way around it.

I hope this gives you an example of just how extensible FlowDocuments are and why I like them so much!!!