次の方法で共有


Windowクラスを継承するには

質問

2011年12月9日金曜日 1:09

Hoshinaです
こんにちは

C# 2010 Expressを使用して,WPFの学習中です。
疑問点がでてきました。お分かりの方がありましたら,ご教授ください。

1.希望すること
・特定の機能を実現したWindowの派生クラスを作成し,さらにそのクラスから個別の画面に対応したWindowクラスを定義して,利用したい

2.実行してみたこと
・クラスライブラリ形式のプロジェクトを作成する
・新規に,クラスのファイルを追加して,親クラスをSystem.Windows.Windowに直接変更する
 (この作業は,WPFでは親子間を含めて,xamlファイルを複数持てないとの情報を得たため)
・上記クラスに,必要な機能を実装する
・上記プロジェクトをコンパイルし,DLLを作成する
・上記DLLを利用する,新しいWPFアプリケーションプロジェクトを作成する
・Windowクラスの親クラスを,上記DLLのクラスに変更する
・プロジェクトをコンパイルすると,以下のエラーとなる
  XXX の partial 宣言では、異なる基本クラスを指定してはいけません

3.DLLに定義した内容
・名前空間: MyWindow
・クラス名: TransactionWindow

4.現在の,XAMLの内容
・Windowタグは標準のまま
 ここの書き換え方法が分かりません

5.教えていただきたいこと
・私のアプローチで希望にたどりつけるかどうか?
・どのように,XAMLファイルを書き変えたらよいか?

ご存じの方がありましたら,ご教授ください。
ちなみに,DLLにカプセル化したいのは,TransactionScopeに関した汎用の処理です。
必要に応じて,派生クラスでoverrideします。

よろしく,お願いします

すべての返信 (19)

2011年12月9日金曜日 8:17 ✅回答済み

Hoshinaです

解法が見つかりました。

<local:TransactionWindow x:Class="XXXWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:MyWindow;assembly=MyWindow"  以下略

肝は,clr-namespaceの記述で,「assembly=」の部分が必須であることでした。
別のDLLとして,参照に登録するだけではダメということです。

意見を寄せていただいた方々,ありがとうございました


2011年12月9日金曜日 1:55

上が継承、下がTemplate差し替え+共通の機能はVMにのアプローチです。
http://d.hatena.ne.jp/okazuki/20091025/1256472760
http://code.msdn.microsoft.com/WPFWindow-c471ff75

お好みの方でどうぞ。個人的にはWindowにTransactionScopeを使ったトランザクション制御を入れるのは、設計的にどうかなぁ・・・と思います。

かずき Blog:http://d.hatena.ne.jp/okazuki/ VS 2010のデザイナでBlendのBehaviorをサポートするツール公開してます。 http://vsbehaviorsupport.codeplex.com/


2011年12月9日金曜日 2:37

Hoshinaです
こんにちは

実は,この記事は既に見ているのですが,XAMLの書き換え方法が分かりませんでした。
最初の投稿にあるように,親クラスにはそもそもXAMLファイルが無い状態でDLLを作成してあります。
この状態で,派生した子クラスのXAMLをどのようにしたらよいかが分かりませんでした。
もう少し,ねばってみます。

ちなみに,TransactionScopeの制御に関して,
・実際のデータベース更新処理は,XXTableやXXDatabaseのようなModelクラスに実装してあります
 Windowなどのクラスにも,自分以外のModelクラスにも依存していません

Windowクラスが担当するのは,
・複数のXXTableやXXDatabaseへの処理の委譲
・Modelクラスからエラーが報告された場合のユーザへの提示
以上の2つになっていますので,設計的にはきれいな構造になっています。

それでは


2011年12月9日金曜日 7:24

> 1.希望すること
> ・特定の機能を実現したWindowの派生クラスを作成し,さらにそのクラスから個別の画面に対応したWindowクラスを定義して,利用したい
> ・・・・・・
> 5.教えていただきたいこと
> ・私のアプローチで希望にたどりつけるかどうか?

 

う~ん、このアプローチは疑問を感じます。

私なら、Window(XAML) ではなく ViewModel を使って機能の継承を図りますが・・・
もし View(XAML) で UI 部品の共通化を図るなら UserControl を作るのが速いと思いますが、これとて実装は ViewModel に行い、UserControl(XAML) 側とはバインドで連携するよう実装しますね。

ひらぽん http://d.hatena.ne.jp/hilapon/


2011年12月9日金曜日 8:13

Hoshinaです
こんにちは

ViewModelを使用して機能の継承を図るのは,私の想定している場合にふさわしくないと考えています。
私の問題は,画面を共通化するのではなく,機能の共通化をどうするかにあります。
そして,その機能には,何らかのメッセージをユーザに表示するという,Windowクラスに適しているものが含まれます。

以下のシナリオを想定してください。
・あるプロジェクトで使用する多くの画面(すなわちWindow)があります
・それぞれのWindow間には,関連性がありません
  社員マスタ登録の画面, 受注登録の画面, 納品書印刷の画面 などなど
・プロジェクトに含まれる,「全ての画面」で共通に使用したい「機能」がある
  たとえば,データベースの操作をTransactionScopeの制御下で統一して行うなど
  繰り返しになりますが,何個かのWindowではなく,プロジェクトに含まれる全Window に関してです

このような場合,
・Windowクラスからこのプロジェクト専用の親クラスを派生させ,
・共通に使用するメソッドや変数をこの親クラスで定義し,
・個別の画面用のWindowを派生して使用する
このような設計は,良く採用する方法ではないでしょうか?

また,解法を発見しましたので,別に投稿したいと思います

それでは


2011年12月9日金曜日 8:39

ViewModelを使用して機能の継承を図るのは,私の想定している場合にふさわしくないと考えています。
私の問題は,画面を共通化するのではなく,機能の共通化をどうするかにあります。
そして,その機能には,何らかのメッセージをユーザに表示するという,Windowクラスに適しているものが含まれます。

以下のシナリオを想定してください。
・あるプロジェクトで使用する多くの画面(すなわちWindow)があります
・それぞれのWindow間には,関連性がありません
社員マスタ登録の画面, 受注登録の画面, 納品書印刷の画面 などなど
・プロジェクトに含まれる,「全ての画面」で共通に使用したい「機能」がある
たとえば,データベースの操作をTransactionScopeの制御下で統一して行うなど
繰り返しになりますが,何個かのWindowではなく,プロジェクトに含まれる全Window に関してです

このような場合,
・Windowクラスからこのプロジェクト専用の親クラスを派生させ,
・共通に使用するメソッドや変数をこの親クラスで定義し,
・個別の画面用のWindowを派生して使用する
このような設計は,良く採用する方法ではないでしょうか?

 

すでに解決済みのようですが・・・

このお話ですが、Window を Forms に置き換えて読んだ場合、何の違和感もありません。実際いままで多くの現場で見てきましたし、私もそう設計してきました。
ただし WPF の場合、従来 Forms で当たり前だった手法が通用しない、もしくは通用しづらい面が実際存在します。
私なら ViewModel もしくは Model に共通機能を設けるか、もしくはコードビハインドなら Window を継承せずコンポジットで対処しますね。

#最適解とはいいません。あくまで私の場合です。

> このような設計は,良く採用する方法ではないでしょうか?

WPF でよく採用される MVVM パターンでは

> ・プロジェクトに含まれる,「全ての画面」で共通に使用したい「機能」がある
> たとえば,データベースの操作をTransactionScopeの制御下で統一して行うなど
 
は Window(View) の仕事ではなく Model の仕事になると思います。以下、一応参考資料を挙げておきます。

MVVMパターンの常識 ― 「M」「V」「VM」の役割とは?

「MVVMパターンが必要な理由」啓蒙用資料公開

ひらぽん http://d.hatena.ne.jp/hilapon/


2011年12月9日金曜日 8:59

このお話ですが、Window を Forms に置き換えて読んだ場合、何の違和感もありません。実際いままで多くの現場で見てきましたし、私もそう設計してきました。
ただし WPF の場合、従来 Forms で当たり前だった手法が通用しない、もしくは通用しづらい面が実際存在します。

私もそう思います。Windowsフォームの場合はデザインとロジックが同居していました。ですから、ロジック部を共用するために継承の手法が取られましたが、WPFではMVVMパターンで見られるように、デザイン部とロジック部が分離され、Windowはデザイン部のみしかないことが多くなっています。よって、現在主流であるMVVMパターンで開発するのであれば、Windowを派生させて各画面を作成するというパターンは当てはまらなくなっていると思います。

 

★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/


2011年12月9日金曜日 16:19

解決されたようでよかったです。
xmlnsでの別DLLにあるクラスの名前空間の定義のしかたがわからなかったようですね。MSDNのここらへんからXAMLについてみておくと今後スムーズかもしれません。

かずき Blog:http://d.hatena.ne.jp/okazuki/ VS 2010のデザイナでBlendのBehaviorをサポートするツール公開してます。 http://vsbehaviorsupport.codeplex.com/


2011年12月9日金曜日 23:39

Hoshinaです

>ひらぽんさん
>は Window(View) の仕事ではなく Model の仕事になると思います。以下、一応参考資料を挙げておきます。

はい。この実装はModelクラスにあります。
そのModelクラスのメソッドを呼び出す役割を,Windowクラスが務めています。
そして,Modelクラスが発見したエラーを受け取り,ユーザに提示するまでがWindowクラスの仕事になります。

それでは


2011年12月9日金曜日 23:59

Hoshinaです

お二人の投稿ありがとうございました。
しかしながら,そう発想が納得できません。
以下に私の考えを書いて見ます。

私の考えは,あるプロジェクトを進行するのに,そのプロジェクトの全てのWindowの親となるクラスを定義します。個別のWindowは,必ずこの親クラスから派生して使用するというものです。この親クラスには,共通に使用するメソッドやデータを定義しておきます。

この考えには,FormやWPFの知識は不要で,C#やJavaといったプログラム言語の知識も不要と思います。
しいて言えば,UMLのクラス図を使用して,オブジェクト指向分析や設計で十分判断がつく問題だと思います。
上記のような,親クラス・子クラスの関係は,継承で定義するのが自然で最適であると思います。
さらに,この親クラス・子クラスの実装には,virtualメソッドとoverrideという,オブジェクト指向言語ならば当然支援している技術だけで可能です。

私には,Windowの継承という判断は実に当然と思われるのですが,何故お二人はそうは考えないのかが不思議です。
特に,Windows Formなら継承を使用するにもかかわらず,WPFの場合はそうしないのが不思議なのです。
オブジェクト指向の分析や設計では継承としていながら,WPFを使用すると決めると,何故変更の必要があるのでしょうか?
しかも,自然な分析・設計であり,かつWPFの特定の技術に全く無関係であるのも係わらず。

お二人の投稿には,この私の疑問への説明が不足しているように思いますが,いかがでしょう。

それでは


2011年12月10日土曜日 5:18 | 1 票

オブジェクト指向の分析や設計では継承としていながら,WPFを使用すると決めると,何故変更の必要があるのでしょうか?

皆さんは継承を否定しているわけじゃないですよ。

やりたいことは多くのWindowで共通の「機能の呼び出し」を継承したいのであって、Windowの「見た目」まで継承する必要はないですよね。?

WindowsFormではボタンなどの「見た目」からWindowクラス内のイベントハンドラで「機能の呼び出し」をしていたので、「機能の呼び出し」を継承しようとすると必然的にWindowの「見た目」まで継承することになります。

ですが、WPFだと「機能の呼び出し」と「見た目」を切り離しやすいので、「機能の呼び出し」の部分だけ継承すればいいという考え方ができます。
#「機能」がModel(M)、「見た目」がWindow(V)、「機能の呼び出し」がViewModel(VM)で、M-V-VMモデルとなります。

そして、「見た目」の継承もしたければStyleの適用でできますよということです。
#Style自体もBaseOnで継承できますしね。

WindowsFormでも切り離すことはできますし、WPFのWindowがsealedされていないのだからを継承することは否定されているわけじゃないです。
個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)


2011年12月10日土曜日 6:03

言葉尻にかみつくようで申し訳ないのですが、1つだけ違和感をかんじたのでコメントします。

この親クラスには,共通に使用するメソッドやデータを定義しておきます。

共通で使用するメソッドを流用するためだけに継承(is a関係)にするのは個人的にはバッドノウハウかなと思います。そういうのはただのユーテリティメソッドなので、別クラスのstaticメソッドにでも切り出しておけばいいと思います。

 

かずき Blog:http://d.hatena.ne.jp/okazuki/ VS 2010のデザイナでBlendのBehaviorをサポートするツール公開してます。 http://vsbehaviorsupport.codeplex.com/


2011年12月12日月曜日 1:52

私の言いたいことは getta さんに判りやすく解説頂いてるので、ひとつだけ。

> 私には,Windowの継承という判断は実に当然と思われるのですが,何故お二人はそうは考えないのかが不思議です。
> 特に,Windows Formなら継承を使用するにもかかわらず,WPFの場合はそうしないのが不思議なのです。

私も以前は Form の継承をメインに行ってましたが、仕様変更が頻繁に発生し変更作業を繰り返す中、継承は却って無駄であると思えるようになってきました。

最近は かずき_okazuki さんのおっしゃる通り

> 共通で使用するメソッドを流用するためだけに継承(is a関係)にするのは個人的にはバッドノウハウかなと思います。そういうのはただのユーテリティメソッドなので、別クラスのstaticメソッドにでも切り出しておけばいいと思います。

と思ってまして、現在のプロジェクト(WPF ・ Forms 混合) においても、Form の継承から委譲にリファクタリングしました。おかげで大幅な仕様変更にも、ほとんど苦も無く対応できてます。現在の OOP では 「継承より委譲」 が主流だそうですが、実際作業してると納得できます。

ひらぽん http://d.hatena.ne.jp/hilapon/


2011年12月12日月曜日 3:05

私には,Windowの継承という判断は実に当然と思われるのですが,何故お二人はそうは考えないのかが不思議です。
特に,Windows Formなら継承を使用するにもかかわらず,WPFの場合はそうしないのが不思議なのです。

gekkaさんが既に書かれているように考えているからです。WPFの場合はそうしないのではなく、WPFだからそうしないことを選択しています。そうしないことによりに、デザインなどの見た目と機能などのロジックを切り離して開発できるようになります。もちろん、WPFでもWindowsフォームと同じようにデザインとロジックを混在させて開発できますし、それを否定するつもりはありませんが、せせっかくWPFが高度なデザインをサポートしており、かつ、デザインとロジックを分離して開発できますので、Windowsフォームの開発に固執せず、少し頭を切り替えられるのも良いのではないかと思います。
ちなみにWindowで共通に使う機能でしたら、BehaviorやTriggerActionなどを継承したクラスを作成して、それを共通に使うこともできます。

 

★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/


2011年12月13日火曜日 0:47

Hoshinaです
こんにちは

たくさんの投稿ありがとうございます。
ただ,現時点ではいままでの繰り返ししか,できそうもありませんので,しばらくは傍観しようと思います。
それと,皆さんの投稿から,もう少しWPFの調査の必要も必要と思いますので。

ただ,少しだけ。

> gekkaさん
>やりたいことは多くのWindowで共通の「機能の呼び出し」を継承したいのであって、Windowの「見た目」まで継承する必要はないですよね。?
>WindowsFormではボタンなどの「見た目」からWindowクラス内のイベントハンドラで「機能の呼び出し」をしていたので、「機能の呼び出し」を継承しようとすると必然的にWindowの「見た目」まで継承することになります。

はい,そうです。私の想定している親クラスには,一切画面がありません。ですので,WPFへもそのまま適用できます(XAMLファイルは1つだけ)。当然のことながら,イベントも定義しません。
virtual なメソッドと 共通のデータ及びそれへのアクセス方法(メソッド/プロパティ)があるだけです。
Modelのクラスで,特定のプロジェクトで使用する,最上位の親クラスを定義して,実際のModelクラスはその親から派生するのと,考え方は全く同じです。

>かずき_okazukiさん
>共通で使用するメソッドを流用するためだけに継承(is a関係)にするのは個人的にはバッドノウハウかなと思います。そういうのはただのユーテリティメソッドなので、別クラスのstaticメソッドにでも切り出しておけばいいと思います。

クラス設計に関しては,それなりの経験を持っています。共通で使用するためだけではありませんので,この点には御懸念なくお願いします。

>ひらぽんさん
>私も以前は Form の継承をメインに行ってましたが、仕様変更が頻繁に発生し変更作業を繰り返す中、継承は却って無駄であると思えるようになってきました。

これは,画面+処理の両方が類;していたために派生の技術を採用していたが,画面 or 処理 or 画面+処理 に仕様変更が発生して,継承が良くなかったということではありませんか?
特に,画面が類;していることを理由にして,継承を選択すると仕様変更に弱くなります。この場合は,そもそも継承を選択することが間違いだと思います。
私は,よほど特別なことがないかぎり,画面のクラスには継承を利用しません。
私の問題としている,親のWindowはそもそも表示に関する要素を全く持たないものです。
virtualなメソッドと共通に使用するデータだけです。そのため,この方法で恩恵を受けた経験は多々ありますが,仕様変更により困った経験はありません。

それでは


2011年12月13日火曜日 1:36

はい,そうです。私の想定している親クラスには,一切画面がありません。ですので,WPFへもそのまま適用できます(XAMLファイルは1つだけ)。当然のことながら,イベントも定義しません。
virtual なメソッドと 共通のデータ及びそれへのアクセス方法(メソッド/プロパティ)があるだけです。
Modelのクラスで,特定のプロジェクトで使用する,最上位の親クラスを定義して,実際のModelクラスはその親から派生するのと,考え方は全く同じです。

みなさんが言われているように、この設計が誤りというわけではありません。MVVMが絶対とは言いませんが、少なくとも現在主流のデザインパターンであることは間違いないでしょう。私を含め、みなさんとHoshinaさんの考え方の違いは、MVVMが前提としてあるかないかだと思います。例えば、Hoshinaさんは以下のように言われていますが、

>ViewModelを使用して機能の継承を図るのは,私の想定している場合にふさわしくないと考えています。
>私の問題は,画面を共通化するのではなく,機能の共通化をどうするかにあります。
>そして,その機能には,何らかのメッセージをユーザに表示するという,Windowクラスに適しているものが含まれます。

MVVMではユーザーへのメッセージを表示することもViewModelが行います。MVVMにおいてWindowは、単なるユーザーとのインターフェースに過ぎません。よって、ロジックを持つことは基本的にありません。ただ、これがいかなる場合も最適解であるとも思っていません。確かにユーザーにメッセージを表示するコードはHoshinaさんが書かれるコードより複雑になるでしょう。目的はアプリケーションを作成することであり、MVVMを実現することではないことを私は知っています。しかし、それでもなお、今のところ私はMVVMで開発を行っていくでしょう。理由はネットでもいろいろ紹介されていますので細かには書きませんが、私としてはトータルで考えた時に一番生産性が良いと感じているのが一番の理由です。

Hoshinaさんは、

>私には,Windowの継承という判断は実に当然と思われるのですが,何故お二人はそうは考えないのかが不思議です。

と書かれていますが、なぜこのパターンが採用されずにMVVMが主流になったのか?

>それと,皆さんの投稿から,もう少しWPFの調査の必要も必要と思いますので。

その辺りも調査されると良いと思います。

 

★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/


2011年12月13日火曜日 2:44

Hoshinaです
傍観するつもりでしたが,気になるものですから。

私はこのように書きました。
>ViewModelを使用して機能の継承を図るのは,私の想定している場合にふさわしくないと考えています。

できれば,「私の想定している場合に」の文言を読み取ってもらいたいと思います。

WPFでプログラムを作成する場合には,MVVMを全面的に採用することに躊躇していません。
現在の時点で,WPFには色々と問題があることも理解しているつもりです。

では,私の想定している場合とはどのような場合なのか?
・ある,特定のプロジェクトで使用しているWindowに,親クラスを定義し,
・個別の画面は,この親クラスから派生して使用する
・当然ですが,この親クラスは抽象クラスであり,画面要素などは一切持ちません
 virtualなメソッドと共通のデータを持つだけです
 この親クラスに対応する,ModelもViewModelもありません

この場合には,派生で解決することが最適な解であると,今でも考えています。
私には,皆さんの発想法が,WPFに固まって,思考の柔軟性を失っているように見えます。
個別の処理や個別の画面を持っているWindowに,継承を使用することと混同していませんか?
プロジェクト共通の派生による親Windowと,それから派生した個別画面のMVVMとの両刀使いが,現時点での私の選択です。

さらに,以下の利点があります。
・FormやWPFなどの違いに,全く影響なく実装可能です
・C#やJavaなどのプログラム言語に,全く影響なく実装可能です
・仕様変更に殆んど影響されません
 個別の処理や個別の画面に影響するようなものではなく,表示のためのクラス階層をどのように構築するかにだけ関係しているからです

と,やはり傍観するのがよさそうです。
それでは


2011年12月13日火曜日 7:32

私の言いたいことがうまく伝わっておらず、すれ違っているように思います。すれ違ったままこのスレッドが終わっても寂しいので、傍観されるとのことですが、少し書かせていただきます。

HoshinaさんはWindowの継承について多く書かれているようですが、少なくとも私は継承やクラス設計の良し悪しなど、そういうことは問題にしていません。
HoshinaさんはWindowにメソッドやデータを持たせる設計を採用されていますので、継承を考えられるのは当然のことだと思います。私はそれが間違っているとか設計が悪いと言っているのではありませんし、それが悪いと思っているからWindowの継承をしないわけではありません。
私はMVVMを採用しており、Windowにメソッドやデータを持ちませんので、自然にWindowを継承するということが無くなっているだけです。Windowを継承することを否定しているのではなく、継承が必要ないデザインパターンを選択しているというだけです。私がWindowの継承をしないので、HoshinaさんがされているWindowの継承を、私が否定しているようにご誤解されているように感じました。以前にも書きましたが、MVVMが唯一絶対のデザインパターンであり、それ以外は間違いだと言うつもりはありませんので、Hoshinaさんが設計されているデザインパターンを否定するつもりはありません。ただ、

>プロジェクト共通の派生による親Windowと,それから派生した個別画面のMVVMとの両刀使いが,現時点での私の選択です。

と書かれていますが、このMVVMは私の中では違和感を覚えました。MVVMでは、メソッドやデータは一括してViewModelが提供するということから逸脱しているからです。

 

★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/


2011年12月15日木曜日 0:01

Hoshinaです
こんにちは

どうも,私の投稿が良くなかった気がしてきました。
WPFで,System.Windows.Windowから直接個別のクラスを派生するのではなく,別のクラスをもう1段派生した上で,個別のクラスを利用する方法に関してだけ質問すればよかったです。なぜそうしたいのかや,別の案については置いておいて,このことがやりたい事なのですという方向で議論すればよかったです。皆さんをミスリードしてしまいました。

今までは,抽象的な内容ばかりでしたので,以下に私が実装した親クラスの内容(省略してあります)を添付しておきます。
興味のある方は,のぞいてみてください。メソッドが3つしかない,極めて簡単なクラスです。

    // データベースの操作をTransactionScopeで管理するためのメソッド
        public void Execute()
        {
            try
            {
                using (TransactionScope ts = new TransactionScope())
                {
                    DoAction();

                    ts.Complete();
                }
            }
            catch (Exception ex)
            {
                OccurredError(ex);
            }
        }

        // データベースの更新処理
        // 派生クラスで適切に再定義すること
        // エラーを発見した場合は,例外を投げること
        public virtual void DoAction()
        {
        }

        // エラーを発見した場合の処理
        // 派生クラスで,必要に応じて再定義すること
        // 標準では,エラー形式のメッセージボックスを表示する
        public virtual void OccurredError(Exception ex, string caption = "エラー", MessageBoxImage image = MessageBoxImage.Error)
        {
            MessageBox.Show(ex.Message, caption, MessageBoxButton.OK, image);
        }