質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

Q&A

解決済

3回答

9993閲覧

[C#][WPF] App.StartupUri を指定する場合と 直接 Window を表示する場合の挙動が違う

draq

総合スコア2573

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

WPF

Windows Presentation Foundation (WPF) は、魅力的な外観のユーザー エクスペリエンスを持つ Windows クライアント アプリケーションを作成するための次世代プレゼンテーション システムです

0グッド

0クリップ

投稿2019/03/19 03:57

編集2019/03/19 06:37

前提

App.xaml のビルドアクションを標準の ApplicationDefinition から Page へ変更し、Main メソッドを自分で定義しており、NavigationWindow を初期表示しています。
NavigationWindow は

  • SizeToContent プロパティを WidthAndHeight
  • WindowStartupLocation プロパティを CenterScreen
  • Source プロパティはは初期ページ(XAML)の URI
  • サイズは未指定

に指定しています。

実現したいこと

自分でインスタンス生成したウインドウを画面中央に表示したい。

発生している問題

App.StartupUri に NavigationWindow の XAML ファイルへのパスを指定した場合は、Source で指定したページを読み込んだ NavigationWindow が画面中央に表示されますが、
直接 NavigationWindow のインスタンスを生成し、Show メソッドで表示(または App.Run の引数にインスタンスを設定)した場合は、Sourceで指定したページが読み込まれていない状態で一瞬画面中央に表示された後初期ページへ遷移するため、ウインドウサイズ分右下にずれて表示されてしまいます。

もう少し詳しく書くと、
App.StartupUri を指定した場合は、

  1. NavigationWindow をロード
  2. Source で指定された Page をロード(この時、NavigationWindow がリサイズ)
  3. NavigationWindow を画面中央に表示

される。
自分で NavigationWindow のインスタンスを生成した場合、

  1. NavigationWindow をロード
  2. NavigationWindow を画面中央に表示
  3. Source で指定された Page をロード(この時、NavigationWindow がリサイズ)

の順番で処理されている様に見えます。
自分で NavigationWindow のインスタンスを生成した場合でも、App.StartupUri を指定した場合と同じように動いて欲しいです。

該当のソースコード

  • App.xaml

XAML

1<Application x:Class="Test.App" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 4</Application>
  • App.xaml.cs

C#

1using System; 2using System.Windows; 3using Test.Views; 4 5namespace Test { 6 public partial class App: Application { 7 [STAThread()] 8 public static void Main( string[] args ) { 9 var app = new App(); 10 app.InitializeComponent(); 11#if false 12 //こちらを有効にすると、ページ遷移後のウインドウサイズで画面中央に表示される 13 app.StartupUri = new Uri( "Views/MainWindow.xaml", UriKind.Relative ); 14#else 15 //こちらはページ遷移後のウインドウサイズ分、画面中央より右下にずれて表示される 16 var window = new MainWindow(); 17 window.InitializeComponent(); //指摘があったので修正 18 window.Show(); 19#endif 20 app.Run(); 21 } 22 } 23} 24
  • MainWindow.xaml(コードビハインドは何も変更していないため省略します。)

XAML

1<NavigationWindow 2 x:Class="Test.Views.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 6 xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" 7 xmlns:v="clr-namespace:Test.Views" 8 Title="MainWindow" 9 SizeToContent="WidthAndHeight" 10 WindowStartupLocation="CenterScreen" 11 Source="DummyPage.xaml"> 12</NavigationWindow>
  • MainWindow.xaml.cs

コードビハインドは何も変更していないため省略します。

  • DummyPage.xaml(テスト用のダミーのため、Buttonコントロールに意味はありません。)

XAML

1<Page 2 x:Class="Test.Views.DummyPage" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 6 xmlns:ei="http://schemas.microsoft.com/expression/2010/interactions" 7 xmlns:v="clr-namespace:Test.Views" 8 Title="MainPage" 9 Height="350" 10 Width="525"> 11 <Grid> 12 <Button Content="Button" Height="140" Width="325"/> 13 </Grid> 14</Page>
  • DummyPage.xaml.cs

コードビハインドは何も変更していないため省略します。

試したこと

  • App.Run の引数に NavigationWindow のインスタンスを指定する。
  • Window.Show 前に Window.Hide 呼び出し。
  • Window.Show 前に WindowStartupLocation に CenterScreen を指定する。
  • オーバーライドした App.OnStartup 内で、window.show する。

補足情報(FW/ツールのバージョンなど)

Visual Studio 2017
.NET Framework 4.7.1

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

draq

2019/03/19 04:12

初めて質問したので、別人です。
takabosoft

2019/03/19 06:55

app.StartupUri方式だと何か都合が悪かったのでしょうか?
draq

2019/03/19 07:08

元々はViewModelのコンストラクタでサービスのインスタンスを渡したい。→MainWindow.xamlのコードビハインドには書きたくない。→Main関数自分で定義してウインドウインスタンスも自分で作れば。 みたいな流れだったのですが、現在は純粋に技術的好奇心の方が勝ってます。
takabosoft

2019/03/19 07:23

なるほど、ありがとうございます。たしかにこの挙動の違いは気持ち悪いですね・・・・
Zuishin

2019/03/19 07:29

いや何にも気持ち悪くありません。至極まっとうな仕様だと思います。 位置決めをした後にわざわざサイズを変更するようにプログラマーが設定しているので。
takabosoft

2019/03/19 07:59

何故絡まれているのか・・・
Zuishin

2019/03/19 08:13 編集

自分が気持ち悪いと思ったものは全員気持ち悪いと思わなきゃいけないのか。思わない人を絡んでる扱いとか気持ち悪い。
takabosoft

2019/03/19 08:16

どうみても絡まれてるんですが・・・
takabosoft

2019/03/19 08:19

失礼があったのなら誤ります、すみません、そんなに怒らないでください。
Zuishin

2019/03/19 08:41

何も気持ち悪くないものを気持ち悪くないといっちゃいけない理由は?
Zuishin

2019/03/19 08:41

人に「絡んでる」と言って「失礼があったのなら」と仮定形の理由は?
takabosoft

2019/03/19 08:45

本当に、申し訳ありませんでした。
guest

回答3

0

既に解決済みですが、自分もちょっと気になったので調べてみました。

結果としては、showではなく、Visibility = Visibility.Visibleにすることで、StartupUriを指定した時と同じ動作になりました。

ShowとStartupUriで何が違うかNavigationWindowのイベント発生の順番を見たところ、その順番が異なっていました。Showをした場合、コンテンツが組み込まれる前にNavigationWindowが表示されるようなので、ぞの状態で各種座標計算がされてしまっているのだと思います。そのためコンテンツなしのNavigationWindowが中央に配置され、その後組み込まれたコンテンツに合わせて、NavigationWindowがリサイズされたのでしょう。

Visibility = Visibility.Visible(もしくはStartupUriで指定した場合)の場合は、NavigationWindowが表示される前にコンテンツの組み込まれ、そのウィンドウサイズを使い画面を中央に表示されるようにXY位置が計算されたものと思われます。

VisibilityとShowの違いは、非同期動作か同期動作かの違いらしいです。

投稿2019/03/20 06:30

ta.fu

総合スコア1667

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

draq

2019/03/20 06:48

ありがとうございます。ということはnavigationwindow以外でもshowした時と、visibility操作した時で挙動異なることがあるって事ですね。参考になりました。
guest

0

ベストアンサー

.NETのソース(Applicationクラスの中)を眺めていたのですが
https://referencesource.microsoft.com/#PresentationFramework/src/Framework/System/Windows/Application.cs,f28b425477f78d2f,references

↑と似たような感じで

window.Show()

app.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new SendOrPostCallback((w) => ((Window)w).Show()), window);

に置き換えてやれば、期待通りに動くようにはなりました。

何故かまでは追えていませんが。

投稿2019/03/19 08:07

takabosoft

総合スコア8356

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

draq

2019/03/19 08:42

.NET のソースまで調べていただき、ありがとうございます。 こちらでも期待通り動くことを確認できました。
moredeep

2019/03/19 08:43

Dispatcher.BeginInvokeでUIスレッドに移動できるので、UIスレッドか否かが分かれ目なんですかね。 UIスレッドで普通にShowしてみたら何かわかるかもしれませんね。
takabosoft

2019/03/19 09:03

mainの中とBeginInvokeの中で Debug.WriteLine(System.Threading.Thread.CurrentThread.ManagedThreadId); した感じでは同じ値が戻ってきたので両者ともUIスレッドなのかなと思います。 それよりもBeginInvokeの中のものが実行されるまでの間に何か処理が走って、それが別れ目になるのかなと推測しています(が、そこからは追えていませんw)
guest

0

実現したいこと:ウインドウを画面中央に表示したい。
発生している問題:App.StartupUri に NavigationWindow の XAML ファイルへのパスを指定した場合は画面中央に表示されます
解決しているのではないですか?自分でShowを呼ばなければいけない事情があるということでしょうか?聞きたいことはなるべく詳細に述べてもらった方が、回答者は助かります。

自分でShowを呼んだ時にPageが遅れて読みこまれるのは、恐らくInitializeComponentを呼んでいないからです。
var app = new App();の後、WindowをShowする前に、app.InitializeComponent();を実行してみてください。

投稿2019/03/19 04:46

moredeep

総合スコア1507

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Zuishin

2019/03/19 05:06

Page を読み込むことによってウィンドウサイズが変わってるみたいですね。 そこを隠して質問しているので意味不明になっています。
draq

2019/03/19 06:38

> moredeep様 別件での試行錯誤の一環で自分でShowを呼ぶ場合に画面中央に表示する方法を知りたくて質問しました。 app.InitializeComponent()の呼び出しは追加してみましたが、結果は変わりませんでした。(質問にも反映しました。)
draq

2019/03/19 06:44

>Zuishin様 >Page を読み込むことによってウィンドウサイズが変わってるみたいですね。 そうです。方法によって、ウインドウサイズ変更のタイミング?が異なっているため、結果とし画面中央に表示できず質問した次第です。 問題点について、質問に詳細を追記しました。
moredeep

2019/03/19 08:36

InitializeComponentはリソース関係でしたね。失礼しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問