【.NET】クリックしたときに全選択されるテキストボックスを作成するシンプルな方法

f:id:ktts:20190601225751p:plain

ブラウザーのアドレスバーみたいなTextBoxを作成する必要があったので調べてみましたが、
シンプルな方法が欲しかったので記事にします。

参考サイト
stackoverflow.com

対象のテキストボックスに下記のEnterイベントを設定します。

C#の場合

/// <summary>
/// アクティブになったときにテキストを全選択します。
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void MyTextBox_Enter(object sender, EventArgs e)
{
    BeginInvoke(new Action(myTextBox.SelectAll));
}

VB.NETの場合

''' <summary>
''' アクティブになったときにテキストを全選択します。
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
Private Sub MyTextBox_Enter(sender As Object, e As EventArgs) Handles myTextBox.Enter
    BeginInvoke(New Action(AddressOf myTextBox.SelectAll))
End Sub

しくみ
普通にEnterイベントでSelectAllしても、選択後にクリックが発生して選択状態が解除されます。
そこでBeginInvokeを利用することでSelectAllが非同期実行されて、期待した動作が実現されました。

※今回は引数なし戻り値なしメソッドのdelegateを取得するためにAction(void() target)デリゲートを利用していますが、
引数があったり戻り値があったりするメソッドの場合は記法が変わってくるので、
”how to convert method to delegate”等でググってみてください。

【C#】【VB.NET】要素が含まれているかAny()したらArgumentNullExceptionが発生する

f:id:ktts:20190418202442p:plain

インスタンス生成していないならエラーが起きるのも当然ちゃ当然でしょうが、
生成されているか判断する必要がある場合もあるでしょう。
if ((list != null) && (list.Any()))とかすればいいんでしょうけど、
String.IsNullOrEmptyみたいな便利なモノはないかと調べてみました。

参考サイト
stackoverflow.com

使用例

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();

        List<string> list = null;
        if (list.AnyOrDefault())
        {
            Console.WriteLine("The List has any item.");
        }
        else
        {
            Console.WriteLine("The List does not has any item.");
        }
    }
}

/// <summary>
/// 拡張メソッドを定義する静的クラス
/// </summary>
public static class Extentions
{
    /// <summary>
    /// null参照エラーではfalseを返す拡張Anyメソッド
    /// </summary>
    /// <typeparam name="T">Type</typeparam>
    /// <param name="source">Source</param>
    /// <returns></returns>
    public static bool AnyOrDefault<T>(this IEnumerable<T> source)
    {
        return (source?.Any() ?? false);
    }
}

エラーを発生させないで結果を得ることができました。
f:id:ktts:20190418210120p:plain

ちな、VB.NETの場合はnull合体演算子(??)がないので(たぶん)、そのまま使うことができません。

使用例(VB.NET

Public Class Form1
    Sub New()

        ' この呼び出しはデザイナーで必要です。
        InitializeComponent()

        ' InitializeComponent() 呼び出しの後で初期化を追加します。

        Dim list As List(Of String) = Nothing
        If list.AnyOrDefault() Then
            Console.WriteLine("The List has any item.")
        Else
            Console.WriteLine("The List does not has any item.")
        End If
    End Sub
End Class

Module Extentions
    <System.Runtime.CompilerServices.Extension()>
    Public Function AnyOrDefault(Of type)(source As IEnumerable(Of type)) As Boolean
        Return If(source?.Any() Is Nothing, False, source.Any())
    End Function
End Module

【C#】インデックスやフィールド名で構造体の値を取得する

インデックス(添え字)やフィールド名で構造体の値を列挙したり取得したりする方法。
ちなみにクラスでも(1行目のstructをclassに変更)同様の結果が得られます。

参考サイト
stackoverflow.com

struct MyStruct
{
    public string Name;
    public int Age;
    public string Address;

    public MyStruct(string p1, int p2, string p3)
    {
        Name = p1;
        Age = p2;
        Address = p3;
    }
}

private void Test()
{
    MyStruct myStruct = new MyStruct("くまモン", 5, "熊本県");

    // FieldInfoを取得する
    Type typeOfMyStruct = typeof(MyStruct);
    System.Reflection.FieldInfo[] fieldInfos = typeOfMyStruct.GetFields();

    // フィールドを列挙する
    foreach (var fieldInfo in fieldInfos)
    {
        Console.WriteLine($"{fieldInfo.Name}:{fieldInfo.GetValue(myStruct)}");
    }

    // インデックスで取得する
    for (int i = 0; i < fieldInfos.Length; i++)
    {
        Console.WriteLine($"{fieldInfos[i].Name}:{fieldInfos[i].GetValue(myStruct)}");
    }

    // フィールド名で取得する
    Console.WriteLine($"{fieldInfos.FirstOrDefault(x => x.Name == "Name").Name}:" +
        $"{fieldInfos.FirstOrDefault(x => x.Name == "Name").GetValue(myStruct)}");
    Console.WriteLine($"{fieldInfos.FirstOrDefault(x => x.Name == "Age").Name}:" +
        $"{fieldInfos.FirstOrDefault(x => x.Name == "Age").GetValue(myStruct)}");
    Console.WriteLine($"{fieldInfos.FirstOrDefault(x => x.Name == "Address").Name}:" +
        $"{fieldInfos.FirstOrDefault(x => x.Name == "Address").GetValue(myStruct)}");
}

結果
f:id:ktts:20190409003337p:plain

※Type.GetFieldsメソッドで取得されるフィールド順序は、常に一定であることが保障されていないようです。
必要であるなら、下記サイトで示されるようにMetadataTokenプロパティで整列してからのほうが良いとのこと。

参考サイト
stackoverflow.com

    // インデックスで取得する
    fieldInfos = fieldInfos.OrderBy(x => x.MetadataToken).ToArray();
    for (int i = 0; i < fieldInfos.Length; i++)
    {
        Console.WriteLine($"{fieldInfos[i].Name}:{fieldInfos[i].GetValue(myStruct)}");
    }

【C#】【VB.NET】プリンターの印刷が終了するまで待つ

プリンターの印刷が終了するまで待つ方法を調べたのでメモ。
調べるきっかけがVisualBasic案件だったのでVB.NET版も。
下記コードは最小限なので必要に応じてエラー処理なりタイムアウト処理なり設定してください。

参考サイト
stackoverflow.com


C#

using System.Printing;

/// <summary>
/// プリンターのキューが終了するまで待機します。
/// </summary>
/// <param name="printerName">プリンター名</param>
private void WaitUntilEndOfPrinting(string printerName)
{
    LocalPrintServer localPrintServer = new LocalPrintServer();
    PrintQueue printQueue = localPrintServer.GetPrintQueue(printerName);
    PrintJobInfoCollection printJobInfos = printQueue.GetPrintJobInfoCollection();

    foreach (PrintSystemJobInfo printJobInfo in printJobInfos)
    {
        bool isEnded = false;
        while (!isEnded)
        {
            System.Threading.Thread.Sleep(1000);
            
            printQueue.Refresh();
            printJobInfo.Refresh();
            isEnded = printJobInfo.IsCompleted || printJobInfo.IsDeleted || printJobInfo.IsPrinted;
        }
    }
}



VB.NET

Imports System.Printing

''' <summary>
''' プリンターのキューが終了するまで待機します。
''' </summary>
''' <param name="printerName">プリンター名</param>
Private Sub WaitUntilEndOfPrinting(printerName As String)
    Dim localPrintServer As New LocalPrintServer
    Dim printQueue As PrintQueue = localPrintServer.GetPrintQueue(printerName)
    Dim printJobInfos As PrintJobInfoCollection = printQueue.GetPrintJobInfoCollection()

    For Each printJobInfo As PrintSystemJobInfo In printJobInfos
        Dim isEnded As Boolean = False
        While (Not isEnded)
            System.Threading.Thread.Sleep(1000)

            printQueue.Refresh()
            printJobInfo.Refresh()
            isEnded = printJobInfo.IsCompleted Or printJobInfo.IsDeleted Or printJobInfo.IsPrinted
        End While
    Next
End Sub

【Excel】図として貼り付けしたら文字化けした話

f:id:ktts:20190122000425p:plain

Excelであるセル範囲を図として貼り付けしたら文字化けした経験を書きます。

【再現手順】
Win10Excel2016環境でブックを新規作成する

  1. 挿入>数式を選択して、数式オブジェクトを作成する(一番上のA=πr²とかで)
  2. 作成した数式の一部(イコールとか)を全角文字で上書きするとFontが游ゴシック(本文)になる
  3. 数式を含むセル範囲をコピーして、図として貼り付けする
  4. ブックを保存して閉じる

上記で作成したブックをWin7Excel2010環境で開くと、
手順3.でコピペした図に含まれる、数式の全角文字が文字化けする



Win10の標準フォントになっている游ゴシックがあやしい気がする・・・
Excelから新規作成した場合と、エクスプローラーから新規作成した場合で標準フォントが変わるし
日本語圏特有の症状のようで、英語では関連情報は見つかりませんでした
数式オブジェクトが含まれる場合は全角/半角に注意しよう

【C#】JPEG画像ファイルを8bitTIFFフォーマットに変換する(スキャン画像のファイルサイズを削減する)

大量のスキャン画像のファイルサイズを削減する必要があっていろいろ検証したので手法を覚書します

スキャン画像の概要
・2色刷りの印刷物に手書きしたもの
・ファイルフォーマットはJPEG

(タイトルに結論が書いてありますがw)PNG・GIFF・TIFFで比較したところ、
この条件では色深度を削ったTIFF形式がファイルサイズ・変換速度で優れていました。
見栄えを考慮すると8bitが限界でしたが、グレースケールなら4bitでもいけそうな感じ。
ただし、TIFFに変換できても色深度や圧縮形式まで指定できる方法は限られており、
簡易な方法では、調べた限りImageMagickくらいしかありませんでした。

今回の場合は大量の画像ファイルを処理する必要があったので、
C#でEncoderParametersを指定する手法を選択しました。

参考サイト
stackoverflow.com

using System.IO;
using System.Drawing;
using System.Drawing.Imaging;

// 変換する画像形式を設定する
ImageFormat imageFormat = ImageFormat.Tiff;
long colorDepth = 8L;
EncoderValue compression = EncoderValue.CompressionLZW;

// EncoderParametersを設定する
EncoderParameters parameters = new EncoderParameters(2);
parameters.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.ColorDepth, colorDepth);
parameters.Param[1] = new EncoderParameter(System.Drawing.Imaging.Encoder.Compression, (long)compression);

// ImageCodecInfoを取得する
ImageCodecInfo imageCodecInfo = ImageCodecInfo.GetImageEncoders().FirstOrDefault(x => x.FormatID == imageFormat.Guid);

// 画像形式の拡張子を取得する
string extention = imageCodecInfo.FilenameExtension.Split(';')[0].Replace("*","").ToLower();

// ファイル名を設定する
string sourceFile = @"C:\temp\source.jpg";
string destinationFile = Path.GetDirectoryName(sourceFile) + @"\" + Path.GetFileNameWithoutExtension(sourceFile) + extention;

// 画像形式を変換する
using (Bitmap bitmap = new Bitmap(sourceFile))
{
    bitmap.Save(destinationFile, imageCodecInfo, parameters);
}

【C#】画像ファイルを指定したサイズ(幅×高さ)に変更する

大量の画像ファイルを指定の縦横サイズにリサイズする必要があり、手法を調べたのでノートします。

1.アスペクト比を維持しないでサイズ変更する
アスペクト比とは長辺と短辺の比率のことで、いわゆる画像の縦横比率です。
この方法では元画像と目的サイズのアスペクト比が異なる場合は、
縦横どちらかに間延びした画像に変わることになります。
最初に試した手法はこちらだったのですが、
目的に対しては好ましくないだろうということで方法2を模索することになりました。
念のためコード例を示します。

参考サイト
stackoverflow.com

using System.Drawing;

/// <summary>
/// 画像ファイルのアスペクト比を変更してサイズを変更します
/// </summary>
/// <param name="sourceFile">サイズ変更する画像ファイル</param>
/// <param name="destinationFile">サイズ変更した画像ファイル</param>
/// <param name="imageFormat">画像ファイル形式</param>
/// <param name="width">変更する幅</param>
/// <param name="height">変更する高さ</param>
public void ResizeImageWhileChangingAspectRatio(string sourceFile,
    string destinationFile,
    System.Drawing.Imaging.ImageFormat imageFormat,
    int width,
    int height)
{
    // サイズ変更する画像ファイルを開く
    using (Image image = Image.FromFile(sourceFile))
    {
        // サイズ変更した画像を作成する
        using (Bitmap bitmap = new Bitmap(width, height))
        using (Graphics graphics = Graphics.FromImage(bitmap))
        {
            // ラップモードを設定する
            using (System.Drawing.Imaging.ImageAttributes wrapMode = new System.Drawing.Imaging.ImageAttributes())
            {
                wrapMode.SetWrapMode(System.Drawing.Drawing2D.WrapMode.TileFlipXY);
                graphics.DrawImage(image, new Rectangle(0, 0, width, height), 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);

                // サイズ変更した画像を保存する
                bitmap.Save(destinationFile, imageFormat);
            }
        }
    }
}


2.アスペクト比を維持したままでサイズ変更する
目的サイズと元画像のアスペクト比が異なる場合は変更後の画像の右端or下端に空き領域ができてしまうので、
予め変更後の画像を塗りつぶしてから(コード例では黒)、サイズ変更した元画像を配置します。
なお、コード例では’変更サイズを取得する’の端数処理の関係で長辺の端に1ピクセルの背景色が残る場合があります。

参考サイト
stackoverflow.com

using System.Drawing;

/// <summary>
/// 画像ファイルのアスペクト比を維持してサイズを変更します
/// </summary>
/// <param name="sourceFile">サイズ変更する画像ファイル</param>
/// <param name="destinationFile">サイズ変更した画像ファイル</param>
/// <param name="imageFormat">画像ファイル形式</param>
/// <param name="width">変更する幅</param>
/// <param name="height">変更する高さ</param>
public void ResizeImageWhileMaintainingAspectRatio(string sourceFile, 
    string destinationFile, 
    System.Drawing.Imaging.ImageFormat imageFormat, 
    int width, 
    int height)
{
    // サイズ変更する画像ファイルを開く
    using (Image image = Image.FromFile(sourceFile))
    {
        // 変更倍率を取得する
        float scale = Math.Min((float)width / (float)image.Width, (float)height / (float)image.Height);

        // サイズ変更した画像を作成する
        using (Bitmap bitmap = new Bitmap(width, height))
        using (Graphics graphics = Graphics.FromImage(bitmap))
        {
            // 変更サイズを取得する
            int widthToScale = (int)(image.Width * scale);
            int heightToScale = (int)(image.Height * scale);

            // 背景色を塗る
            SolidBrush solidBrush = new SolidBrush(Color.Black);
            graphics.FillRectangle(solidBrush, new RectangleF(0, 0, width, height));

            // サイズ変更した画像に、左上を起点に変更する画像を描画する
            graphics.DrawImage(image, 0, 0, widthToScale, heightToScale);

            // サイズ変更した画像を保存する
            bitmap.Save(destinationFile, imageFormat);
        }
    }
}