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

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

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

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

Q&A

解決済

1回答

742閲覧

C#でウィンドウを表示して画面項目を更新したい

rai03k

総合スコア2

C#

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

2グッド

0クリップ

投稿2024/09/21 15:23

編集2024/09/22 04:21

実現したいこと

C#でウィンドウを表示して画面項目を更新させたいです。

発生している問題・分からないこと

※独自のUIライブラリを使用しています。
画面表示ボタンを押下するとButtonClickメソッドが実行され、画面が表示されます。
この時Valueは0なので、画面のValue項目は「0」と表示されます。
画面更新ボタンを押下するとUpdateButtonClickメソッドが実行され、画面項目が更新されます。
Valueが50になり、画面のValue項目は「50」と更新されます。
該当のソースは以下になります。

C#

1private XXXWindow xWin; 2private void ButtonClick(object sender, RoutedEventArgs e) 3{ 4 xWin = new XXXWindow(); 5 xWin.Value = 0; 6 xWin.Show(); 7} 8 9private void UpdateButtonClick(object sender, RoutedPropertyChangedEventArgs<double> e) 10{ 11 xWin.Value = 50; 12}

この挙動を以下のように実装しているのですが、画面のValue項目が「50」に更新されません。
処理が流れ終わってから画面が更新されます。
原因が全くわからないので、ご教授お願い致します。

C#

1XXXWindow xWin = new XXXWindow(); 2xWin.Value = 0; 3xWin.Show(); 4xWin.Value = 50;

該当のソースコード

C#

1public class XXXWindow : YYYWindow 2{ 3 public static readonly DependencyProperty ValueProperty; 4 5 [Category("")] 6 [Description("")] 7 public double Value 8 { 9 get 10 { 11 return (double)GetValue(ValueProperty); 12 } 13 set 14 { 15 SetValue(ValueProperty, value); 16 } 17 } 18 static XXXWindow() 19 { 20 ValueProperty = DependencyProperty.Register("Value", typeof(double), typeof(XXXWindow), new FrameworkPropertyMetadata(0.0)); 21 } 22 23 public XXXWindow() 24 { 25 // 画面の初期設定など 26 } 27}

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

イベントを発生させるのかと思いましたが、よく理解できませんでした。

補足

特になし

TN8001, juner👍を押しています

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

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

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

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

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

TN8001

2024/09/21 16:51

> ※独自のUIライブラリを使用しています。 ルーティングイベントを使っているということはWPFやUWP等だと思うのですが、それらではなく「独自のUIライブラリ」という意味ですか?(もしそうなら第3者は答えようがないと思うのですが...) > この挙動を以下のように実装しているのですが、画面のValue項目が「50」に更新されません。 上のコードでは更新されるが、下のコードでは更新されないという意味ですか? 下のコードはどのメソッド内にあるのですか? > 処理が流れ終わってから画面が更新されます。 「処理」というのは何ですか? 結局更新自体はされるのですか? もう少し他人にも伝わるような内容にしてください。 [ヘルプ|質問する時のヒント](https://teratail.com/help/question-tips)
rai03k

2024/09/21 17:30

申し訳ありません。最近C#を始めたばかりなので粒度の低い質問になってしまいました。 >ルーティングイベントを使っているということはWPFやUWP等だと思うのですが、 XXXWindowクラス内でDependencyPropertyが定義されているため、WPFだと思います。 内容の詳細について 上のコードは試験的に、Demo.xamlを作成してDemo.xaml.csに書いた内容になります。 下のコードはMainViewModel.csのコンストラクタに記載しています。 処理が終わってから更新されるというのは、MainViewModel.csの処理がすべて流れ切って、デバッグ実行できなくなると画面が更新されるということです。 理想としては、ボタンを押下するなどのイベントがなくても、xWin.Valueの値が変更されたら画面項目を更新したいです。 xWin.Value = 0; xWin.Show(); // Value項目が「0」で画面表示 xWin.Value = 50; // Value項目が「50」で画面表示 xWin.Value = 100; // Value項目が「100」で画面表示
hqf00342

2024/09/22 02:24 編集

XXXWindowのソースコードを提示できますか? XXXWindow.xaml, XXXWindow.xaml.csのValueプロパティや表示したいコントロールとのつながりが分かる部分でいいですが不明なら全体でもいいと思います。
rai03k

2024/09/22 04:23

XXXWindow.xamlは存在しないため、XXXWindowクラス内のコードを記載しました。 XXXWindowは外部ライブラリのため、インスタンス生成し、Showメソッドを実行するだけで画面が表示される仕様になっています。
hqf00342

2024/09/22 10:18

ありがとうございます。 このValue依存関係プロパティは値変更時のコールバックが設定されていません。 通常はRegister()の最後の引数で new FrameworkPropertyMetadata(0.0, valueChangedCallback) のようにvalueChangedCallbackを設定し、 値の変更通知を受けて必要なコントロールに反映させたりするのですが、 それがないのと、このコードだけではどうやって画面表示しているのかが分からないので 回答案(コールバックの例)を書けないですね。
guest

回答1

0

ベストアンサー

下のコードはMainViewModel.csのコンストラクタに記載しています。
処理が終わってから更新されるというのは、MainViewModel.csの処理がすべて流れ切って、デバッグ実行できなくなると画面が更新されるということです。

なるほど進捗表示ですかね?(あるいはローディング画面のような)

cs

1xWin.Show(); 2// 実際はここに時間のかかる処理がある 3xWin.Value = 50; 4// 実際はここにも時間のかかる処理がある 5xWin.Value = 100;

重い処理を別スレッドでやれば、表示は変わると思います。

cs

1public MainViewModel() 2{ 3 XXXWindow xWin = new XXXWindow(); 4 xWin.Value = 0; 5 xWin.Show(); 6 7 // 別スレッドで処理(awaitできないので投げっぱなし) 8 Task.Run(() => 9 { 10 Thread.Sleep(1000); // 何か重い処理 11 Application.Current.Dispatcher.Invoke(() => xWin.Value = 50); // UIスレッドで更新 12 13 Thread.Sleep(1000); // 何か重い処理 14 Application.Current.Dispatcher.Invoke(() => xWin.Value = 100); // UIスレッドで更新 15 }); 16}

Dispatcher.Invoke メソッド (System.Windows.Threading) | Microsoft Learn

XXXWindowクラス内でDependencyPropertyが定義されている

わざわざ依存関係プロパティを作る必要はありません。
バインドするだけで済む話です(別スレッドからの変更もOK)

xml

1<Window 2 x:Class="Q4mn6unrgvjhe45.XXXWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 Title="XXXWindow" 6 Width="800" 7 Height="450"> 8 <Grid Height="40"> 9 <ProgressBar Value="{Binding Value}" /> 10 <TextBlock 11 HorizontalAlignment="Center" 12 VerticalAlignment="Center" 13 Text="{Binding Value, StringFormat={}{0}%}" /> 14 </Grid> 15</Window>

cs

1using System.Windows; 2using CommunityToolkit.Mvvm.ComponentModel; 3 4namespace Q4mn6unrgvjhe45; 5 6public partial class MainWindow : Window 7{ 8 public MainWindow() 9 { 10 InitializeComponent(); 11 DataContext = new MainViewModel(); 12 } 13} 14 15partial class MainViewModel : ObservableObject 16{ 17 [ObservableProperty] private int value; 18 // ↓が自動生成 19 //public int Value { get => value; set => SetProperty(ref value, value); } 20 21 public MainViewModel() 22 { 23 XXXWindow xWin = new XXXWindow { DataContext = this, }; 24 xWin.Show(); 25 26 Task.Run(() => 27 { 28 Thread.Sleep(1000); 29 Value = 50; 30 Thread.Sleep(1000); 31 Value = 100; 32 }); 33 } 34}

NuGet Gallery | CommunityToolkit.Mvvm
MVVM Toolkit の概要 - Community Toolkits for .NET | Microsoft Learn


キャンセル対応等ちゃんとやるなら、IProgressを使ってください^^
IProgress<T> Interface (System) | Microsoft Learn
wpf iprogress - Google 検索


そのあたりを一切変えないとすると、MainWindowを隠しておく位しか対処はない気がします。

xml

1<Window 2 x:Class="Q4mn6unrgvjhe45.MainWindow" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 Title="MainWindow" 6 Width="800" 7 Height="450"> 8 <Window.Resources> 9 <BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" /> 10 </Window.Resources> 11 <Window.Visibility> 12 <Binding 13 Converter="{StaticResource BooleanToVisibilityConverter}" 14 Mode="TwoWay" 15 Path="IsVisible" /> 16 </Window.Visibility> 17</Window>

cs

1using System.Windows; 2using System.Windows.Data; 3using CommunityToolkit.Mvvm.ComponentModel; 4 5namespace Q4mn6unrgvjhe45; 6 7public partial class MainWindow : Window 8{ 9 public MainWindow() 10 { 11 InitializeComponent(); 12 Visibility = Visibility.Hidden; // バインドだけで済ませたいがチラッと見えちゃうので^^; 13 DataContext = new MainViewModel(); 14 } 15} 16 17partial class MainViewModel : ObservableObject 18{ 19 [ObservableProperty] private int value; 20 [ObservableProperty] private bool isVisible; 21 22 public MainViewModel() 23 { 24 XXXWindow xWin = new XXXWindow(); 25 Binding binding = new Binding(nameof(Value)) { Source = this, }; 26 xWin.SetBinding(XXXWindow.ValueProperty, binding); 27 xWin.Show(); 28 29 // 別スレッドで処理(投げっぱなし) 30 Task.Run(() => 31 { 32 Thread.Sleep(1000); // 何か重い処理 33 Value = 50; 34 Thread.Sleep(1000); // 何か重い処理 35 Value = 100; 36 37 Thread.Sleep(500); // 100%見えてから 38 Application.Current.Dispatcher.Invoke(() => xWin.Close()); 39 IsVisible = true; 40 }); 41 } 42}

重い処理を書く場所を変えていいなら、App.xaml.csでやるほうが素直な気はします。

xml

1<Application 2 x:Class="Q4mn6unrgvjhe45.App" 3 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 4 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 5 xmlns:local="clr-namespace:Q4mn6unrgvjhe45" 6 Startup="Application_Startup" />

cs

1using System.Windows; 2 3namespace Q4mn6unrgvjhe45; 4 5public partial class App : Application 6{ 7 private async void Application_Startup(object sender, StartupEventArgs e) 8 { 9 MainWindow MainWindow = new MainWindow(); 10 11 XXXWindow xWin = new XXXWindow(); 12 xWin.Value = 0; 13 xWin.Show(); 14 15 // 別スレッドでの処理を待機 16 await Task.Run(() => 17 { 18 Thread.Sleep(1000); // 何か重い処理 19 Current.Dispatcher.Invoke(() => xWin.Value = 50); // UIスレッドで更新 20 21 Thread.Sleep(1000); // 何か重い処理 22 Current.Dispatcher.Invoke(() => xWin.Value = 100); // UIスレッドで更新 23 24 Thread.Sleep(500); // 100%見えてから 25 }); 26 xWin.Close(); 27 28 MainWindow.Show(); 29 } 30}

投稿2024/09/21 21:01

編集2024/09/22 06:04
TN8001

総合スコア10086

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

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

rai03k

2024/09/22 04:49 編集

進捗表示を行いたいです! Task.Runを組み込んでみたのですが、以下の挙動になります。 MainViewModelコンストラクタが呼び出される→(MainViewModelコンストラクタ内で画面を更新したい)→MainViewModelコンストラクタの処理が流れきる→MainWindow.xaml.csに戻る→MainWindowコンストラクタ外に行くと、Task.Runが実行される。 理想としては、(MainViewModelコンストラクタ内で画面を更新したい)内で更新したいです。 >依存関係プロパティを作る必要はありません。 これはXXXWindow内で作成されているので、変更できません。(ライブラリ化されているため) XAMLは存在しなく、C#で画面を作成しているようです・・・。 見当違いな回答になっていれば申し訳ないです。
TN8001

2024/09/22 05:47 編集

> 進捗表示を行いたいです! 「MainWindowが出る前」に、読み込み中のような表示がしたいということですよね? > Task.Runを組み込んでみたのですが、以下の挙動になります。 コンストラクタではawaitできないのでそうなります。 > 理想としては、(MainViewModelコンストラクタ内で画面を更新したい)内で更新したいです。 MainViewModelは、MainWindowのコンストラクタでnewしているんですよね? そのあたりを一切変えないとすると、MainWindowを隠しておく位しか対処はない気がします。 重い処理を書く場所を変えていいなら、App.xaml.csでやるほうが素直な気はします。 [wpf startup - Google 検索](https://www.google.co.jp/search?q=wpf+startup) > これはXXXWindow内で作成されているので、変更できません。(ライブラリ化されているため) XXXWindowをいじれないのであれば、Dispatcher.Invokeするかコードでバインドするかでしょうか。
rai03k

2024/09/23 14:47

別スレッド(Task.Run)することで理想の挙動にすることができました! 表示が変わらず、寝れずに対応していたので助かりました;;
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.30%

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

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

質問する

関連した質問