ExceptionのHResultについて

C#でファイル操作などのWindowsに関わる操作を行うと内部的にはWindows APIが呼ばれます。
WindowsAPIはC#ではないので、エラーになったときにはC++的な形でエラーコードを返します。
それをC#でラッピングしてあるので、C#でExceptionとしてエラーを取得する事ができます。

C#のExceptionのメッセージだと大まかな分け方しかしていないので詳細に知りたい場合はHResultの値を調べます。

try{ 
}catch(Exception e ){
  e.HResult // <- こいつについての話
}

例えばファイルを置換に失敗した場合 IOException になります。

try{
  System.IO.File.Replace(sourceFileName, destFileName, null);
}catch( IOException e ){
  //  e.Message → 置換されるファイルを削除できません。
  //  e.HResult → -2147023721
}

int型の表示でマイナスの値が表示されますが、所詮ビットデータなので2進数表示で確認します。

更に上位下位として半分ずつにします。

2進数:1000000000000111 0000010010010111
16進数:0x8007 0x0497

で、これがなんなんだっていうと 上位の0x8007はWin32エラーコードということを示しています。
下位の0x0497がエラーの番号です。

ERROR_UNABLE_TO_REMOVE_REPLACED 1175 (0x497)
置換されたファイルを削除できませんでした。置換されるファイルと置換ファイルの名前は、元のまま変更されていません。

ReplaceFileA 関数 (winbase.h) - Win32 apps | Microsoft Learn

こんな感じでWindows APIのエラーコードにたどり着くことができます。


参考:
ASCII.jp:Windowsで表示されるエラーコードの見方

無理やりワンライナー

こんな感じのときってあると思います。

var result = "";
if( 条件1 ){
  result = "1つ目";
}else if( 条件2 ){
  result = "2つ目";
}

この初期化めんどくさくない?

var result = "";

三項演算子

ただ、三項演算子で書くと「(◕‿‿◕)ワケガワカラナイヨ・・・」

var result = (条件1) ? "1つ目" : (条件2) ? "2つ目" : "";

メソッド

メソッドに切り出したいけど、メソッド名考えるの大変なんだよなー

var result = GetResult( 条件 );

string GetResult( 条件 ){
  if( 条件1 ){
    return "1つ目";
  }else if( 条件2 ){
    return "2つ目";
  }
  return "";
}

匿名メソッド&即実行

Funcで無理やり関数宣言&即実行じゃー

var result =
  ( (Func<string>)( () => {
    return "1";
  }) )();

または

var result = 
  ( new Func<string>( ()=>{
    return "2";
  }) )();

()括弧多すぎ問題


そしてStaticへ・・・

そしてこうなった・・・

var result = Func.Run( ()=>{
    return "1";
  } );

public static class Func {
  public static T Run<T>( Func<T> func ) {
    return func();
  }
}

他の便利な使い方

初期化のときにメソッドの形で書けます。

public class Item {
  public string Value { get; set; } = Func.Run( () => { return ""; } );
}

初期化のときにメソッドの形で書けます。その2

var item = new Item(){
  Value = Func.Run( () => { return ""; } ),
}

[Rx] 変更通知の合成はObservable.Merge<object>です。

WPFでBindingする時にLivetを使っていたのですが、ReactivePropertyが面白そうだったのではじめました。
Livetだと自分の好きなタイミングで変更通知イベントを出せていたのですが、Rxだとそのタイミングもロジックとして書く必要があります。

要は、AプロパティとBプロパティのどちらかが変わった時に、Cプロパティの変更通知を出したい みたいな

Livet

Livet で書くとこんな感じ

public class LivetViewModel : ViewModel {
  public int A {
    get => _A;
    set {
      if( _A != value ){
        _A = value;
        this.RaisePropertyChanged();
        this.RaisePropertyChanged( nameof(this.C) );
      }
    }
  }
  int _A;
  
  public int B {
    get => _B;
    set {
      if( _B != value ){
        _B = value;
        this.RaisePropertyChanged();
        this.RaisePropertyChanged( nameof(this.C) );
      }
    }
  }
  int _B;
  
  // A, B が変わった時にCの値が変わる
  public int C => A + B;
}

ReactiveProperty

だいたいCombineLatestを使えと書いてあるのでこんな感じ

public class RxViewModel {
  public RxViewModel() {
    this.C = this.A.CombineLatest(this.B, (a,b)=> a+b).ToReactiveProperty();
  }

  public ReactiveProperty<int> A { get; } = new ReactiveProperty<int>();
  public ReactiveProperty<int> B { get; } = new ReactiveProperty<int>();
  public ReactiveProperty<int> C { get; }
}

ReadOnlyとかSlimとかしっかり使うとこんな感じ

public class RxViewModel2 {
  public RxViewModel2() {
    this.C = this.A.CombineLatest(this.B, (a,b)=> a+b).ToReadOnlyReactivePropertySlim();
  }

  public ReactivePropertySlim<int> A { get; } = new ReactivePropertySlim<int>();
  public ReactivePropertySlim<int> B { get; } = new ReactivePropertySlim<int>();
  public ReadOnlyReactivePropertySlim<int> C { get; }
}

ただし、CombineLatest だと全ての値がそろわないと通知が始まらないので「値がない場合~」とかはできません。

やっと本題 Observable.Merge<object> を使いましょう。

public class RxMerge {
  public RxMerge() {
    this.C = Observable.Merge( this.A, this.B )
              .Select(x => this.A.Value + this.B.Value ).ToReadOnlyReactivePropertySlim();
  }

  public ReactivePropertySlim<int> A { get; } = new ReactivePropertySlim<int>();
  public ReactivePropertySlim<int> B { get; } = new ReactivePropertySlim<int>();
  public ReadOnlyReactivePropertySlim<int> C { get; }
}

CombineLatest の謎のラムダ式 (a,b)=> a+b も消えてスッキリしました。

全ての型が同じ場合は型推論ジェネリックの<T>部分を書かなくていいですが、複数の型が混ざる場合は<object>とすれば問題ありません。


そしてそして、本当にやりたかったことは ReactiveCommand のコマンドが実行可能かどうかという CanExecute 部分の書き方なのだった・・・

Observable.Merge<object>(
    // 変更通知 が来たら条件判定したいReactiveProperty
    this.A, this.B
  ).Select( x => {
    // True になる条件
    return this.A.Value != 0 && this.B.Value != 0;
  } )
	.ToReactiveCommand()

複数の条件が複雑に混ざり合うような時にとても重宝します。


追記 2018/10/18
ジェネリックのキャストの都合上IObservable<[struct]>がIObservable<object>にキャストできないので無理やり変換します。

こんなの用意して

public static Observable {
  public static IObservable<object> Cast<T>( this IObservable<T> source ) where T: struct {
    return source.Select( x => (object)x );
  }
}

こうだ!

public class RxMerge {
  public RxMerge() {
    this.C = Observable.Merge( this.A.Cast(), this.B )
                .Select(x => this.A.Value + int.Parse( this.B.Value ) ).ToReadOnlyReactivePropertySlim();
  }

  public ReactivePropertySlim<int> NumItem { get; } = new ReactivePropertySlim<int>();
  public ReactivePropertySlim<string> StrItem { get; } = new ReactivePropertySlim<string>();
  public ReadOnlyReactivePropertySlim<int> C { get; }
}


2019/01/29 追記
ToUnit() で IObservable に変換するのがデフォらしい。
https://qiita.com/Temarin/items/107fa2e39f427e591dbd

Unit は戻り値なしのvoid的なやつだそうです。
でもデバッグ的にはIObservable<object> のほうが便利だなー

// ReactiveProperty.dll
namespace Reactive.Bindings.Extensions {
	public static class ToUnitObservableExtensions {
		public static IObservable<Unit> ToUnit<T>( this IObservable<T> self );
	}
}

// こんな感じ
Observable.Merge( this.A.ToUnit(), this.B.ToUnit() )

[WPF] タッチ長押しで右クリックをやめる(□を表示させない)

少ないですがWindowsタブレット用のアプリでドラッグやタッチしっぱなしの操作がメインのときにタッチ長押しの右クリック機能が邪魔なときがあります。

対象のXamlの添付プロパティにこれを使いするだけ

Stylus.IsPressAndHoldEnabled = false

こんな感じです。

<Grid Stylus.IsPressAndHoldEnabled="False">
</Grid>

WindowsFormsではこの設定はなさそうなので珍しくWPFの利点でしょうか

参考
https://code.msdn.microsoft.com/windowsdesktop/CVBXAML-WPF-4-TouchDown-b1018a60

他にもStylusクラスには面白そうなものがあるのでご覧あれ
Stylus クラス (System.Windows.Input) | Microsoft Learn

[WPF] ComboBoxをItemsSourceとSelectedItemで扱う時の注意

2019/06/13 再度試してみたら問題なく初期値が設定されました


SelectedItem で初期値を設定する場合には SelectedItem を先に設定してから ItemsSource を変更しないと初期値が設定されない場合があるようです。

味気ないので、雰囲気コード(実際には動かない)置いておきます。

こんなコンボボックスを作って

<ComboBox
  ItemsSource="{Binding Items}"
  SelectedItem="{Binding SelectedItem}"
  DisplayMemberPath="Name"
/>

ViewModel部分

public class ViewModel : INotifyPropertyChanged {
	public List<Item> Items { get; set; }	// 変更通知
	public Item SelectedItem { get; set; }	// 変更通知
	
	public void ChangeComboBox(){
		// これだとダメ
		//this.Items = new List<Item>(){ 
		//		new Item(){ Name = "1" },
		//		new Item(){ Name = "2" },
		//		new Item(){ Name = "3" },
		//	};
		//this.SelectedItem = this.Items.First();

		var items = new List<Item>(){
				new Item(){ Name = "1" },
				new Item(){ Name = "2" },
				new Item(){ Name = "3" },
			};
		this.SelectedItem = items.First();
		this.Items = items;
	}
}

public class Item {
	public string Name { get; set; }
	public int Value { get; set; }
}

[WPF] ワンランク上のPOPUP コントロール

POPUP コントロール についてです。
よく 「ボタンを押す」→「ポップアップ出す」というデザインを使います。

ポップアップの動作的にはこんな感じになると思います。

  1. ボタンを押す。
  2. ポップアップを表示させる。
  3. ポップアップ以外の場所をクリックするとポップアップが閉じる。

ただね、

  1. ボタンを押す。
  2. ポップアップを表示させる。
  3. ボタンをクリックする。
  4. ボタンを押す時のMouseDownでポップアップが閉じる。
    MouseUPでポップアップが表示される。

ポップアップ閉じてくれえええええ


はい。
すっごい探しまして見つけました。
ToggleButton のNameを指定しないといけませんが、仕方ないでしょう。

<Grid>
	<ToggleButton Name="tbWithPopup" Width="30" Height="30"
			   IsChecked="{Binding ElementName=pUp, Path=IsOpen}">
		<ToggleButton.Style>
			<Style TargetType="ToggleButton">
				<Style.Triggers>
					<DataTrigger Binding="{Binding ElementName=pUp, Path=IsOpen}" Value="True">
						<Setter Property="IsHitTestVisible" Value="False" />
					</DataTrigger>
				 </Style.Triggers>
			</Style>
		</ToggleButton.Style>
		<Polygon Points="0,0 2,0, 1,1" Fill="Black" Stretch="Fill" />
	</ToggleButton>
		
	<Popup Name="pUp"
		   PlacementTarget="{Binding ElementName=tbWithPopup}"
		   AllowsTransparency="True" StaysOpen="False" Width="200">
		<RichTextBox IsReadOnly="True">
			<RichTextBox.Document>
				<FlowDocument>
					<Paragraph>
					   Checkout WPF Tat for more elegant 
					   solutions
					</Paragraph>
				</FlowDocument>
			</RichTextBox.Document>
		</RichTextBox>
	</Popup>
</Grid>

この人神様かな?
WPF TaT: ToggleButton with Popup

Visual Studio デザインモードの判定

Visual Studioのデザイナーすごい便利ですよね!
ただ、デザイン時には実行してほしくないコードが実行されてしまうときもあります。

そんなときはコードに実行中かどうかの判定を記述しましょう。
自分は以下のコードを使用しています。

if( System.ComponentModel.LicenseManager.UsageMode != System.ComponentModel.LicenseUsageMode.Runtime ) {
	//ここはデザイン時にしか通らない
}

他にも判定方法はあるようです。
.net - DesignMode with nested Controls - Stack Overflow

[WPF] 自作ライブラリを使う時に、Xamlのxmlnsで指定する名前空間について

自分でライブラリを作った時にXmlnsDefinitionを記述するとXamlで指定する名前空間に好きな文字を付けられます。
AssemblyInfo.cs にでも記述しておくとわかりやすいかな?

// [assembly: XmlnsDefinition("Xamlでの名前空間", "ライブラリのネームスペース")]

// 複数のnamespaceを同じ名前にしてもOK
[assembly: XmlnsDefinition("http://schemas.WpfApp1/", "WpfApp1")]
[assembly: XmlnsDefinition("http://schemas.WpfApp1/", "WpfApp1.View")]

使うときは指定した名前空間を使用します。

xmlns:wpfApp1="http://schemas.WpfApp1/"

名前空間の指定に決まりはなく、好きな名前が付けられるようです。

// これも行ける?
[assembly: XmlnsDefinition("俺のライブラリ", "WpfApp1")]


参考:
[WPF] WPF入門 ~XAML編 [名前空間]~
XAMLの基礎(1/2) - @IT
2009-09-27

[WinForm] NativeWindowでWinProcの処理を切り分け

WinFormの場合、WinProcからメッセージを受け取って処理を行うという処理をまとめたい場合にNativeWindowと便利になります。
といっても下記URLからの情報なのでこれ以上は無いです😭

https://social.msdn.microsoft.com/Forums/vstudio/ja-JP/a121e807-50ad-4777-b218-2c82ed62742c?forum=csharpgeneralja
http://tryerror.net/tryerror/wordpress/post-214

[WPF] WebBrowserコントロール

WebBrowserコントロールを使用すると、アプリケーション内でブラウザを使用することができます。
が、これはMicrosoftのコントロールなのでInternet Explorerです。
しかも、互換性の問題とやらで標準ではIE7になります。
最新のIEを使用するためにはレジストリに値を追加する必要があります。

さらに、タッチ機能も無効になっているので有効にする必要があります。
さらに、ローカルのファイルを読み込むとセキュリティ警告が出るので、ローカルのファイル読み込みを有効にします。

void SetIEBrowserLatest() {
	var filename = Assembly.GetEntryAssembly().ManifestModule.Name;

	// WebbrowserのIEバージョンを指定
	using( var key = Registry.CurrentUser.CreateSubKey( @"SOFTWARE\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION" ) ) {
		key.SetValue( filename, 11001, RegistryValueKind.DWord );
	}

	// ローカルのファイル読み込みを有効にする
	using( var key = Registry.CurrentUser.CreateSubKey( @"Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BLOCK_LMZ_SCRIPT" ) ) {
		key.SetValue( filename, 0, RegistryValueKind.DWord );
	}

	// タッチ機能を有効にする
	using( var key = Registry.CurrentUser.CreateSubKey( @"Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_NINPUT_LEGACYMODE" ) ) {
		key.SetValue( filename, 0, RegistryValueKind.DWord );
	}
}

タッチ機能を有効にしないと、ライブラリによってはJavascriptのイベントが走らずに動かないものがあります。
例えばこのライブラリ
https://bokeh.pydata.org/en/latest/docs/gallery/image.html

参考:
scroll - C# WebBrowser PanningMode - Stack Overflow

また別の問題もあるようですが・・・
Windows 8のWebBrowserControlをタッチ対応にする - kkamegawa's weblog

WPFのデザインについて

WPFではView(デザイン)の変更がわりと簡単に行えるが、少し特殊なことをしたいときにはどうにもならない場合がある。

sygh.hatenadiary.jp

ControlTemplateを使った場合、指定したデザインに固定されてしまうため、Windowsのバージョンによって微妙に違うコントロールのデザインが違う部分が全く吸収できないのである。
さらに言えば、VisualStudioで「テンプレートの編集」→「コピーして編集」をした時のテンプレートが開発環境のOSに依存する。
Win7でこの作業を行うと、Win8で表示した時にWin7のデザインで表示されるわけです。


では、どうするかというと、上記サイトのようにテーマごとに地道にコードを書くしか無い感じです。

こういうところがWinFormに劣っている気がします。

VS Package での注意

Visual Studio Packageを作成します。

デンッ!


自動的に参照を追加してくれてDLLファイルがコピーされるのですが、どうもパスが長いようです。
保存しているソリューションのフォルダ場所によっては255文字を超えるためエラーになります。

自動的に追加される中でこの2つが原因です。

Microsoft.VisualStudio.Threading.dll
[ソリューションフォルダ]\packages\Microsoft.VisualStudio.Threading.15.0.240\lib\portable-net45+win+wpa81+MonoAndroid10+xamarinios10+MonoTouch10\Microsoft.VisualStudio.Threading.dll
Microsoft.VisualStudio.Validation.dll
[ソリューションフォルダ]\packages\Microsoft.VisualStudio.Validation.15.0.82\lib\portable-net45+win+wpa81+wp80+MonoAndroid10+xamarinios10+MonoTouch10\Microsoft.VisualStudio.Validation.dll

パッケージの中身だけで162文字あるんだけど!!!
パス長すぎ!!!

Visual Studioで開く の削除方法

Visual Studio 2017をインストールするとフォルダの右クリックメニューに「Visual Studioで開く」が追加されます。

最近だと日本語化もされずに「Open in Visual Studio」が追加されます。
追記:2018/08/16

削除するには以下のレジストリを削除します。

Windows Registry Editor Version 5.00
[-HKEY_CLASSES_ROOT\Directory\Background\shell\AnyCode]
[-HKEY_CLASSES_ROOT\Directory\shell\AnyCode]

コマンドプロンプトを管理者権限で起動して以下のコマンドを打てばおっけー

reg delete HKEY_CLASSES_ROOT\Directory\Background\shell\AnyCode /f
reg delete HKEY_CLASSES_ROOT\Directory\shell\AnyCode /f

http://dentnt.windowsfaq.ru/?p=5837

[WPF] TextBoxのキャレットが消える問題

TextBoxをScaleTransformで縮小するとキャレット(カーソルの場所の縦棒|)がたまに消えます。

まあ、キャレットの1ドットも一緒に縮小されて消えちゃうんだろうなーと思いますが。。。

解決方法はScaleTransformを使わないことです。
もしも縮小していたらInverseとかで100%に戻せばバグりません。
拡大縮小についてはFontSizeでなんとかしろということでした。

c# - WPF TextBox Caret Disappearing - Stack Overflow

GitHub Microsoft

GitHubMicrosoft公式のリポジトリがあります。
Microsoft · GitHub

いろんなサンプルがあるので参考になりそうです。
GitHub - microsoft/Windows-classic-samples: This repo contains samples that demonstrate the API used in Windows classic desktop applications.
GitHub - microsoft/WPF-Samples: Repository for WPF related samples
GitHub - microsoft/Windows-universal-samples: API samples for the Universal Windows Platform.