LoginSignup
5
5

More than 5 years have passed since last update.

WPF初心者ですがWindowクラスにイベントを追加したクラスを継承して使いたいです

Posted at

動機

WPFではWindows.Formと違い、DPIが変更されたことやウィンドウの移動、リサイズなどを知る方法がない。つまり自分でウィンドウプロシージャを書かないといけない。しかもウィンドウプロシージャに全部書きだすとそれどこのC言語?となるのでもちろんC#使いが所持する基本的人権の一つeventを使いたい。つまりevent実装コードも書く必要がある。

ここでMVVMパターンにおけるViewにあたるWindowクラスを継承するpartialクラスにそんな処理をごちゃごちゃ書きたくない、でもWindowクラスの幾つかのイベントをホックしないといけない・・・

これは別クラスに押し込めてViewはそのクラスを継承するべきでは、と思った。

書いた

省略するとまずSystem.Windows名前空間にWindowクラスが多分こんな感じである。

Window
namespace System.Windows
{
    public class Window : ContentControl, IWindowService {
        //なんか
    }
}

そしてウィンドウプロシージャやらイベントやらを実装したクラスを作った

MoreEventWindow.cs
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_));
                }
            };
        }
    }
}

さてこれを継承させよう。

MainWindow.xaml.cs
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

に感謝を。

ところでなんで私はXAML書きながらウィンドウプロシージャ書いてるんだ・・・?

ソースコード

yumetodo/prime_num_searcher at feat/save_graph_image

問題が起きていたのは b2679f4で、解決したのが231cb13です。

5
5
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5