■はじめに
今回は画面内にヘルプページを表示するサンプルと、画面パーツを別xamlにするやり方を学びます。
これらの実装例として、正規表現の動作確認ツールを作ります。
■開発環境
- 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の場合
◇.NET Core 3.1 / .NET 5.0の場合
■プロジェクトの設定
◇.NET Core 3.1の場合
プロジェクトを右クリック、「プロパティ」で「対象のフレームワーク」を.NET Core 3.1
に設定します。
◇.NET 5.0の場合
プロジェクトを右クリック、「プロパティ」で「対象のフレームワーク」を.NET 5.0
に設定します。
プロジェクトをダブルクリックし、
csprojファイルのSdkがMicrosoft.NET.Sdk.WindowsDesktop
になっていたらMicrosoft.NET.Sdk
に編集します。
■メイン画面の枠組み作成
<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>
■画面パーツ用意
プロジェクトを右クリック、「追加」-「新しいフォルダー」でViews
と入力してください。
「Views」フォルダー内にPages
フォルダーを作成してください。
「Pages」フォルダーを右クリック、「追加」-「ユーザーコントロール(WPF)」でMatchingContent
と入力してください。
同様に「Pages」フォルダにユーザーコントロールHelpContent
を追加してください。
■タブのコンテンツ組み込み
追加したコントロールをメイン画面の各タブで参照します。
MainWindow.xamlのWindowに
xmlns:page="clr-namespace:RegexTester.Views.Pages"
を追加してください。
<Window
:
xmlns:page="clr-namespace:RegexTester.Views.Pages"
:
そして、TabItemの中にpage:
の後に先ほど追加したユーザーコントロールの名前を書きます。
<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リンクを作ります。
Xamlの書き方もHTMLのTableと少し似ています。
<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>
(?<🍇>🥝)
</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の場合
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の場合
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で「ヘルプ」タブを選択してみましょう。
ちゃんと「ヘルプ」タブ内に表示されました。
■メインコンテンツ作成
<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>
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で「マッチング」タブを見てみましょう。
■メイン画面外枠部分の実装
「閉じる」ボタンの処理を書きます。
:
<StackPanel Grid.Row="1">
<Button
Width="150"
Margin="12,6"
HorizontalAlignment="Right"
Click="CloseButton_Click"
Content="閉じる(_C)" />
</StackPanel>
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();
}
}
}
■実行
「文字列」のテキストボックスに検索対象の文字列を入力し、
「正規表現パターン」のテキストボックスに検索する正規表現を入力し、実行ボタンを押します。
おしまい