動機
WPFではWindows.Formと違い、DPIが変更されたことやウィンドウの移動、リサイズなどを知る方法がない。つまり自分でウィンドウプロシージャを書かないといけない。しかもウィンドウプロシージャに全部書きだすとそれどこのC言語?となるのでもちろんC#使いが所持する基本的人権の一つevent
を使いたい。つまりevent
実装コードも書く必要がある。
ここでMVVMパターンにおけるViewにあたるWindow
クラスを継承するpartial
クラスにそんな処理をごちゃごちゃ書きたくない、でもWindowクラスの幾つかのイベントをホックしないといけない・・・
これは別クラスに押し込めてViewはそのクラスを継承するべきでは、と思った。
書いた
省略するとまずSystem.Windows
名前空間にWindow
クラスが多分こんな感じである。
namespace System.Windows
{
public class Window : ContentControl, IWindowService {
//なんか
}
}
そしてウィンドウプロシージャやらイベントやらを実装したクラスを作った
namespace prime_num_searcher_gui
{
public class MoreEventWindow : Window
{
public event EventHandler ResizeBegin;
protected virtual void OnResizeBegin(EventArgs e) => ResizeBegin?.Invoke(this, e);
public event EventHandler ResizeEnd;
protected virtual void OnResizeEnd(EventArgs e) => ResizeEnd?.Invoke(this, e);
private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
//略
}
//ばっさり省略
public MoreEventWindow()
{
this.ResizeBegin += (object sender, EventArgs e) => { this.isBeingMoved = true; };
this.ResizeEnd += (object sender, EventArgs e) => { this.isBeingMoved = false; };
this.Move += (object sender, EventArgs e) =>
{
if(true == this.willBeAdjusted && this.IsLocationGood())
{
this.willBeAdjusted = false;
this.OnDelayedDpiChanged(new DelayedDpiChangedEventArgs(this.dpiOld, this.wParam_, this.lParam_));
}
};
}
}
}
さてこれを継承させよう。
namespace prime_num_searcher_gui
{
/// <summary>
/// MainWindow.xaml の相互作用ロジック
/// </summary>
public partial class MainWindow : MoreEventWindow
{
public MainWindow()
{
this.benchmarkExecuter_ = (Environment.Is64BitOperatingSystem) ? new BenchmarkExecuter("prime_num_searcher_x64.exe") : new BenchmarkExecuter("prime_num_searcher.exe");
InitializeComponent();
this.SourceInitialized += (object sender, EventArgs e) =>
{
this.hWnd_ = new WindowInteropHelper(this).Handle;
this.benchmarkResultManager_ = new BenchmarkResultManager(this.hWnd_);
};
}
}
}
問題発生
MainWindowクラスの中でSystem.Windows.Window
内にあるものが一切使えない。なんでや?
原因
XAML側でもこのMainWindowクラスを自動生成している。だからこそpartial
キーワードがMainWindow
クラスに指定されているわけですね。
で、XAML側の基底クラス指定とC#側の基底クラス指定が一致しないので何もできない状態になっていたんですね。
解決策
xaml側の基底クラス指定を変えましょう。
<Window x:Class="prime_num_searcher_gui.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:prime_num_searcher_gui"
xmlns:oxy="clr-namespace:OxyPlot.Wpf;assembly=OxyPlot.Wpf"
mc:Ignorable="d"
Title="PrimeNumSearchBenchmark"
Height="408" Width="760" MinHeight="428" MinWidth="408"
Loaded="Window_Loaded"
FontFamily="Yu Gothic Medium">
<!-- 略 -->
</Window>
これを
<local:MoreEventWindow x:Class="prime_num_searcher_gui.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:prime_num_searcher_gui"
xmlns:oxy="clr-namespace:OxyPlot.Wpf;assembly=OxyPlot.Wpf"
mc:Ignorable="d"
Title="PrimeNumSearchBenchmark"
Height="408" Width="760" MinHeight="428" MinWidth="408"
Loaded="Window_Loaded"
FontFamily="Yu Gothic Medium">
<!-- 略 -->
</local:MoreEventWindow>
こうじゃ。
ルートのtagがWindow
になっていてこれこそがx:Class="prime_num_searcher_gui.MainWindow"
で指定しているprime_num_searcher_gui.MainWindow
クラスの基底クラスがWindow
だと指定していたので、xmlns:local="clr-namespace:prime_num_searcher_gui"
を指定した上でこれをlocal:MoreEventWindow
に置き換えたんですね。
感想
原因をググってるときになんかxamlもいじらないととかいうサイトばかり見つけてんなわけね~だろ、こいつらみんな狂ってやがる・・・、とか本気で思ってましたが、メロンパンと黒糖蒸しパンと梨を食べて冷静になったあとでpartial
指定されている意味を考えたらすんなり納得できました。
WPFでFormの継承(Windowの継承 or 見た目の継承?) - かずきのBlog@hatena
に感謝を。
yumetodoさんが凄いC#コードを書いていたので2度見しています。なんとコメントしたら良いものか……#要するに私の腕が足りてないhttps://t.co/msHH0ANBeE
— YSR@デレステP (@YSRKEN) 2017年9月29日
WinAPIと格闘している辺りが……
— YSR@デレステP (@YSRKEN) 2017年9月29日
どっちといった話ではなく、C#でそんなにWinAPI直叩きしないイメージがあるもので……
— YSR@デレステP (@YSRKEN) 2017年9月29日
ところでなんで私はXAML書きながらウィンドウプロシージャ書いてるんだ・・・?
yumetodoさんの出題(?)である「DPIが動的に変化しても自ウィンドウのスクショを撮る方法」についていろいろ考えていますが、簡単ではありませんね……
— YSR@デレステP (@YSRKEN) 2017年9月29日
次のコードは「DPIが100%表示なら」正常に動くのですが pic.twitter.com/8y7Dgko0iU