0

I have a button in my C# .NET WPF app which discloses more content on the screen when clicked. I'd like to use the standard progressive disclosure rotating triangle:

progressive disclosure rotating triangle

I'm about to do this by making a button with an image that changes when you click on it. All I need to do is draw two triangles. However, since this is a standard UI task, it makes me wonder if Microsoft has provided, if not an entire control, at least the button images already. I could just proceed and roll my own button, but:

  1. I'm just re-inventing the wheel--probably in a worse fashion than Microsoft has already done.
  2. My images likely won't look exactly the same as the Microsoft ones (either due to scaling or positioning, or rotation speed, etc.).

Is there already a progressive discolure rotating triangle control? If not, are the button images already installed with the OS where I can use them?

watkipet
  • 959
  • 12
  • 23
  • Use Path instead of images, those will scale nicely when your app is used on a 4k monitor. Images is so 90's. What you have there is `TreeViewItem`, use blend to get the template for the triangle and the visual states, then just apply those to your button style. – XAMlMAX Jan 13 '20 at 16:12
  • Looks like the button on a `TreeView`? – canton7 Jan 13 '20 at 16:13
  • 1
    Something like an [Expander](https://learn.microsoft.com/en-us/dotnet/framework/wpf/controls/expander) ? – Fildor Jan 13 '20 at 16:17
  • Yes, I'd like something that looks like a button on a TreeView. I'm basically making a DataGrid which can [have expanded child rows](https://stackoverflow.com/questions/463915/multi-column-tree-view-in-wpf). – watkipet Jan 13 '20 at 16:22
  • Then that would be a .... how was that called ... PropertyGrid? Something like that? Nah, that was WinForms ... – Fildor Jan 13 '20 at 16:23
  • The concept is the same as an expander, but I'm displaying more child rows in a DataGrid when the user clicks the button--so it doesn't really fit the Header + Content concept of an expander. – watkipet Jan 13 '20 at 16:24
  • @XAMlMAX, I think what you're describing is what I need to do, but I have no idea how to do those steps you've mentioned (I've never used Blend, visual states, etc.) If this is a concise enough scenario that you can list the steps, would you mind doing that in an answer? – watkipet Jan 13 '20 at 16:27
  • 1
    If that is the case then use ListView instead, that's when you can utilise grouping of items using CollectionViewSource. ListView supports GridView, so you can have all of the columns that you need plus an expander that will give you rows. It's really easy. [Here](https://stackoverflow.com/a/21229558/2029607) is my SO post – XAMlMAX Jan 13 '20 at 16:30

1 Answers1

2

I'm not aware of any official resource you can use, though I agree that there should probably be one. That said, the best I can do is show you how Microsoft makes theirs so you can do the same.

As XAMlMAX pointed out, the standard TreeViewItem control makes use of these triangles. I used Visual Studio to get the template for said control. Here are the relevant parts:

        <PathGeometry x:Key="TreeArrow" Figures="M0,0 L0,6 L6,0 z"/>
        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Fill" Color="#FF595959"/>
        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Checked.Stroke" Color="#FF262626"/>
        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Stroke" Color="#FF27C7F7"/>
        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Fill" Color="#FFCCEEFB"/>
        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Stroke" Color="#FF1CC4F7"/>
        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.MouseOver.Checked.Fill" Color="#FF82DFFB"/>
        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Fill" Color="#FFFFFFFF"/>
        <SolidColorBrush x:Key="TreeViewItem.TreeArrow.Static.Stroke" Color="#FF818181"/>
        <Style x:Key="ExpandCollapseToggleStyle" TargetType="{x:Type ToggleButton}">
            <Setter Property="Focusable" Value="False"/>
            <Setter Property="Width" Value="16"/>
            <Setter Property="Height" Value="16"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ToggleButton}">
                        <Border Background="Transparent" Height="16" Padding="5,5,5,5" Width="16">
                            <Path x:Name="ExpandPath" Data="{StaticResource TreeArrow}" Fill="{StaticResource TreeViewItem.TreeArrow.Static.Fill}" Stroke="{StaticResource TreeViewItem.TreeArrow.Static.Stroke}">
                                <Path.RenderTransform>
                                    <RotateTransform Angle="135" CenterY="3" CenterX="3"/>
                                </Path.RenderTransform>
                            </Path>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsChecked" Value="True">
                                <Setter Property="RenderTransform" TargetName="ExpandPath">
                                    <Setter.Value>
                                        <RotateTransform Angle="180" CenterY="3" CenterX="3"/>
                                    </Setter.Value>
                                </Setter>
                                <Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Fill}"/>
                                <Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.Static.Checked.Stroke}"/>
                            </Trigger>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Stroke}"/>
                                <Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Fill}"/>
                            </Trigger>
                            <MultiTrigger>
                                <MultiTrigger.Conditions>
                                    <Condition Property="IsMouseOver" Value="True"/>
                                    <Condition Property="IsChecked" Value="True"/>
                                </MultiTrigger.Conditions>
                                <Setter Property="Stroke" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Stroke}"/>
                                <Setter Property="Fill" TargetName="ExpandPath" Value="{StaticResource TreeViewItem.TreeArrow.MouseOver.Checked.Fill}"/>
                            </MultiTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

At the top is the "TreeArrow" PathGrometry which is used to create the triangle. Below that are SolidColorBrushes that provide the colors for the various states. And below that is the style for ToggleButton which uses the previously mentioned resources, correctly setting the color and rotation of the triangle based on the state of said ToggleButton.

You should be able to use this to create a progressive disclosure mechanism that matches Microsoft's.

Keith Stein
  • 6,235
  • 4
  • 17
  • 36
  • Thanks. I tried this and it worked. I used a ToggleButton and then bound IsChecked to a property in my ViewModel. Changes in that property cause row additions to or subtractions from my GridView. – watkipet Jan 14 '20 at 15:33