【C# WPF】追加削除ボタンタブのカスタムコントロールライブラリ

今回は、WPF で追加削除ボタンつきのタブコントロールをカスタムコントロールライブラリで作成します。

前回はプロジェクト内にカスタムコントロールを作りました。

【C# WPF】カスタムコントロールで追加削除ボタンつきのTabControl を作成

2020.03.06

ライブラリ化することで、他のプロジェクトでも組み込みが容易になります

やりたいこと

  • ライブラリ名: TabAddDel_CustomControlLibrary
  • カスタムコントロール名 : TabAddDel_CustomControl

とした場合、

Xamlファイルにカスタムコントロールのタグを追記するだけで、

追加ボタン、削除ボタンを実装したTabControlを生成することです。

仕様
  • 追加ボタンを押すたび、初期設定のtitle+全タブ数をタイトルとしたタブを生成
  • タブを追加されるたびにハンドラを呼び出す
  • 先頭のタブは削除ボタンを非表示
  • タブを削除すると、タイトルが順番通りになるようにヘッダーを修正
開発環境
  • Windows10
  • Microsoft Visual Studio Community2019
  • .NET Framework 4.7.2
  • WPF アプリ( .NET Framework )

カスタムコントロールライブラリを追加

まずはプロジェクトにカスタムコントロールライブラリを追加します。

ソリューションエクスプローラから、ソリューション→追加→新しいプロジェクト を選択します。

WPF カスタム コントロール ライブラリ( ,NET Framework ) を選択します。次へを押して、ダイアログでプロジェクトの場所を決定します。

次はライブラリを参照できるようにします。

ソリューションエクスプローラーから、参照元のプロジェクト→参照で右クリック → 参照の追加 を選択します。

参照マネージャーでプロジェクト→ソリューションを選択すると追加したカスタムコントロールライブラリが表示されます。チェックすることで参照が有効となります。

カスタムコントロールライブラリの作成

Generic.xaml

ライブラリを作成すると、Generic.xamlが生成されます。

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:TabAddDel_CustomControlLibrary">
<Style TargetType="{x:Type local:TabAddDel_CustomControl}" BasedOn="{StaticResource {x:Type TabControl}}">
        <!--
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:TabAddDel_CustomControl}">
                    <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">

                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        -->
    </Style>
</ResourceDictionary>
  • StyleタグのBased属性の値にでTabControlを設定します。
  • Setter タグ以降は使用しないのでコメントアウトします。

カスタムコントロールのCSファイル

カスタムコントロールのCSファイルは以下となります。

using System;
using System.Windows;
using System.Windows.Controls;

namespace TabAddDel_CustomControlLibrary
{
    public class TabAddDel_CustomControl : TabControl
    {
        static TabAddDel_CustomControl()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(TabAddDel_CustomControl), new FrameworkPropertyMetadata(typeof(TabAddDel_CustomControl)));
        }

        /// <summary>
        /// タブ追加したときのイベント
        /// </summary>
        public static readonly RoutedEvent AddTabSettingEvent = 
            EventManager.RegisterRoutedEvent(
                "AddTabSetting",
                RoutingStrategy.Bubble,
                typeof(RoutedEventHandler),
                typeof(TabAddDel_CustomControl)
            );

        public event RoutedEventHandler AddTabSetting
        {
            add { AddHandler(AddTabSettingEvent, value); }
            remove { RemoveHandler(AddTabSettingEvent, value); }
        }

        /// <summary>
        /// タブ追加したときのイベントのパラメータ
        /// </summary>
        public class AddTabSettingEventArgs : RoutedEventArgs
        {
            public TabItem item { get; set; }

            public AddTabSettingEventArgs(RoutedEvent routedEvent) : base(routedEvent) { }
        }

        void RaiseAddTabSettingEvent( TabItem item )
        {
            AddTabSettingEventArgs newEventArgs = new AddTabSettingEventArgs(AddTabSettingEvent);
            newEventArgs.item = item;
            RaiseEvent(newEventArgs);
        }

        /// <summary>
        /// タブのタイトル
        /// </summary>
        public static readonly DependencyProperty TitleProperty =
            DependencyProperty.Register(
                "Title",
                typeof(String),
                typeof(TabAddDel_CustomControl),
                new PropertyMetadata("")
            );

        public static string GetTitle(DependencyObject obj)
        {
            return (string)obj.GetValue(TitleProperty);
        }

        public static void SetTitle(DependencyObject obj, string value)
        {
            obj.SetValue(TitleProperty, value);
        }

        /// <summary>
        /// コントロールの初期化後の処理
        /// </summary>
        public override void EndInit()
        {
            base.EndInit();

            // 追加ボタン
            TabItem itemAdd = new TabItem();
            itemAdd.Header = "+";
            Items.Add(itemAdd);

            // 最初のタブを追加
            AddTabItemCustom(Visibility.Hidden);

            // イベントを登録
            SelectionChanged += SelectedAddDelTab;

        }

        /// <summary>
        /// タブの切り替え時
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void SelectedAddDelTab(object sender, SelectionChangedEventArgs e)
        {
            // 追加ボタン(+)を選択する
            if (SelectedIndex == Items.Count - 1)
            {
                // 最初のタブを追加
                AddTabItemCustom(Visibility.Visible);

                // 追加したタブを選択状態にする
                SelectedIndex = Items.Count - 2;

                // 追加したタブにフォーカスを設定する
                ((TabItem)(Items[SelectedIndex])).Focus();

                e.Handled = true;
            }
        }

        /// <summary>
        /// 削除ボタンをクリック
        /// </summary>
        /// <param name="item">削除ボタンクリックしたアイテム</param>
        public void ClickDelButton(TabItem item)
        {
            // インデックスを1つ戻す
            SelectedIndex--;

            // タブを削除する
            Items.Remove(item);

            // タブのタイトルを再設定する
            for (int i = 1; i < Items.Count - 1; i++)
            {
                ((HeaderUserControl)((TabItem)Items[i]).Header).label.Content = GetTitle(this) + (i + 1);
            }
        }

        /// <summary>
        /// タブの追加
        /// </summary>
        /// <param name="visibility">削除ボタンの表示状態</param>
        private void AddTabItemCustom(Visibility visibility)
        {
            // タブを作成し、追加ボタンの前に追加
            TabItem item = new TabItem();
            item.Header = new HeaderUserControl(GetTitle(this) + Items.Count, visibility);
            Items.Insert(Items.Count - 1, item);

            // イベントハンドラの呼び出し
            if ( AddTabSettingEvent != null )
            {
                RaiseAddTabSettingEvent(item);
            }
        }
    }
}

タブ追加したときのイベント

タブを追加したときに、コントロールを使用する側の関数を呼び出せるようにします。また、関数を呼び出すとき、追加したTabItem もパラメータとして渡します。

タブを追加

タブを追加した後、コントロールを使用する側のイベントハンドラを呼び出します。

前回は、ユーザーコントロールの複製を行いました。ライブラリ化したとことで、呼び出し側のユーザーコントロールの複製ができなくなりました。今回はコントロールを使用する側でコンテンツの設定をできるようにします。

それ以外のソースコードの説明は、以下の記事を参考にしてください。

【C# WPF】カスタムコントロールで追加削除ボタンつきのTabControl を作成

2020.03.06

カスタムコントロールを使用する

最後にMainWindowでカスタムコントロールを使用するコードの説明です。

<Window 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:TabAddDelTest" xmlns:TabAddDel_CustomControlLibrary="clr-namespace:TabAddDel_CustomControlLibrary;assembly=TabAddDel_CustomControlLibrary" x:Class="TabAddDelTest.MainWindow" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800">
    <Grid>
        <TabAddDel_CustomControlLibrary:TabAddDel_CustomControl HorizontalAlignment="Left" Margin="35,35,0,0" VerticalAlignment="Top" Height="285" Width="500" Title="タブ" Visibility="Visible" AddTabSetting="AddTabSetting">
        </TabAddDel_CustomControlLibrary:TabAddDel_CustomControl>
    </Grid>
</Window>

TabAddDel_CustomControlLibrary:TabAddDel_CustomControl タグでカスタムコントロールの設定ができます。AddTabSetting プロパティにタブを追加したときのハンドラ関数を指定します。

using System.Windows;
using System.Windows.Controls;

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

        private void AddTabSetting(object sender, RoutedEventArgs e)
        {
            // タブが追加されたとき呼ばれる
            // ここでは、追加されたタブのコンテンツにユーザーコントロールを設定する
            TabItem item = ((TabAddDel_CustomControlLibrary.TabAddDel_CustomControl.AddTabSettingEventArgs)e).item;
            TabItemUserControl control = new TabItemUserControl();

            item.Content = control;
        }
    }
}

AddTabSetting 関数はタブが追加されたとき、ライブラリのカスタムコントロールから呼ばれます。関数内で、追加したTabItemのコンテンツにユーザーコントロールを設定します。

まとめ

WPF で追加削除ボタンつきのタブコントロールをカスタムコントロールライブラリで作成しました。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です