読者です 読者をやめる 読者になる 読者になる

Visual Studio 再起動の拡張機能の紹介

インテリセンスのウインドウが表示されなくなるとか、
Xamlでデザインいじってる時にGUIからプロパティが操作効かなくなったり、
と割とよく不具合があるので再起動しまくりです。
[×] 押して毎回起動するのめんどくせーーーーーと思ってたらありました。

Visual Studio Restart
visualstudiogallery.msdn.microsoft.com

通常起動と管理者で起動があるのでご注意下さい。
あと、Undoの情報も消えちゃうよね。

Visual Studio 許すまじ

Visual Studio 2013 にて
Showで作成した小ウインドウを閉じた時に親ウインドウが見えなくなってしまうという不思議な現象に見舞われました・・・
どうも原因は親ウインドウよりもVisual Studioがしゃしゃり出てきて前面に表示されるため、
親ウインドウはアクティブ状態だけど表示されてない感じになってました。


Visual Studio再起動したら直りました。


調子悪い時はVisual Studio再起動しよ。。。

続きを読む

定義済みコマンドを使っていこう

既に定義がされているコマンドがいくつかあります。

System.Windows.Input 名前空間

  • ComponentCommands
     ちょっと特殊。あまり使う機会はないんじゃなかろうか
  • MediaCommands
     Play や Stop など音楽や動画を扱うときに便利なコマンドを持つ
  • NavigationCommands
     ブラウザに類似したコマンドを持つ


System.Windows.Documents 名前空間

  • EditingCommands
     エディタでよく使われるコマンドを持つ

引用:定義済みコマンドまとめ - しばやん雑記


ショートカットキーが定義されていたり、Copy、Pasteなどに至っては各コントロールにより処理が入っていたりして便利です。

これを使わない手はない!と思ったのですが、手順がわかるまでが大変でした。

処理の追加

Windowクラス(UIElementなら何でもOK)のCommandBindingsで処理を追加します。
※ 方法がいくつかあるので一つの手段として見て下さい。

public partial class MainWindow: Window {
    public MainWindow() {
        InitializeComponent();
        this.CommandBindings.Add( new CommandBinding( ApplicationCommands.New, New, CanNew  ) );
    }

    // 実行されるメソッド
    public void New( object sender, ExecutedRoutedEventArgs e ){
        MessageBox.Show( "New" );
    }

    // こんなのでもおっけー 
    //public ExecutedRoutedEventHandler New {
    //    get {
    //        return ( s, e ) => {
    //            MessageBox.Show( "New" );
    //        };
    //    }
    //}

    public void CanNew ( object sender, CanExecuteRoutedEventArgs e ){
        // 実行できるなら e.CanExecute = true;
        // 実行できない(グレー表示)なら e.CanExecute = false;
        e.CanExecute = true;
    }
}

この記述で

<Menu DockPanel.Dock="Top" Margin="0,0,0,1">
	<MenuItem Header="ファイル(_F)">
		<MenuItem Command="New"/>
	</MenuItem>
</Menu>

文字もショートカットキーも出てる!!便利ー!!
f:id:kitunechan:20151112182715p:plain

ショートカットキーの追加

Newコマンドでは Ctrl+N が初めから定義されていましたが、名前を付けて保存 の SaveAs にはショートカットキーが割り当てられていませんでした。
自分で割り当てろってことですね。

ショートカットキーを割り当てて、Ctrl+Shift+S の表示もさせるにはデザインが出来上がる前に記述する必要がありました。

つまり ApplicationCommands.SaveAs に Ctrl+Shift+S を割り当てるには InitializeComponent より前に書いてしまうか、

public MainWindow() {  
    ApplicationCommands.SaveAs.InputGestures.Add( new KeyGesture( Key.S, ModifierKeys.Control | ModifierKeys.Shift ) );
    InitializeComponent();  
}  

静的コンストラクタ(Static)に追加して最速で呼び出します。

static MainWindow() {  
    ApplicationCommands.SaveAs.InputGestures.Add( new KeyGesture( Key.S, ModifierKeys.Control | ModifierKeys.Shift ) );
}

全体像はこんな感じになりました。

public partial class MainWindow: Window {
    public MainWindow() {
        InitializeComponent();

        this.CommandBindings.Add( new CommandBinding( ApplicationCommands.New, New, CanNew ) );

        // SaveAs はラムダ式で強引に書いちゃう
        this.CommandBindings.Add( new CommandBinding( ApplicationCommands.SaveAs, ( s, e ) => {
            MessageBox.Show( "SaveAs" );
        } ) );
    }

    static MainWindow() {  
        ApplicationCommands.SaveAs.InputGestures.Add( new KeyGesture( Key.S, ModifierKeys.Control | ModifierKeys.Shift ) );
    }

    // 実行されるメソッド
    public void New( object sender, ExecutedRoutedEventArgs e ) {
        MessageBox.Show( "New" );
    }

    // こんなのでもおっけー 
    //public ExecutedRoutedEventHandler New {
    //    get {
    //        return ( s, e ) => {
    //            MessageBox.Show( "New" );
    //        };
    //    }
    //}

    public void CanNew( object sender, CanExecuteRoutedEventArgs e ) {
        // 実行できるなら e.CanExecute = true;
        // 実行できない(グレー表示)なら e.CanExecute = false;
        e.CanExecute = true;
    }
}

Xaml

<Window x:Class="LivetWPFApplication1.Views.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <DockPanel>
        <Menu DockPanel.Dock="Top">
            <MenuItem Header="ファイル">
                <MenuItem Command="New"/>
                <MenuItem Command="SaveAs"/>
            </MenuItem>
        </Menu>
        <DockPanel></DockPanel>
    </DockPanel>
</Window>

やったぜ!
f:id:kitunechan:20151112183938p:plain

参考:http://blogs.wankuma.com/kazuki/archive/2008/03/20/128720.aspx

自分でコントロールを描画する

ControlPaint
ControlPaint クラス (System.Windows.Forms)


自分でコントロールを描画したい時に便利なクラスです
参考:コントロールを描画する: .NET Tips: C#, VB.NET


Button周りのボーダーグラデーションも指定色から作成することができます。
ControlPaint.LightLight( Color );
ControlPaint.Light( Color );
ControlPaint.Dark( Color );
ControlPaint.DarkDark( Color );

上から順に
・強い光色
・弱い光色
・弱い影
・強い影
となっています。

引用:C# 指定色の明暗|おーう なんじゃそら


DarkDarkとか安直すぎない(*_*)

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

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 ) ) {
		// キー押されてます
	}
}