テキストボックスに数値型のデータバインドをした時の不正文字入力後のフォーカス変更

TextBoxのTextにデータバインディングをした時に、バインディング元が数値型だと入力エラーになりフォーカスが外せなくなります。

FormのAutoValidateプロパティをAutoValidate.EnableAllowFocusChangeにするとフォーカスが外せるようになります。

ウインドウの位置を保存・復元する

プログラムを再度起動した時に前回のウインドウ位置やサイズを保存・復元する方法です。
保存する情報は
・ウインドウのサイズ(Width,Height)
・ウインドウの位置(Location)
・ウインドウの状態(最大化、最小化)
になります。

また、起動した時に画面からはみ出ている状態では困るのでそこも修正します。

ウインドウの情報を保存する

ウインドウのサイズ、位置、状態のやりとりはWinAPIを使用したほうが便利です。
画面外に吹っ飛ぶようなウインドウでも、画面内に収まるように自動で修正してくれます。

こんな感じのクラスを作って

public class WinAPI {
 // WinAPI
 // ウインドウ情報をセットする
 [DllImport("user32.dll")]
 public static extern bool SetWindowPlacement(
 	IntPtr hWnd,
 	[In] ref WINDOWPLACEMENT lpwndpl
 );
 //WinAPI
 // ウインドウ情報を取得する
 [DllImport("user32.dll")]
 public static extern bool GetWindowPlacement(
 	IntPtr hWnd,
 	out WINDOWPLACEMENT lpwndpl
 );
 
 // 以下 WinAPIで使用するクラス定義
 public struct WINDOWPLACEMENT {
 	public int length;
 	public int flags;
 	public SW showCmd;
 	public POINT minPosition;
 	public POINT maxPosition;
 	public RECT normalPosition;
 }
 
 [StructLayout(LayoutKind.Sequential)]
 public struct POINT {
 	public int X;
 	public int Y;
 
 	public POINT( int x, int y ) {
 		this.X = x;
 		this.Y = y;
 	}
 }
 
 [StructLayout(LayoutKind.Sequential)]
 public struct RECT {
 	public int Left;
 	public int Top;
 	public int Right;
 	public int Bottom;
 
 	public RECT( int left, int top, int right, int bottom ) {
 		this.Left = left;
 		this.Top = top;
 		this.Right = right;
 		this.Bottom = bottom;
 	}
 }
 
 public enum SW {
 	HIDE = 0,
 	SHOWNORMAL = 1,
 	SHOWMINIMIZED = 2,
 	SHOWMAXIMIZED = 3,
 	SHOWNOACTIVATE = 4,
 	SHOW = 5,
 	MINIMIZE = 6,
 	SHOWMINNOACTIVE = 7,
 	SHOWNA = 8,
 	RESTORE = 9,
 	SHOWDEFAULT = 10,
 }
}

こんな感じで使ってます。

public void ウインドウ情報の取得( Control c ) {
	WINDOWPLACEMENT wp = new WINDOWPLACEMENT();
	WINAPI.GetWindowPlacement(c.Handle, out wp);
}

public void ウインドウ情報を設定( WINDOWPLACEMENT wp ) {
	if( wp.length != 0 ) {
		wp.length = Marshal.SizeOf(typeof(WINDOWPLACEMENT));
		wp.flags = 0;
		wp.showCmd = ( wp.showCmd == SW.SHOWMINIMIZED ? SW.SHOWNORMAL : wp.showCmd );
		IntPtr hwnd = c.Handle;
		WinAPI.SetWindowPlacement(hwnd, ref wp);
	}
}


参考:http://grabacr.net/archives/1585

リフレクション

最近WPFを触っています。
が、ほしいフィールドやプロパティがプライベートだったりして歯がゆい思いをよくしてます。

気に入らないのでリフレクションで持ってきてしまいましょう。
.NET FOLLOWER » C#: How to set or get value of a private or internal property through the Reflection

リフレクションに便利な拡張メソッド

public static class ReflectionHelper
{
 private static PropertyInfo GetPropertyInfo(Type type, string propertyName)
 {
   PropertyInfo propInfo = null;
   do
   {
     propInfo = type.GetProperty(propertyName,
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
     type = type.BaseType;
   }
   while (propInfo == null && type != null);
   return propInfo;
 }
 
 public static object GetPropertyValue(this object obj, string propertyName)
 {
   if (obj == null)
     throw new ArgumentNullException("obj");
   Type objType = obj.GetType();
   PropertyInfo propInfo = GetPropertyInfo(objType, propertyName);
   if (propInfo == null)
     throw new ArgumentOutOfRangeException("propertyName",
       string.Format("Couldn't find property {0} in type {1}", propertyName, objType.FullName));
   return propInfo.GetValue(obj, null);
 }
 
 public static void SetPropertyValue(this object obj, string propertyName, object val)
 {
    if (obj == null)
      throw new ArgumentNullException("obj");
    Type objType = obj.GetType();
    PropertyInfo propInfo = GetPropertyInfo(objType, propertyName);
    if (propInfo == null)
      throw new ArgumentOutOfRangeException("propertyName",
        string.Format("Couldn't find property {0} in type {1}", propertyName, objType.FullName));
    propInfo.SetValue(obj, val, null);
 }
}

Blend for Visual Studio のデザイン表示について

WPFのデザイン(Xaml)表示がされなくなってしまう事がありました。
調べた結果、Blend for Visual Studio 2013 で .NET Framework 4.5 より上のバージョンを使用するとデザイン表示ができないようです。

.NET Framework 4.5.1
.NET Framework 4.5.2
.NET Framework 4.6
このバージョンを使うときは Blend for Visual Studio 2015 を使いましょう!

Windows API Code Packのインストール

Windows7以降に追加された機能を使用するには追加ライブラリを入れると簡単です。
以前はMicrosoftのコードギャラリーから入手できましたが消滅してしまいました。
現在ではNuGetから入手・インストールします。

NuGetの画面
f:id:kitunechan:20150730134815p:plain

  • Core
  • Shell
  • ShellExtensions
  • DirectX
  • ExtendedLinguisticServices
  • Sensors


Windows API Code Packで何ができるのかは参考リンクを参照で。。。


ちなみにCoreとShellをインストールした状態では、参照設定で以下のものが必要でした。

  • PresentationCore
  • PresentationFramework
  • System.Xaml
  • WindowsBase


参考リンク:Windows API Code Pack連載 第1回~第10回の一覧 - 田中達彦のブログ - Site Home - MSDN Blogs
Windows API Code Pack連載 第1回~第10回の一覧 | 田中達彦のブログ


Windows API Code PackのソースはGithubで公開されています。
github.com

オブジェクトをファイルに保存する

自分で作成したクラスをファイルに保存する方法です。
DataContractSerializerを使用してXML形式で保存します。

DataContractSerializer は System.Runtime.Serialization を使います。
多分初期設定では参照設定がないので追加して下さいね。


早速ですが、読み書き用のクラスにまとめました。 ジェネリック仕様になります。

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Xml;
public class FileXML {
  /// <summary>
  /// XMLファイルに保存
  /// </summary>
  static public bool Save<T>( T obj, string FilePath ) {
    try {
      // XmlSerializerを使ってファイルに保存(T型オブジェクトの内容を書き込む)
      var serializer = new DataContractSerializer(typeof(T));

      using( var fs = new FileStream(FilePath, FileMode.Create) )
      using( var xw = XmlWriter.Create(fs, new XmlWriterSettings {
        Indent = true,
        IndentChars = "\t"
      }) ) {
        // オブジェクトをシリアル化してXMLファイルに書き込む
        serializer.WriteObject(xw, obj);
      }

    } catch( Exception ) {
      return false;
    }
    return true;
  }

  /// <summary>
  /// XMLファイルを読み込み
  /// </summary>
  static public T Load<T>( string FilePath ) where T: new() {
    try {
      using( var sr = new StreamReader(FilePath) )
      using( var xr = XmlReader.Create(sr, new XmlReaderSettings() )) {
        // XMLをオブジェクトに読み込む
        var serializer = new DataContractSerializer(typeof(T));

        // XMLファイルを読み込み、逆シリアル化(復元)する
        return (T)serializer.ReadObject(xr);
      }
    } catch( Exception ) {
      return default(T);
    }
  }
}


保存するオブジェクトのクラスを作成します。

public class 保存クラス {
	public int PublicNum1;

	private int PrivateNum2;
	public void SetNumber(int num) {
		this.PrivateNum2 = num;
	}

	public int PublicProperty3 { get; set; }

	private int PrivateProperty4 { get; set; }
	public void SetNumberProperty( int num ) {
		this.PrivateProperty4 = num;
	}
}

実際に使うとこんな感じ

// 保存
var Save = new 保存クラス();
  Save.PublicNum1 = 100;
  Save.SetNumber(200);
  Save.PublicProperty3 = 300;
  Save.SetNumberProperty(400);

FileXML.Save<保存クラス>(Save, "./保存.xml");


// 読込
var Load = FileXML.Load<保存クラス>("./保存.xml");
// Load
//   PublicNum1      100
//   PrivateNum2       0
//   PublicProperty3   300
//   PrivateProperty4    0

出力されるファイルの中身がこちら
読込したデータからわかるようにPublicのものが全て保存されます

<?xml version="1.0" encoding="utf-8"?>
<保存クラス xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/%E3%82%B7%E3%83%AA%E3%82%A2%E3%83%AB">
	<PublicNum1>100</PublicNum1>
	<PublicProperty3>300</PublicProperty3>
</保存クラス>

Privateや保存したいデータを指定する場合は[DataContract]属性をクラスにつけて、保存したいフィールドやプロパティに[DataMember]属性を付けます。

using System.Runtime.Serialization;

[DataContract]
public class 保存クラス {
	[DataMember]
	public int PublicNum1;

	[DataMember]
	private int PrivateNum2;
	public void SetNumber(int num) {
		this.PrivateNum2 = num;
	}

	//[DataMember] ← 付けないと保存されない
	public int PublicProperty3 { get; set; }

	[DataMember]
	private int PrivateProperty4 { get; set; }
	public void SetNumberProperty( int num ) {
		this.PrivateProperty4 = num;
	}
}

PublicProperty3 は Public ですが [DataMember]をつけていないので保存されません。

<?xml version="1.0" encoding="utf-8"?>
<保存クラス xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/%E3%82%B7%E3%83%AA%E3%82%A2%E3%83%AB">
	<PublicNum1>100</PublicNum1>
	<PrivateNum2>200</PrivateNum2>
	<PrivateProperty4>400</PrivateProperty4>
</保存クラス>


ちなみにオブジェクトなら何でも保存できるので、こんなのもできます

var tuple = Tuple.Create(10, 20, 30, "string");
FileXML.Save<Tuple<int,int,int,string>>(tuple, "./保存.xml");
<?xml version="1.0" encoding="utf-8"?>
<TupleOfintintintstring xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/System">
	<m_Item1>10</m_Item1>
	<m_Item2>20</m_Item2>
	<m_Item3>30</m_Item3>
	<m_Item4>string</m_Item4>
</TupleOfintintintstring>


こんな感じで好き放題保存してやりましょう!


参考:
DataContractSerializerを使って、オブジェクトのXMLシリアル化、逆シリアル化を行う: .NET Tips: C#, VB.NET
neue cc - .NETの標準シリアライザ(XML/JSON)の使い分けまとめ

[Windows Form] データバインドのやり方

テキストボックスの内容をいちいち変数に入れる処理を書くのはめんどくさいですね。
データバインドを使いましょう。

Visual Studioのデザイナーで設定すると簡単にできます。
スクリーンショットではアドイン画面がいくつか含まれています

適当にフォームを作りました。
テキストボックスが3つあるだけです。
f:id:kitunechan:20150416114723p:plain

続きを読む

ComboBoxの見た目と中身を別にする

コンボボックスの見た目と欲しいデータが別なんてことはよくあることですね。
簡単な物だったら匿名クラスを使ってちょいちょいです。

this.comboBox1.ValueMember = "value";
this.comboBox1.DisplayMember = "display";
this.comboBox1.DataSource = new[]{
	new { value="1", display="でーた1" },
	new { value="2", display="でーた2"},
	new { value="3", display="でーた3"},
	new { value="4", display="でーた4"},
	new { value="5", display="でーた5"},
};
//ValueMember と DisplayMember はデザイナーの方で指定したほうがスッキリすると思うます

DisplayMember が見た目に使うプロパティ名
ValueMember が実際のデータのプロパティ名 になります。

this.comboBox1.SelectedValueにデータバインドをしておけばvalueが勝手に取得できて楽になります。

ユーザーコントロールのスナップを自作する

ユーザーコントロールを作成してテキストボックスなんか置いたりすると、スナップ(ピンクの線)が消えてしまって配置するのがめんどくさくなります。
そこで、ユーザーコントロールにもスナップを追加します。

とりあえずUserControlを継承したクラスを作成します。
この後作成するスナップ付きのユーザーコントロールにこのクラスを使います。

[Designer(typeof(UserControlSnapLineDesigner))]
public class UserControlBase: UserControl {
	protected virtual Control SnapLineControl { get { return null; } }

	private class UserControlSnapLineDesigner: ControlDesigner {
		public override IList SnapLines {
			get {
				IList snapLines = base.SnapLines;

				Control targetControl = ( this.Control as UserControlBase ).SnapLineControl;

				if( targetControl == null )
					return snapLines;

				using( ControlDesigner controlDesigner = TypeDescriptor.CreateDesigner(targetControl,
					typeof(IDesigner)) as ControlDesigner ) {
					if( controlDesigner == null )
						return snapLines;

					controlDesigner.Initialize(targetControl);

					foreach( SnapLine line in controlDesigner.SnapLines ) {
						if( line.SnapLineType == SnapLineType.Baseline ) {
							snapLines.Add(new SnapLine(SnapLineType.Baseline, line.Offset + targetControl.Top,
								line.Filter, line.Priority));
							break;
						}
					}
				}
				return snapLines;
			}
		}
	}
}

先ほど作ったクラスを使用してユーザーコントロールを作成します。
作成するユーザーコントロールにはテキストボックスが配置してあり、このテキストボックスのスナップを持ってきます。

UserControlBase で作成したSnapLineControlをオーバーライドでコントロールを指定します。

public partial class ユーザーコントロール: UserControlBase {
	
	// ~ 細かいところは省略します ~
	
	//ここで指定するだけ↓
	protected override Control SnapLineControl {
		get {
			return this.textBox1;
		}
	}
}

これでテキストボックスのピンクの線がユーザーコントロールにもつくようになりました。

引用:.net - Baseline snaplines in custom Winforms controls - Stack Overflow

自作プロパティを(DataBindings)に追加する方法

Visual Studioで自作プロパティをデザイナー内の(DataBindings)に追加する方法

System.ComponentModel.Bindable(true)をプロパティに付けましょう

[System.ComponentModel.Bindable(true)]
public int プロパティ{ get;set; }

デザイナーから直接いじれるのは便利です。

キーが押されているか調べる

Control.MouseButtons とか Control.ModifierKeysを使うだけ

2つのキー同時押しはビット演算を使いましょう。

if( ( Control.MouseButtons & MouseButtons.Right ) == MouseButtons.Right ) {
	if( ( Control.ModifierKeys & ( Keys.Control | Keys.Shift ) ) == ( Keys.Shift | Keys.Control ) ) {
		// キー押されてます
	}
}

はてなブログのソースコード記述

はてな記法で、csは小文字じゃないとダメなのね・・・

>|cs|
// C#
/// <summary>
/// MainWindow の ViewModel です。
/// </summary>
class MainWindowViewModel {
    /// <summary>
    /// Hoge を取得または設定します。
    /// </summary>
    public String Hoge { get; set; }
}
 ||<
↑ここのスペースを消すべし!