サジェッション付きTextBox
ComboBox
PDFSharp の FontResolver
OpenCvSharp4 の RotatedRect の Angle で得られる角度について
サジェッション付きTextBox
PCの操作に慣れている人でも、ここに得意先コードを入れてEnterキーを押してくださいとか、
DataGridに一覧を表示しておいて、選択してくださいとか言いにくい状況がやってきたときに、
サジェッション付きTextBoxなら良いかと思ってつくったもの
ユーザーコントロールにするにはxamlが複雑すぎて、プログラムの中で使ったそのまま
こちらのサイトを参考にさせていただきました。
WPF Auto Complete/Suggestion Text Box Control
<StackPanel HorizontalAlignment="Left" Margin="139,92,0,0" VerticalAlignment="Top">
<TextBox x:Name="tokuimei" TextWrapping="Wrap" Text="" Width="350" TextChanged="Tokuimei_TextChanged" PreviewKeyDown="Tokuimei_OnPreviewKeyDown">
<TextBox.InputBindings>
<!-- Window1は使用環境に合わせて変更してください-->
<KeyBinding Key="Enter" Command="{x:Static local:Window1.TokuimeiIn}" />
</TextBox.InputBindings>
<TextBox>
<Popup x:Name="tokuimeiListPopup" Visibility="Collapsed" StaysOpen="False" Placement="Bottom">
<ListBox x:Name="tokuimeiList" Visibility="Collapsed" SelectionChanged="TokuimeiList_SelectionChanged" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<!-- TOKUINO, TOKUIMEI, Width, Margin は使用環境に合わせて変更してください-->
<TextBlock Text="{Binding TOKUINO}" Width="50" Margin="10" />
<TextBlock Text="{Binding TOKUIMEI}" Margin="10" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="BorderBrush" Value="LightBlue"></Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Popup>
</StackPanel>
// このクラスの最初のほうに
:
List<Tokui> tokuimeiSuggestionList = new List<Tokui>();
public Tokui? SelectedTokui = null;
:
// InitializeComponent();のあとで
:
CommandBindings.Add(new CommandBinding(TokuimeiIn, TokuimeiInExec));
:
// Loaded か ContentRendaed でデータベースより表示用データを読む。、Tokuis と TOKUINO の所は変更してください
:
tokuimeiSuggestionList = dbContext.Tokuis.OrderBy(x => x.TOKUINO).ToList();
:
:
// 処理コードの部分
:
public static RoutedCommand TokuimeiIn = new RoutedCommand();
:
private void OpenTokuimeiSuggestionBox()
{
try
{
// Enable.
tokuimeiListPopup.Visibility = Visibility.Visible;
tokuimeiListPopup.IsOpen = true;
tokuimeiList.Visibility = Visibility.Visible;
}
catch (Exception ex)
{
// Info.
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
Console.Write(ex);
}
}
public void CloseTokuimeiSuggestionBox()
{
try
{
// Enable.
tokuimeiListPopup.Visibility = Visibility.Collapsed;
tokuimeiListPopup.IsOpen = false;
tokuimeiList.Visibility = Visibility.Collapsed;
}
catch (Exception ex)
{
// Info.
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
Console.Write(ex);
}
}
private void Tokuimei_TextChanged(object sender, TextChangedEventArgs e)
{
try
{
// Verification.
if (string.IsNullOrEmpty(tokuimei.Text))
{
// Disable.
this.CloseTokuimeiSuggestionBox();
SelectedTokui = null;
// Info.
return;
}
// Settings.
// TOKUIMEI の所は変更してください
var itemSourceList = tokuimeiSuggestionList.Where(x => x.TOKUIMEI != null && x.TOKUIMEI.Contains(tokuimei.Text, StringComparison.CurrentCultureIgnoreCase)).ToList();
if (itemSourceList.Count > 0)
{
tokuimeiList.ItemsSource = itemSourceList;
// Enable.
this.OpenTokuimeiSuggestionBox();
}
else
{
// Disable.
this.CloseTokuimeiSuggestionBox();
SelectedTokui = null;
}
}
catch (Exception ex)
{
// Info.
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
Console.Write(ex);
}
}
private void TokuimeiList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
// Verification.
if (tokuimeiList.SelectedIndex <= -1)
{
// Disable.
this.CloseTokuimeiSuggestionBox();
// Info.
return;
}
// Disable.
this.CloseTokuimeiSuggestionBox();
// Settings.
// Tokui の所は変更してください
SelectedTokui = ((Tokui)tokuimeiList.SelectedItem);
// ここで選択されたデータを表示します(この例では、郵便番号や住所など。tokuimei 以外の表示項目は必要に応じて作成します)
tokuimei.Text = SelectedTokui.TOKUIMEI;
yubinno.Text = SelectedTokui.YUBINNO;
jyusyo1.Text = SelectedTokui.JYUSYO1;
jyusyo2.Text = SelectedTokui.JYUSYO2;
telno.Text = SelectedTokui.TELNO;
faxno.Text = SelectedTokui.FAXNO;
tokuimeiList.SelectedIndex = -1;
}
catch (Exception ex)
{
// Info.
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
Console.Write(ex);
}
}
private void Tokuimei_OnPreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
this.CloseTokuimeiSuggestionBox();
}
}
private void TokuimeiInExec(object sender, ExecutedRoutedEventArgs e)
{
// ここに tokuimei の所に入力されて Enter を押された場合の処理を書きます
:
}
上へ戻る
ComboBox
CombpoBox で IsEditable=true とした場合、
SelectionChanged イベントが最初の1文字を入力した直後に発生します
SelectedIndex が -1 かチェックすれば回避できると思いますが、 上のサジェッション付きTextBox を少し改造して作ったもの
必要な時だけPopUpListを表示したい場合(例えば郵便番号を入れて住所が表示されるとかの、キーが時々重複していてその時だけ PopUpList から選択させたい)
などにも少しの改造で使えます
(PopupList を表示させるボタンの削除と TextBox を囲っている内側の StackPanel の定義(2,3行目)を削除して、プログラムで PopupList を表示させるなど)
<StackPanel HorizontalAlignment="Left" Margin="148,53,0,0" VerticalAlignment="Top">
<Border BorderThickness="1" BorderBrush="#FFABADB3" Grid.Column="1" Grid.Row="1">
<StackPanel Orientation="Horizontal">
<TextBox x:Name="textInputBox1" TextWrapping="Wrap" Text="" Width="180" Height="23" VerticalContentAlignment="Center" BorderThickness="0">
<TextBox.InputBindings>
<!-- Window1 は使用環境に合わせて変更してください-->
<KeyBinding Key="Enter" Command="{x:Static local:Window1.textInputBox1In}" />
</TextBox.InputBindings>
</TextBox>
<Button Width="14" Background="White" BorderThickness="0" Click="OpenPopUpListBox">
<Button.Content >
<TextBlock Text="⌄">
<TextBlock.RenderTransform>
<ScaleTransform ScaleX="1.0" ScaleY="0.8"/>
</TextBlock.RenderTransform>
</TextBlock>
</Button.Content>
</Button>
</StackPanel>
</Border>
<Popup x:Name="pouupListPopup" Visibility="Collapsed" StaysOpen="False" Placement="Bottom">
<ListBox x:Name="popuoList" Visibility="Collapsed" SelectionChanged="popUpList_SelectionChanged" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<!-- KUBUNMEI は使用環境に合わせて変更してください-->
<TextBlock Text="{Binding KUBUNMEI}" Width="180" Margin="4" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="BorderBrush" Value="LightBlue"></Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Popup>
</StackPanel>
// InitializeComponent();のあとで
:
CommandBindings.Add(new CommandBinding(textInputBox1In, textInputBox1InExec));
:
// Loaded か ContentRendaed でデータベースより表示用データを読む。Kubuns と KUBUNMEI の所は変更してください
:
kubuns = dbContext.Kubuns.OrderBy(x => x.KUBUNMEI).ToList();
popUpList.ItemsSource = kubuns;
:
:
// 処理コードの部分
:
public static RoutedCommand textInputBox1In = new RoutedCommand();
:
private void OpenPopUpListBox(object sender, RoutedEventArgs e)
{
try
{
// Enable.
popUpListPopup.Visibility = Visibility.Visible;
popUpListPopup.IsOpen = true;
popUpList.Visibility = Visibility.Visible;
}
catch (Exception ex)
{
// Info.
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
Console.Write(ex);
}
}
private void ClosepopUpListBox()
{
try
{
// Enable.
popUpListPopup.Visibility = Visibility.Collapsed;
popUpListPopup.IsOpen = false;
popUpList.Visibility = Visibility.Collapsed;
}
catch (Exception ex)
{
// Info.
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
Console.Write(ex);
}
}
private void popUpList_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
try
{
// Verification.
if (popUpList.SelectedIndex <= -1)
{
// Disable.
this.ClosepopUpListBox();
// Info.
return;
}
// Disable.
this.ClosepopUpListBox();
// Settings.
var selectedKakoKubno = ((Kubun)popUpList.SelectedItem);
textInputBox1.Text = selectedKakoKubno.KUBUNMEI;
popUpList.SelectedIndex = -1;
}
catch (Exception ex)
{
// Info.
MessageBox.Show(ex.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
Console.Write(ex);
}
// ここに popUpKist が選択された後の処理を書きます
:
}
private void textInputBox1InExec(object sender, ExecutedRoutedEventArgs e)
{
// ここに textInputBox1 の所に入力されて Enter を押された場合の処理を書きます
:
}
上へ戻る
PDFSharp の FontResolver
PDF の文書を作成するために、PDFsharp を使いました
nuget サイトによると、最新バージョンは 6.1.1 で .NET6.0 に対応しているとのことですが、.NET8 でも使えました(文字出力とbmp画像の張り付けだけしか試していません)
次のバージョン 6.2.0 は .NET8 に正式対応するようです
日本語を使用するのは FontResolver を用意しなければいけないのですが、いろいろ試して Arial 以外のフォントは FontResolver の記述が必要のようです
使えるフォントは ttf と otf で、windows に最初からインストールしてある 「游明朝」が日本語として使用可能です
以下のサンプルでは、游明朝 と Courier(Courier new) が使用できます
Notoフォントをインストールして使用した場合は、出力されるファイルにフォントが埋め込まれるため、1フォントあたり 10~20MB程出力ファイルが大きくなります
:
// プログラムの中で PDF を作成する部分
// 新しいドキュメントを作る前に FontResolver を登録する
PdfSharp.Fonts.GlobalFontSettings.FontResolver = new JapaneseFontResolver();
PdfDocument document = new PdfDocument();
:
:
// フォント指定の部分
// フォント名は FontResolverInfo の case 文で指定したものと同じ名前を使う
XFont font = new XFont("游明朝", 10, XFontStyleEx.Regular);
:
:
// 日本語を使うための FontResolver
//
public class JapaneseFontResolver : IFontResolver
{
public byte[]? GetFont(string faceName)
{
// Windowsのフォントフォルダーを指定する
string? variable = System.Environment.GetEnvironmentVariable("windir");
string fontPath = System.IO.Path.Combine(variable ?? "", "Fonts", faceName);
if (File.Exists(fontPath) == false)
{
// ユーザーのフォントフォルダーを指定する
variable = System.Environment.GetEnvironmentVariable("USERPROFILE");
fontPath = System.IO.Path.Combine(variable ?? "", @"AppData\Local\Microsoft\Windows\Fonts", faceName);
}
try
{
using (var fontStream = File.OpenRead(fontPath))
{
var fontData = new byte[fontStream.Length];
fontStream.Read(fontData, 0, fontData.Length);
return fontData;
}
}
catch
{
Console.WriteLine("フォントファイルを開けませんでした。");
return null;
}
}
public FontResolverInfo? ResolveTypeface(string fontName, bool isBold, bool isItalic)
{
switch (fontName)
{
// caseでのフォント名はフォント指定時とここで使うだけなので、適当につけて良い
// returnでフォントのファイル名を返す
case "游明朝":
if (isBold)
{
return new FontResolverInfo("Yumindb.ttf");
}
else if (isItalic)
{
return new FontResolverInfo("Yuminl.ttf");
}
else
{
return new FontResolverInfo("Yumin.ttf");
}
case "Courier":
if (isBold && isItalic)
{
return new FontResolverInfo("courbi.ttf");
}
if (isBold)
{
return new FontResolverInfo("courbd.ttf");
}
else if (isItalic)
{
return new FontResolverInfo("couri.ttf");
}
else
{
return new FontResolverInfo("cour.ttf");
}
}
// デフォルトのフォント
return PlatformFontResolver.ResolveTypeface("Arial", isBold, isItalic);
}
}
上へ戻る
OpenCvSharp4 の RotatedRect の Angle で得られる角度について
PCに接続したカメラを使って、伝票を取り込んでPDFにするプログラムを作成しました
RotatedRect.Angle で傾きを求めて、Cv2.WarpAffine で傾き補正を行ったのですが、得られる角度が下の図のようになっていました
RotatedRect が右に傾いていた時

RotatedRect が左に傾いていた時

左右の傾きで異なる辺の角度が得られるので Cv2.GetRotationMatrix2D に与える角度に、
得られた角度が45度以上の場合は「角度 - 90.0」として使用しました