C#データバインディングされたプロパティの更新

このQ&Aのポイント
  • C#のプロパティにバインディングされたラベルの更新方法
  • C#のINotifyPropertyChangedを実装したプロパティをバインディングしてラベルに値を表示する方法
  • C#のプロパティの値を更新する際に発生する例外とその解決方法
回答を見る
  • ベストアンサー

C# データバインディングされたプロパティの更新

C#の質問になります。 INotifyPropertyChangedを実装したプロパティをラベルにバインディングして、プロパティの値を表示しています。 ボタンを押下するとプロパティの値を更新するプログラムを追加しました。プロパティの値が変わるとラベルの表記も自動で更新されています。 ある時、プロパティに収める値を演算する処理に3秒掛かるようになりました。結果がラベルに反映されるまでの時間は重要ではないため、この部分をスレッド化しようと思い、下記のようなプログラムを作成しました。 結果として、下記のプログラムはInvalidOperationExceptionを発生し動作しません。 別スレッドからラベルにバインディングしているプロパティを変更したためですが、正しい?(一般的な)解決法がわかりません。 そもそも設計が間違っているという考えも大いにあり得るのですが、その場合、どの様に実装すべきかご教示いただければと思います。 public partial class Form1 : Form { private SampleViewModel svm = new SampleViewModel(); public Form1() { InitializeComponent(); } private void Form1_Load(object sender, EventArgs e) { label1.DataBindings.Add(new Binding("Text", svm.Data, "MyProperty", true, DataSourceUpdateMode.OnPropertyChanged)); } private void button1_Click(object sender, EventArgs e) { // データ更新 svm.Update(); } } internal sealed class SampleViewModel { public SampleModel Data { get { return this._Data; } set { this._Data = value; } } private SampleModel _Data = new SampleModel(); public void Update() { // 重い処理がなかったときは下記の1行だった //Data.MyProperty = DateTime.Now.ToLongTimeString(); // 重い処理をスレッドで処理 // label1にMyPropertyをバインディングしているからInvalidOperationExceptionが発生した Task.Factory.StartNew(() => { // 重い処理ダミー System.Threading.Thread.Sleep(3000); return DateTime.Now.ToLongTimeString(); } ).ContinueWith(task => { Data.MyProperty = task.Result; }); } } // 以下お決まりの実装 internal sealed class SampleModel : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public string MyProperty { get { return this._MyProperty; } set { this._MyProperty = value; if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs("MyProperty")); } } } private string _MyProperty = DateTime.Now.ToLongTimeString(); }

質問者が選んだベストアンサー

  • ベストアンサー
  • amyaad
  • ベストアンサー率100% (8/8)
回答No.1

UIを更新できるのはUIスレッドであり、別スレッドから更新しようとすると、InvalidOperationExceptionが発生します。参考URLそのままなのですが、 Update()で、 var syncContext = SynchronizationContext.Current;としておき、 ContinueWith(task => { Data.MyProperty = task.Result; }); の部分を .ContinueWith(task => { Data.MyProperty = task.Result; },TaskScheduler.FromCurrentSynchronizationContext()); とかすればよいです。 http://ufcpp.wordpress.com/2012/04/26/%E9%9D%9E%E5%90%8C%E6%9C%9F%E5%87%A6%E7%90%86%E3%81%A8%E3%83%87%E3%82%A3%E3%82%B9%E3%83%91%E3%83%83%E3%83%81%E3%83%A3%E3%83%BC/

ctmogawa
質問者

お礼

ありがとうございます。 無事に解決できました。 自分ではGUIを直接変更していないからとGUIスレッドの考えが無くなっていました。 InvalidOperationExceptionエラーなのだからもっとよく考えれば良かったです。

関連するQ&A

  • C# スレッド終了の監視について

    お世話になります。 C#2005でプログラムを作成しております。 マルチスレッドでの、スレッドの終了の監視のことでご質問させていただきます。 下記のコードを実行すると、問題なく実行されます。 ------------------------------------------------------------------------------------ private int SetDataTreeView() { Thread RcvSetThread = new Thread(new ThreadStart(this.RcvDataSet)); RcvSetThread.Start(); } delegate void RcvDataSetDelegate(); void RcvDataGridSet() { /* フォーム内のDatagidviewへの値の代入 */ } void RcvDataSet() { Invoke(new RcvDataSetDelegate(RcvDataGridSet)); } ------------------------------------------------------------------------------------ このスレッドの終了を監視したく、下記のコードを追加してデバッグしてみましたが、 スレッドが実行されませんでした。 ------------------------------------------------------------------------------------ int isRcvSearchFlg = 0; private int SetDataTreeView() { Thread RcvSetThread = new Thread(new ThreadStart(this.RcvDataSet)); RcvSetThread.Start(); // スレッドが終了するまで待機する for (; ; ) { if (isRcvSearchFlg == 1) { break; } } } delegate void RcvDataSetDelegate(); void RcvDataGridSet() { /* フォーム内のDatagidviewへの値の代入 */ isRcvSearchFlg = 1; } void RcvDataSet() { Invoke(new RcvDataSetDelegate(RcvDataGridSet)); } ------------------------------------------------------------------------------------ また、以下も試してみましたが、結果は同じでした。 ------------------------------------------------------------------------------------ private int SetDataTreeView() { Thread RcvSetThread = new Thread(new ThreadStart(this.RcvDataSet)); RcvSetThread.Start(); // スレッドが終了するまで待機する RcvSetThread.Join(); } delegate void RcvDataSetDelegate(); void RcvDataGridSet() { /* フォーム内のDatagidviewへの値の代入処理 */ } void RcvDataSet() { Invoke(new RcvDataSetDelegate(RcvDataGridSet)); } ------------------------------------------------------------------------------------ スレッドの終了を監視する方法がわからず困っております。 お手数ですが、ご教授いただきたくよろしくお願い申し上げます。

  • データバインディングで代入を検出できません。

    var RssXML:XML; private var RssxmlLoader:URLLoader; private function init():void { var RssUrl:String ="http://example.com/ex.xml"; RssxmlLoader = new URLLoader(); RssxmlLoader.dataFormat = URLLoaderDataFormat.TEXT; RssxmlLoader.addEventListener(Event.COMPLETE, RssxmlComplete); var RssurlReq:URLRequest = new URLRequest(RssUrl); RssxmlLoader.load(RssurlReq); }; private function RssxmlComplete(e:Event):void { RssXML =new XML(RssxmlLoader.data); trace(RssXML); }; <mx:VBox label="一般" width="100%" height="100%"> <mx:DataGrid width="777" height="339" dataProvider="{RssXML}"> <mx:columns> <mx:DataGridColumn dataField="title" headerText="名前"/> <mx:DataGridColumn dataField="link" headerText="値"/> </mx:columns> </mx:DataGrid> これでデータバインディングでは"RssXML"への代入を検出できません。となってしまうのですが、どうしたらよいでしょうか?

  • C#でプロパティを使わずXMLにデータを保存したい

    C#を使っているのでC・C++のジャンルでいいのか分からないのですがこちらで質問させて下さい。 現在簡単な本の管理をするアプリを作っています。 プロパティをできるだけ使わずにカプセル化して作っていたのですが、肝心のデータを保存する段階で分からなくなりました。 public class Book { private static int _id_gen; private int _id2; private string _isbn; private string _name; public Book(string isbn, string name) { this._isbn = isbn; this._name = name; } } このようにコンストラクタでデータを渡して get プロパティだけあとで追加するようにしています。 こういうデータを保存する際はまとめて保存するのではなくて List<Book> なんかを foreach で回して1件ずつ保存するといいのでしょうか? 読み込む際は1件ずつ読み込んでクラスに設定していくというのが標準的な考え方ですか? 「C# XML 保存」で検索をかけると XmlSerializer でまとめて保存する事例ばかりでちょっと分からなくなってしまいました。 よろしくお願いします。

  • 【C#】作成したファイルに書き込みしたい

    開発環境はMicrosoft VisualC#2008です。 C#初心者です。よろしくお願いします。 現在、Form1とUserControl1を組み合わせてパズルゲームを作成中です。 Form1でボタンをクリックすると、クリックした日時を取得して、その日時をファイル名とするテキストファイルを作成することは出来ました。(例:20100115154717.txt) 今度は、そのファイルをUserControl1で開いて、書き込みをしていきたいのですが、エラーが出てしまい、この作成したファイルに書き込むことが出来ない状況です。 ※ちなみに、別のファイル(log_sample.txt)を指定したら書き込みは出来ました。 アドバイスよろしくお願いします。 <Form1.cs>一部抜粋 public string File_name { get { return this.file_name; } set { this.file_name = value; } } private void select_button_Click(object sender, EventArgs e) { //開いた時刻をファイル名に指定 DateTime dt = DateTime.Now; file_name = dt.ToString("yyyyMMddHHmmss"); //ログファイル作成・データ追加 StreamWriter sw = new StreamWriter(new FileStream"C:\\Documents and Settings\\My Documents\\log\\" + file_name + ".txt", FileMode.CreateNew)); } <UserControl1.cs>一部抜粋 //候補選択 private void label1_Click(object sender, EventArgs e) { Form1 f1 = this.Controls["Form1"] as Form1; DateTime dt = DateTime.Now; //ログ書き込み(日付、場所(ユーザーコントロール、ラベル名)、クリック(1)orダブルクリック(2)) StreamWriter sw = new StreamWriter(new FileStream("C:\\Documents and Settings\\My Documents\\log\\"+ f1.File_name +".txt", FileMode.Append)); sw.Write(dt.ToString()+","); sw.Write(this.Name +","); sw.Write("label1,"); sw.WriteLine("1"); sw.Close(); }

  • 別スレッドとイベントの終了手順について

    C#の質問になります。 メインフォーム上で別スレッドを起動し、別スレッドからのイベントで メインフォーム上のテキストボックスにメッセージを表示しています。 サンプルソースはフォームにボタン2個とテキストボックス1個を貼り 付けたものになり、ボタン1でスレッド起動、ボタン2で停止させてい ます。 正常パターンでボタン1とボタン2を交互に押下すると意図したとおり テキストボックスにメッセージが出力されます。 このプログラムで、ボタン1を押下し別スレッドが起動した状態で、 フォームの×ボタンを押下すると別スレッドの停止処理中にJoin() 呼び出しで永久に止まってしまいます。 止めるべきスレッド中でイベント(OnTraceEvent)を呼び出している のが問題のような気がします。(この処理がなければ正常) このような時の終了手順の王道的なものはありますでしょうか。   public partial class Form1 : Form   {     ThreadTest _thread = null;     public Form1()     {       InitializeComponent();     }     private void button1_Click( object sender, EventArgs e )     {       if ( this._thread == null )       {         this._thread = new ThreadTest();         this._thread.TraceEvent += new ThreadTest.TraceEventHandler( OnTrace );         this._thread.Open();       }     }     private void button2_Click( object sender, EventArgs e )     {       if ( this._thread != null )       {         this._thread.Close();         this._thread.TraceEvent -= new ThreadTest.TraceEventHandler( OnTrace );         this._thread = null;       }     }     private void OnTrace(String message)     {       if ( this.IsHandleCreated == false )       {         return;       }       MethodInvoker process = (MethodInvoker)delegate()       {         textBox1.AppendText( message + "\r\n" );       };       if ( this.InvokeRequired )       {         this.Invoke( process );       }       else       {         process.Invoke();       }       return;     }     private void Form1_FormClosed( object sender, FormClosedEventArgs e )     {       //フォームの×ボタンを押下した時にスレッドを停止しないと       //破棄されたコントロールを操作しようとするため下記を追加       if ( this._thread != null )       {         this._thread.Close();         this._thread.TraceEvent -= new ThreadTest.TraceEventHandler( OnTrace );         this._thread = null;       }     }   }   class ThreadTest   {     public delegate void TraceEventHandler( String message );     public event TraceEventHandler TraceEvent;     protected virtual void OnTraceEvent( String message )     {       TraceEventHandler TraceEventTemp = TraceEvent;       if ( TraceEventTemp != null )       {         TraceEventTemp( message );       }     }     private Thread _threadLoop = null;     private volatile Boolean _threadFlag = false;     public void Open()     {       //スレッド開始       if ( this._threadLoop == null )       {         this._threadLoop = new Thread( new ThreadStart( Loop ) );         this._threadLoop.Start();         while ( !this._threadLoop.IsAlive ) ;       }     }     public void Close()     {       //スレッド停止       if ( this._threadLoop != null )       {         this._threadFlag = false;         //this._threadLoop.Abort();//ここを有効にすればとりあえず終了する         this._threadLoop.Join();         this._threadLoop = null;       }     }     public void Loop()     {       this._threadFlag = true;       while ( this._threadFlag )       {         OnTraceEvent( DateTime.Now.ToString( "yyyy/MM/dd hh:mm:ss:fff" ) );         Thread.Sleep( 100 );       }     }   }

  • 2つのスレッドの実行について

    お世話になります。 VS2005C#で作成しております。 以下のコードにてスレッドを2つ実行させ、終了を監視する処理を記述しました。 ----------------------------------------------------------------------------------------------------------- isRcvSearchFlg = 0; isFwdSearchFlg = 0; private int SetDataTreeView() { // それぞれのデータ格納処理をスレッドで処理する Thread RcvSetThread = new Thread(new ThreadStart(this.RcvDataSet)); Thread FwdSetThread = new Thread(new ThreadStart(this.FwdDataSet));   RcvSetThread.Start(); FwdSetThread.Start(); // 両方のスレッドが終了するまで待機する while (isRcvSearchFlg != 1 && isFwdSearchFlg != 1) Application.DoEvents(); } delegate void RcvDataSetDelegate(); void RcvDataGridSet() { /* メインフォーム内のDatagidview(1)への値の代入 */ isRcvSearchFlg = 1; } void RcvDataSet() { Invoke(new RcvDataSetDelegate(RcvDataGridSet)); } delegate void FwdDataSetDelegate(); private void FwdDataGridSet() { /* メインフォーム内のDatagidview(2)への値の代入 */ isRcvSearchFlg = 1; } void FwdDataSet() { Invoke(new FwdDataSetDelegate(FwdDataGridSet)); } ----------------------------------------------------------------------------------------------------------- RcvDataGridSet、FwdDataGridSetでログを出力して、進行状況を監視しておりますが、どうも2つのスレッドが同時に走っていないようなのです。 原因として考えられることはございますでしょうか。 また、同時に走らせるコードはありますでしょうか。 お手数ですが、ご教授いただきたく宜しくお願い申し上げます。

  • C#のスレッド動作について

    下記はスレッドの中で永久ループさせるテストプログラムで、1秒毎にテキストボックス内の数値をインクリメントします。 【問題点1】 ※1と※2のコマンドを削除した状態(※3のみ)で正常な動作を期待していたのですが、実際にはフリーズ状態となり、カウント値が表示されません。 ※2のApplication.DoEvent()を実装するか、※3の替わりに※1に実装すると正常動作となります。 ExecThread実行中は他の処理を出来なくても、これを抜けた時点で表示処理に移るので表示される筈と思っていたのですが違う様です。 【問題点2】 Invokeの替わりにBeginInvokeを使えばExecThread実行中でも他の処理と並列処理されると思っていたのですが、スレッドを2個用意して試したところInvokeと全く変わらず、やはりフリーズ状態となります。 【問題点3】 ExecThreadを匿名メソッドにすると「フィールド初期化子は、静的でないフィールド、メソッド、又はプロパティ'iCount'を参照できません」のエラーとなり、「iCount」をstatic変数にするとOKになります。 匿名メソッドではインスタンス変数は使えないのでしょうか。 上記3項目についてネットで調べたのですがその様な記述は見当たりませんでした。 何か使い方が間違っているのでしょうか? 間違い点など、ご指摘頂ければ有難いです。 どうぞ宜しくお願いします。 private void ExecWorker() ← スレッド {   while (true)   {     Invoke(new Exec1Delegate(this.ExecThread)); ← BeginInvokeを使っても症状は同じ     iCount++;     Thread.Sleep(1000); ← ※1 このSleepが無いとフリーズする     Application.DoEvent(); ← ※2 これがあると※1のSleepが無くてもOK   } } delegate void ExecDelegate(); private void ExecThread() {   lbThread.Text = iCount.ToString(); ← 匿名メソッドにするとエラーになる   Thread.Sleep(1000); ← ※3 (※1のSleepと同時実装はしない) }

  • C# プロパティのスコープ/寿命について

    たびたびすみません。 いつもありがとうございます。 C#でウィンドウアプリケーションを作成しています。 今回下のような TestClass クラスを作りました。 TestClass クラスの中に Paths プロパティを持っているのですが、 このプロパティは、コンストラクタの一番下に到達した時点で開放されています。 プロパティのスコープってそういうものでしょうか。 この TestClass クラスは上位で new されてインスタンスが作成されていますが、 このインスタンスが存在する間はデータが保持されると思っていたのです。 逆に、インスタンスが存在する間、そのクラス内で値を保持するには、どうするのでしょうか? よろしくお願い致します。 public partial class MainForm : Form {   // プロパティ   TestClass Test {get; set;}   private void MainForm_Load(object sender, EventArgs e)   {     Test = new TestClass();   } } class TestClass() {   // プロパティ   string[] Paths {get; set;}   // コンストラクタ   // Dirクラスには、複数のディレクトリ情報が格納されている   public TestClass(Dir[] dirs)   {     try {       string[] TagPaths = new string[Const.MaxNum.tag];       // Dirクラスのディレクトリ情報のうち、パスを取り出す       for (int i = 0; i < n; i++) {         Paths[i] = dirs[i].Path;       }       // 某処理       ActiveFAPanel = SetupXXX(Paths);     }     catch(Exception ex)     {     }   } ←●●●● ここに来た時点でPathsはnullとなっている }

  • C#で、ある条件でFormが自動で閉じるプログラム

    C#で、例えばForm2をForm1から開き、ある問題があってForm2のボタンを押すとForm2のラベルに正解と表示されます。 その文字が表示されたら数秒後に自動でForm2が閉じられるプログラムを書きたいのですがうまくいきません。 現在、こう書いています。3秒後とします。 using System.Threading;は宣言しています。 private void Form2_FormClosing(object sender, FormClosingEventArgs e) { if(label1.Text=="正解です。") { Thread.Sleep(3000); e.Cancel= false; } こうするといつまでも閉じてくれません。 ボタンクリックプログラムに、if文で、~ならば正解です、~ならば間違いです、などとふり分けている中、正解のところに label1.Text="正解です。"; Thread.Sleep(3000); this.Close(); などとすると、ボタンを押したら、ラベルには何も表示されずに3秒後に閉じます。 どうすればいいでしょうか?

  • LaravelのコントローラーにプロパティはOK?

    お世話になっております。 保守開発でPHPの案件に参画したのですが、既存のソースコードを見たところ、 コントローラーの実装で以下のようなものがありました。 class HogeController extends Controller { private $hoge = null; public function index(Request $request_) { $this->hoge = new stdClass; .... } public function next(Request $request_) { $foo = $this->hoge->foo; .... } .... コントローラーにインスタンス変数(プロパティ)があって、 それに対していろいろと処理をしているのですが、 この実装は問題ないでしょうか? JavaでWebのMVCを開発してきた経験からすると、コントローラーは Singleton である場合が多いのではないかと思うのです。 当案件での実行環境等は、PHP7.2, Laravel5.6 です。 よろしくお願いいたします。

    • ベストアンサー
    • PHP