Help us understand the problem. What is going on with this article?

Windows GUIプログラミング入門27 ヘルプ画面

■はじめに

今回は画面内にヘルプページを表示するサンプルと、画面パーツを別xamlにするやり方を学びます。
これらの実装例として、正規表現の動作確認ツールを作ります。
000.png

■開発環境

  • Windows 10
  • Visual Studio Community 2019 (Version 16.8.2)
  • .NET Framework 4.6.2 / .NET Core 3.1 / .NET 5.0

■プロジェクト作成

RegexTesterという名前でプロジェクトを作成します。

◇.NET Frameworkの場合

001.png

002.png

◇.NET Core 3.1 / .NET 5.0の場合

003.png

004.png

■プロジェクトの設定

◇.NET Core 3.1の場合

プロジェクトを右クリック、「プロパティ」で「対象のフレームワーク」を.NET Core 3.1に設定します。
005.png

◇.NET 5.0の場合

プロジェクトを右クリック、「プロパティ」で「対象のフレームワーク」を.NET 5.0に設定します。
006.png

プロジェクトをダブルクリックし、
csprojファイルのSdkがMicrosoft.NET.Sdk.WindowsDesktopになっていたらMicrosoft.NET.Sdkに編集します。
007.png

■メイン画面の枠組み作成

Gridを分割し、上にタブ、下に閉じるボタンを配置します。
010.png

MainWindow.xaml
<Window
    x:Class="RegexTester.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:local="clr-namespace:RegexTester"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    Title="Regex Tester .NET"
    Width="800"
    Height="450"
    ResizeMode="CanResizeWithGrip"
    mc:Ignorable="d">
    <Grid>
        <Grid.RowDefinitions>
            <!--  タブ行  -->
            <RowDefinition />
            <!--  ボタン行  -->
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>

        <TabControl Margin="6">
            <TabItem Header="マッチング">
            </TabItem>
            <TabItem Header="ヘルプ">
            </TabItem>
        </TabControl>

        <StackPanel Grid.Row="1">
            <Button
                Width="150"
                Margin="12,6"
                HorizontalAlignment="Right"
                Content="閉じる(_C)" />
        </StackPanel>

    </Grid>
</Window>

■画面パーツ用意

015_.png
プロジェクトを右クリック、「追加」-「新しいフォルダー」でViewsと入力してください。
「Views」フォルダー内にPagesフォルダーを作成してください。

020_.png
「Pages」フォルダーを右クリック、「追加」-「ユーザーコントロール(WPF)」でMatchingContentと入力してください。
同様に「Pages」フォルダにユーザーコントロールHelpContentを追加してください。
025_2.png

■タブのコンテンツ組み込み

追加したコントロールをメイン画面の各タブで参照します。

MainWindow.xamlのWindowに
xmlns:page="clr-namespace:RegexTester.Views.Pages"を追加してください。

MainWindow.xaml
<Window
:
    xmlns:page="clr-namespace:RegexTester.Views.Pages"
:

そして、TabItemの中にpage:の後に先ほど追加したユーザーコントロールの名前を書きます。

MainWindow.xaml
<TabControl Margin="6">
    <TabItem Header="マッチング">
        <page:MatchingContent />
    </TabItem>

    <TabItem Header="ヘルプ">
        <page:HelpContent />
    </TabItem>
</TabControl>

pageの部分は任意の名前を付けられます。
xmlns:abc="clr-namespace:RegexTester.Views.Pages"としていたら
<abc:MatchingContent />という書き方でPagesフォルダのコントロールを参照できます。

ここまでで一度ビルドしておきましょう。

■ヘルプ内容作成

◇画面

「ヘルプ」タブに表示する内容を作ります。
HTMLのテーブル風のリファレンスとURLリンクを作ります。
030.png

Xamlの書き方もHTMLのTableと少し似ています。

HelpContent.xaml
<UserControl
    x:Class="RegexTester.Views.Pages.HelpContent"
    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:local="clr-namespace:RegexTester.Views.Pages"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    d:DesignHeight="500"
    d:DesignWidth="800"
    mc:Ignorable="d">
    <FlowDocumentScrollViewer>
        <FlowDocument>
            <Section>
                <Paragraph>
                    <Bold>■正規表現クイックリファレンス</Bold>
                </Paragraph>
                <Table FontFamily="JetBrains Mono, Cascadia Code, BIZ UDGothic, Yu Gothic Medium, Meiryo">
                    <Table.Resources>
                        <Style TargetType="TableCell">
                            <Setter Property="BorderBrush" Value="Silver" />
                            <Setter Property="BorderThickness" Value="0.5" />
                        </Style>
                    </Table.Resources>
                    <Table.Columns>
                        <TableColumn Width="*" />
                        <TableColumn Width="4*" />
                    </Table.Columns>
                    <TableRowGroup>
                        <TableRow>
                            <TableCell>
                                <Paragraph>.</Paragraph>
                            </TableCell>
                            <TableCell>
                                <Paragraph>任意の1文字</Paragraph>
                            </TableCell>
                        </TableRow>
                    </TableRowGroup>
                    <TableRowGroup>
                        <TableRow>
                            <TableCell>
                                <Paragraph>*</Paragraph>
                            </TableCell>
                            <TableCell>
                                <Paragraph>直前の要素の0回以上繰り返し</Paragraph>
                            </TableCell>
                        </TableRow>
                    </TableRowGroup>
                    <TableRowGroup>
                        <TableRow>
                            <TableCell>
                                <Paragraph>+</Paragraph>
                            </TableCell>
                            <TableCell>
                                <Paragraph>直前の要素の1回以上繰り返し</Paragraph>
                            </TableCell>
                        </TableRow>
                    </TableRowGroup>
                    <TableRowGroup>
                        <TableRow>
                            <TableCell>
                                <Paragraph>?</Paragraph>
                            </TableCell>
                            <TableCell>
                                <Paragraph>直前の要素の0~1回繰り返し</Paragraph>
                            </TableCell>
                        </TableRow>
                    </TableRowGroup>
                    <TableRowGroup>
                        <TableRow>
                            <TableCell>
                                <Paragraph>^</Paragraph>
                            </TableCell>
                            <TableCell>
                                <Paragraph>行頭</Paragraph>
                            </TableCell>
                        </TableRow>
                    </TableRowGroup>
                    <TableRowGroup>
                        <TableRow>
                            <TableCell>
                                <Paragraph>$</Paragraph>
                            </TableCell>
                            <TableCell>
                                <Paragraph>行末</Paragraph>
                            </TableCell>
                        </TableRow>
                    </TableRowGroup>
                    <TableRowGroup>
                        <TableRow>
                            <TableCell>
                                <Paragraph>{🥝}</Paragraph>
                            </TableCell>
                            <TableCell>
                                <Paragraph>直前の要素の🥝回繰り返し</Paragraph>
                            </TableCell>
                        </TableRow>
                    </TableRowGroup>
                    <TableRowGroup>
                        <TableRow>
                            <TableCell>
                                <Paragraph>{🥝,}</Paragraph>
                            </TableCell>
                            <TableCell>
                                <Paragraph>直前の要素の🥝回以上繰り返し</Paragraph>
                            </TableCell>
                        </TableRow>
                    </TableRowGroup>
                    <TableRowGroup>
                        <TableRow>
                            <TableCell>
                                <Paragraph>{🥝,🍉}</Paragraph>
                            </TableCell>
                            <TableCell>
                                <Paragraph>直前の要素の🥝~🍉回繰り返し</Paragraph>
                            </TableCell>
                        </TableRow>
                    </TableRowGroup>
                    <TableRowGroup>
                        <TableRow>
                            <TableCell>
                                <Paragraph>[🥝]</Paragraph>
                            </TableCell>
                            <TableCell>
                                <Paragraph>🥝内の任意の1文字</Paragraph>
                            </TableCell>
                        </TableRow>
                    </TableRowGroup>
                    <TableRowGroup>
                        <TableRow>
                            <TableCell>
                                <Paragraph>[^🥝]</Paragraph>
                            </TableCell>
                            <TableCell>
                                <Paragraph>🥝内にない任意の1文字</Paragraph>
                            </TableCell>
                        </TableRow>
                    </TableRowGroup>
                    <TableRowGroup>
                        <TableRow>
                            <TableCell>
                                <Paragraph>[🥝-🍉]</Paragraph>
                            </TableCell>
                            <TableCell>
                                <Paragraph>文字範囲🥝から🍉までの任意の1文字</Paragraph>
                            </TableCell>
                        </TableRow>
                    </TableRowGroup>
                    <TableRowGroup>
                        <TableRow>
                            <TableCell>
                                <Paragraph>()</Paragraph>
                            </TableCell>
                            <TableCell>
                                <Paragraph>グループ</Paragraph>
                            </TableCell>
                        </TableRow>
                    </TableRowGroup>
                    <TableRowGroup>
                        <TableRow>
                            <TableCell>
                                <Paragraph>(?:🥝)</Paragraph>
                            </TableCell>
                            <TableCell>
                                <Paragraph>非キャプチャ グループ</Paragraph>
                            </TableCell>
                        </TableRow>
                    </TableRowGroup>
                    <TableRowGroup>
                        <TableRow>
                            <TableCell>
                                <Paragraph>
                                    (?&lt;🍇&gt;🥝)
                                </Paragraph>
                            </TableCell>
                            <TableCell>
                                <Paragraph>名前付きグループ。🍇がグループ名</Paragraph>
                            </TableCell>
                        </TableRow>
                    </TableRowGroup>
                </Table>
            </Section>
            <Section>
                <Paragraph>
                    <Bold>■もっと詳しく</Bold>
                </Paragraph>
                <Paragraph>
                    <Hyperlink
                        NavigateUri="https://docs.microsoft.com/ja-jp/dotnet/standard/base-types/regular-expressions"
                        RequestNavigate="Hyperlink_RequestNavigate">
                        .NET の正規表現 | Microsoft Docs
                    </Hyperlink>
                </Paragraph>
            </Section>
        </FlowDocument>
    </FlowDocumentScrollViewer>
</UserControl>

◇ロジック

◎.NET Frameworkの場合

HelpContent.xaml.cs
using System.Windows.Controls;
using System.Windows.Navigation;

namespace RegexTester.Views.Pages
{
    /// <summary>
    /// HelpContent.xaml の相互作用ロジック
    /// </summary>
    public partial class HelpContent : UserControl
    {
        public HelpContent()
        {
            InitializeComponent();
        }

        private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
        {
            // URLを開く
            System.Diagnostics.Process.Start(e.Uri.AbsoluteUri);
        }
    }
}

◎.NET Core 3.1 / .NET 5.0の場合

HelpContent.xaml.cs
using System.Windows.Controls;
using System.Windows.Navigation;

namespace RegexTester.Views.Pages
{
    /// <summary>
    /// HelpContent.xaml の相互作用ロジック
    /// </summary>
    public partial class HelpContent : UserControl
    {
        public HelpContent()
        {
            InitializeComponent();
        }

        private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e)
        {
            var p = new System.Diagnostics.Process();
            p.StartInfo.FileName = e.Uri.AbsoluteUri;
            p.StartInfo.UseShellExecute = true;
            // URLを開く
            p.Start();
        }
    }
}

作り終わったらビルドし、MainWindowで「ヘルプ」タブを選択してみましょう。
ちゃんと「ヘルプ」タブ内に表示されました。
035.png

■メインコンテンツ作成

「マッチング」タブに表示する内容を作ります。
040.png

MatchingContent.xaml
<UserControl
    x:Class="RegexTester.Views.Pages.MatchingContent"
    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:local="clr-namespace:RegexTester.Views.Pages"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    d:DesignHeight="450"
    d:DesignWidth="800"
    mc:Ignorable="d">
    <UserControl.Resources>
        <Style TargetType="Label">
            <Setter Property="Margin" Value="6,6,6,0" />
        </Style>
        <Style TargetType="TextBox">
            <Setter Property="Margin" Value="6,0,6,6" />
            <Setter Property="FontFamily" Value="JetBrains Mono, Cascadia Code, BIZ UDGothic, Yu Gothic Medium, Meiryo" />
        </Style>
        <Style TargetType="CheckBox">
            <Setter Property="Margin" Value="6" />
        </Style>
    </UserControl.Resources>
    <Grid FocusManager.FocusedElement="{Binding ElementName=InTxt}">
        <Grid.RowDefinitions>
            <!--  上部(検索文字列)  -->
            <RowDefinition Height="Auto" MinHeight="60" />
            <!--  スプリッター  -->
            <RowDefinition Height="Auto" />
            <!--  下部(正規表現パターン~結果)  -->
            <RowDefinition />
        </Grid.RowDefinitions>

        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <!--  文字列テキスト行  -->
                <RowDefinition />
            </Grid.RowDefinitions>

            <Label Content="文字列(_I)" Target="{Binding ElementName=InTxt}" />
            <TextBox
                x:Name="InTxt"
                Grid.Row="1"
                AcceptsReturn="True"
                AcceptsTab="False"
                Text=""
                TextWrapping="Wrap"
                VerticalScrollBarVisibility="Visible" />
        </Grid>

        <GridSplitter
            Grid.Row="1"
            Height="6"
            Margin="0,1"
            HorizontalAlignment="Stretch" />

        <Grid Grid.Row="2">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <!--  結果のテキスト行  -->
                <RowDefinition />
            </Grid.RowDefinitions>

            <StackPanel>
                <Label Content="正規表現パターン(_P)" Target="{Binding ElementName=RegPtn}" />
                <TextBox x:Name="RegPtn" Text="" />

                <StackPanel Orientation="Horizontal">
                    <CheckBox x:Name="ROptIgnoreCase" Content="大文字小文字の区別をしない(_O)" />
                    <CheckBox x:Name="ROptMultiLine" Content="複数行モード(_M)" />
                    <CheckBox x:Name="ROptSingleLine" Content="単一行モード(_S)" />

                    <Button
                        Margin="12,0"
                        Padding="18,0"
                        Click="RunButton_Click"
                        Content="実行(_E)"
                        ToolTip="マッチングを実行します" />
                </StackPanel>

                <Label Content="結果(_R)" Target="{Binding ElementName=OutTxt}" />
            </StackPanel>

            <TextBox
                x:Name="OutTxt"
                Grid.Row="1"
                IsReadOnly="True"
                Text=""
                VerticalScrollBarVisibility="Visible" />
        </Grid>
    </Grid>
</UserControl>
MatchingContent.xaml.cs
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;
using System.Windows.Controls;

namespace RegexTester.Views.Pages
{
    /// <summary>
    /// MatchingContent.xaml の相互作用ロジック
    /// </summary>
    public partial class MatchingContent : UserControl
    {
        public MatchingContent()
        {
            InitializeComponent();
        }

        /// <summary>
        /// 実行ボタンクリック
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void RunButton_Click(object sender, RoutedEventArgs e)
        {
            this.IsEnabled = false;
            ExecuteMatching(InTxt.Text, RegPtn.Text, GetRegexOptions());
            this.IsEnabled = true;
        }

        /// <summary>
        /// マッチング実行
        /// </summary>
        /// <param name="input">文字列</param>
        /// <param name="regPattern">正規表現パターン</param>
        /// <param name="regOpt">検索オプション</param>
        public void ExecuteMatching(string input, string regPattern, RegexOptions regOpt)
        {
            OutTxt.Clear();

            // 未入力がないかチェック
            if (IsAnyNullOrEmpties(input, regPattern))
            {
                MessageBox.Show("未入力の項目があります。");
                return;
            }

            try
            {
                var reg = new Regex(regPattern, regOpt);
                // マッチング実行
                MatchCollection matches = reg.Matches(input);
                if (matches.Count == 0)
                {
                    OutTxt.Text = "★マッチなし★";
                    return;
                }

                var sb = new StringBuilder();
                sb.Append($"★{matches.Count}件マッチ★\n");
                foreach (Match m in matches)
                {
                    sb.Append($"Value ⇒ {m.Value}\n");

                    sb.Append("【グループ名】\n");
                    foreach (string gName in reg.GetGroupNames())
                    {
                        sb.Append($"{gName} => {m.Groups[gName].Value}\n");
                    }
                    sb.Append("--------------------\n");
                }

                // 結果を設定
                OutTxt.Text = sb.ToString();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString());
            }
        }

        /// <summary>
        /// 未入力チェック
        /// </summary>
        /// <param name="values"></param>
        /// <returns>全部入力されていればfalse</returns>
        private bool IsAnyNullOrEmpties(params string[] values)
        {
            foreach (string value in values)
            {
                if (string.IsNullOrEmpty(value))
                {
                    // 未入力
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 画面から正規表現オプション取得
        /// </summary>
        /// <returns></returns>
        private RegexOptions GetRegexOptions()
        {
            var opt = RegexOptions.None;

            // 大文字小文字の区別をしない
            if (ROptIgnoreCase.IsChecked == true)
            {
                opt |= RegexOptions.IgnoreCase;
            }
            // 複数行モード
            if (ROptMultiLine.IsChecked == true)
            {
                opt |= RegexOptions.Multiline;
            }
            // 単一行モード
            if (ROptSingleLine.IsChecked == true)
            {
                opt |= RegexOptions.Singleline;
            }

            return opt;
        }

    }
}

作り終わったらビルドし、MainWindowで「マッチング」タブを見てみましょう。

■メイン画面外枠部分の実装

「閉じる」ボタンの処理を書きます。

MainWindow.xaml
:
<StackPanel Grid.Row="1">
    <Button
        Width="150"
        Margin="12,6"
        HorizontalAlignment="Right"
        Click="CloseButton_Click"
        Content="閉じる(_C)" />
</StackPanel>
MainWindow.xaml.cs
using System.Windows;

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

        private void CloseButton_Click(object sender, RoutedEventArgs e)
        {
            Close();
        }
    }
}

■実行

「文字列」のテキストボックスに検索対象の文字列を入力し、
「正規表現パターン」のテキストボックスに検索する正規表現を入力し、実行ボタンを押します。
045.png
050_2.png
055_2.png
060_2.png

おしまい


<< 最初の記事   < 前の記事   次の記事 >

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away