WPFで開閉可能なパネルを作ったっていう

最近、なぜかわからんけど、謎のデータのビジュアライズしたい欲が沸き上がっていまして、それを表現するための枠組みというかパネル系を考えていました。

今年のマイブームなんだろうか??で、イメージ的には Expander みたいなタイトルとコンテンツの2部構成ものでさらに開閉可能なやつです。ただ Expander の見た目が自分のイメージと違っていて、もうちょいカッコイイやつが欲しいなぁと考えていました。というか Visual Studio のダイアグラムみたいなやつですね。

で、調べて作ってボツにして、、、の繰り返しをしてて、やっとなんとなく形になったのでここに控えておきます。これを使って何するの?っていうと、まだ何も考えていなかったり...

ポイントは、開閉機能の実現方法で、Grid の RowDefinition の高さを調整しているだけでコンテンツ自体には何もしていないところです。あとは例によって適当仕様のため、大量データとか高速化とか実用性についての対策に関してはゼロです。

目次

見た目

タイトルバー的なものの右端にある << をクリックするとコンテンツが隠れてタイトルのみの表示、 >> をクリックするとコンテンツが見えるようになる、という動作になります。こういうのは薄いグレー、青、緑、赤、オレンジ、ピンクとか、淡い色がキレイですよね~。

f:id:sutefu7:20190723211203p:plain

ExpandablePanel っていう UserControl

<UserControl x:Class="WpfApp4.ExpandablePanel"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:WpfApp4"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">

    <UserControl.Template>
        <ControlTemplate TargetType="UserControl">

            <StackPanel Orientation="Horizontal">
                <Border BorderBrush="LightGray" BorderThickness="1" CornerRadius="0">
                    <Grid>
                        <!-- タイトルとコンテンツ -->
                        <Grid.RowDefinitions>
                            <RowDefinition Height="Auto" />
                            <RowDefinition />
                        </Grid.RowDefinitions>
                        <Grid>
                            <!-- タイトルの背景色とタイトルと開閉ボタン -->
                            <Rectangle Fill="LightGray" RadiusX="0" RadiusY="0" />
                            <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type local:ExpandablePanel}}, Path=Title}" Margin="30,0,30,0" HorizontalAlignment="Center" />
                            <StackPanel Grid.Column="1" Orientation="Horizontal" HorizontalAlignment="Right">
                                <Button Margin="5,0,5,0" Content="&lt;&lt;" Click="Button_Click">
                                    <Button.Template>
                                        <ControlTemplate TargetType="Button">
                                            <TextBlock Text="{TemplateBinding Content}" />
                                        </ControlTemplate>
                                    </Button.Template>
                                </Button>
                            </StackPanel>
                        </Grid>
                        <ContentPresenter Grid.Row="1" />
                    </Grid>
                </Border>
            </StackPanel>
            
        </ControlTemplate>
    </UserControl.Template>
    
</UserControl>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApp4
{
    /// <summary>
    /// ExpandablePanel.xaml の相互作用ロジック
    /// </summary>
    public partial class ExpandablePanel : UserControl
    {
        public static readonly DependencyProperty TitleProperty =
            DependencyProperty.Register(
                "Title",
                typeof(string),
                typeof(ExpandablePanel),
                new PropertyMetadata("title1"));

        public string Title
        {
            get { return (string)GetValue(TitleProperty); }
            set { SetValue(TitleProperty, value); }
        }


        public ExpandablePanel()
        {
            InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var button1 = sender as Button;
            var stackPanel1 = button1.Parent as StackPanel;
            var grid1 = stackPanel1.Parent as Grid;
            var grid2 = grid1.Parent as Grid;
            var rowDefinition1 = grid2.RowDefinitions[1];

            if (button1.Content.ToString() == "<<")
            {
                button1.Content = ">>";
                rowDefinition1.Height = new GridLength(0, GridUnitType.Pixel);
            }
            else
            {
                button1.Content = "<<";
                rowDefinition1.Height = new GridLength(1, GridUnitType.Star);
            }
        }
    }
}

MainWindow(コントロールを使う側)

<Window x:Class="WpfApp4.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp4"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">

    <ScrollViewer>

        <StackPanel Margin="50" x:Name="TopPanel">

            <local:ExpandablePanel Title="Sample1" Margin="10">
                <Rectangle Width="120" Height="80" />
            </local:ExpandablePanel>

            <local:ExpandablePanel Title="Sample2" Margin="10">
                <Rectangle Width="120" Height="80" />
            </local:ExpandablePanel>

            <local:ExpandablePanel Title="Sample3" Margin="10">
                <local:ExpandablePanel Title="Inner" Margin="10">
                    <Rectangle Width="120" Height="80" />
                </local:ExpandablePanel>
            </local:ExpandablePanel>

            <local:ExpandablePanel Title="Sample4" Margin="10">

                <StackPanel>

                    <local:ExpandablePanel Title="Inner1" Margin="10">
                        <Rectangle Width="120" Height="80" />
                    </local:ExpandablePanel>

                    <local:ExpandablePanel Title="Inner2" Margin="10">
                        <Rectangle Width="120" Height="80" />
                    </local:ExpandablePanel>

                </StackPanel>
                
            </local:ExpandablePanel>

        </StackPanel>

    </ScrollViewer>
    
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApp4
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    }
}