none
アンマネージドクラスからのフォームの更新 RRS feed

  • 質問

  • Visual Studio 2013 Express Edition でアプリを製作しています。

    ラベル(label1)とボタン(button1)を1つずつ配置したフォームを1つ(Form1)、そして

    アンマネージドクラスを1つ(Test_Class)作りました。

    フォームクラス(Form1)、アンマネージドクラス(Test_Class)がある状況です。

    それから、フォームクラスには label1 の Text を書き換える関数("set_text")を、

    Test_Class にはその関数を呼び出す関数("call_set_text")を作りました。

    仕組みとしては、Form1 の button1 を押すと Test_Class の 関数 "call_set_text" が呼び出され、

    そこから Form1 の関数 "set_text" が呼び出されて Form1 の label1 が書き換えられるというものです。

    ところが button1 を押しても label1 の Text が更新されません。関数自体が呼び出されていることは

    確認したのですが、原因が分かりません。

    どなたかこの現象の原因と解決方法をご教授いただけないでしょうか。

    以下にソースコードを記載しました。どうかよろしくお願いします。

    //Form1.h-----------------------------------------------------
    
    #pragma once
    #include "..\Test_Class\Test_Class.h"
    
    namespace label_test {
    
    	using namespace System;
    	using namespace System::ComponentModel;
    	using namespace System::Collections;
    	using namespace System::Windows::Forms;
    	using namespace System::Data;
    	using namespace System::Drawing;
    
    	/// <summary>
    	/// Form1 の概要
    	/// </summary>
    	public ref class Form1 : public System::Windows::Forms::Form
    	{
    	public:
    		Form1(void)
    		{
    			InitializeComponent();
    			//
    			//TODO: ここにコンストラクター コードを追加します
    			//
    		}
    
    	protected:
    		/// <summary>
    		/// 使用中のリソースをすべてクリーンアップします。
    		/// </summary>
    		~Form1()
    		{
    			if (components)
    			{
    				delete components;
    			}
    		}
    	private: System::Windows::Forms::Label^  label1;
    	private: System::Windows::Forms::Button^  button1;
    	protected:
    
    	private:
    		/// <summary>
    		/// 必要なデザイナー変数です。
    		/// </summary>
    		System::ComponentModel::Container ^components;
    
    		Test_Class *test_class;
    
    	public: void set_text(String^ text);
    
    #pragma region Windows Form Designer generated code
    		/// <summary>
    		/// デザイナー サポートに必要なメソッドです。このメソッドの内容を
    		/// コード エディターで変更しないでください。
    		/// </summary>
    		void InitializeComponent(void)
    		{
    			this->label1 = (gcnew System::Windows::Forms::Label());
    			this->button1 = (gcnew System::Windows::Forms::Button());
    			this->SuspendLayout();
    			// 
    			// label1
    			// 
    			this->label1->AutoSize = true;
    			this->label1->Font = (gcnew System::Drawing::Font(L"MS UI Gothic", 18, System::Drawing::FontStyle::Regular, System::Drawing::GraphicsUnit::Point,
    				static_cast<System::Byte>(128)));
    			this->label1->Location = System::Drawing::Point(104, 24);
    			this->label1->Name = L"label1";
    			this->label1->Size = System::Drawing::Size(67, 24);
    			this->label1->TabIndex = 0;
    			this->label1->Text = L"label1";
    			this->label1->TextAlign = System::Drawing::ContentAlignment::MiddleCenter;
    			// 
    			// button1
    			// 
    			this->button1->Location = System::Drawing::Point(64, 72);
    			this->button1->Name = L"button1";
    			this->button1->Size = System::Drawing::Size(147, 40);
    			this->button1->TabIndex = 1;
    			this->button1->Text = L"button1";
    			this->button1->UseVisualStyleBackColor = true;
    			this->button1->Click += gcnew System::EventHandler(this, &Form1::button1_Click);
    			// 
    			// Form1
    			// 
    			this->AutoScaleDimensions = System::Drawing::SizeF(6, 12);
    			this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
    			this->ClientSize = System::Drawing::Size(273, 147);
    			this->Controls->Add(this->button1);
    			this->Controls->Add(this->label1);
    			this->MaximizeBox = false;
    			this->MinimizeBox = false;
    			this->Name = L"Form1";
    			this->StartPosition = System::Windows::Forms::FormStartPosition::CenterScreen;
    			this->Text = L"Form1";
    			this->Shown += gcnew System::EventHandler(this, &Form1::Form1_Shown);
    			this->ResumeLayout(false);
    			this->PerformLayout();
    
    		}
    #pragma endregion
    	private: System::Void Form1_Shown(System::Object^  sender, System::EventArgs^  e) {
    				 test_class = new Test_Class();
    	}
    	private: System::Void button1_Click(System::Object^  sender, System::EventArgs^  e) {
    				 test_class->call_set_text();
    	}
    	};
    }
    
    
    //Form1.cpp---------------------------------------------------
    #include "Form1.h"
    
    using namespace label_test;
    
    [STAThreadAttribute]
    
    int main(array<System::String ^> ^args)
    {
    	Application::EnableVisualStyles();
    	Application::SetCompatibleTextRenderingDefault(false);
    	Application::Run(gcnew Form1());
    	return 0;
    }
    
    void Form1::set_text(String^ text)
    {
    	label1->Text = text;
    }
    
    
    //Test_Class.h------------------------------------------------
    #pragma once
    
    class Test_Class
    {
    public:
    	Test_Class();
    	~Test_Class();
    
    	void call_set_text(void);
    };
    
    
    //Test_Class.cpp----------------------------------------------
    #include "Test_Class.h"
    #include "..\Form1\Form1.h"
    #include <vcclr.h>
    
    using namespace label_test;
    
    Test_Class::Test_Class()
    {
    }
    
    Test_Class::~Test_Class()
    {
    }
    
    void Test_Class::call_set_text(void)
    {
    	gcroot<Form1^> form1 = gcnew Form1();
    
    	form1->set_text(L"Hello World !");
    
    	//delete form1;
    }


    • 編集済み Snow13 2013年12月7日 8:18
    2013年12月7日 6:00

回答

  • 佐祐理さんが指摘するように、あなたがやりたいことを実現するためには Form1 を複数作ってはいけません。
    (main で gcnew した Form1 と、Test_Class が gcnew した Form1 は別物であるため、同じものを共有する考えを持たなければならない)

    この手のケースの場合、(1)Form1 の既存の参照を相手のクラスに渡して使えるようにする(循環参照は許容する)、(2)相手のクラスからイベント・コールバックを受けて Form1 側が更新するという 2 つのことが考えられます。
    (1)のケースでもあらかじめクラスに設定しておく、引数で渡すの 2 パターンが考えられます。

    (1)のケースでは gcroot クラスを引数に渡す、メンバー変数に作ることを考えてください。
    (2)のケースでは一般的なコールバック関数を Test_Class 側に用意しつつ、それに渡せるデリゲート・関数ポインタの得る方法を調べてください。

    // 具体的なコードを書くのは、準備の手間があるのでこの時点では避けます。
    // 個人的には、C++/CLI で進めていって大丈夫かなと不安を感じますが…。

    • 回答としてマーク Snow13 2013年12月7日 11:19
    2013年12月7日 10:46
    モデレータ

すべての返信

  • managed / unmanaged以前の問題としてオブジェクト指向が理解できていないように思います。

    | gcroot<Form1^> form1 = gcnew Form1();
    | form1->set_text(L"Hello World !");
    | delete form1;

    これはどういう意味かわかっていますか? たとえオブジェクト指向を理解できていなくても、設定した内容が即座にdeleteされている点について、疑問を感じないのでしょうか。もし感じないということであれば、プログラミングよりも前に、中学生レベルの英語を勉強し、deleteにどんな意味があるのか、その他の単語もそれぞれの意味をある程度察しが付くようになるべきです。

    2013年12月7日 6:16
  • 早速のご返信ありがとうございます。

    当方プログラミング初心者でして、英語については理解しておりますが

    ご指摘のありました「オブジェクト指向」については十分な理解ができておりません。

    それについては今後学んでいきます。

    誠に恐縮でございますが、解決方法をご教授いただければ幸甚に存じます。

    2013年12月7日 7:00
  • 佐祐理さんが指摘するように、あなたがやりたいことを実現するためには Form1 を複数作ってはいけません。
    (main で gcnew した Form1 と、Test_Class が gcnew した Form1 は別物であるため、同じものを共有する考えを持たなければならない)

    この手のケースの場合、(1)Form1 の既存の参照を相手のクラスに渡して使えるようにする(循環参照は許容する)、(2)相手のクラスからイベント・コールバックを受けて Form1 側が更新するという 2 つのことが考えられます。
    (1)のケースでもあらかじめクラスに設定しておく、引数で渡すの 2 パターンが考えられます。

    (1)のケースでは gcroot クラスを引数に渡す、メンバー変数に作ることを考えてください。
    (2)のケースでは一般的なコールバック関数を Test_Class 側に用意しつつ、それに渡せるデリゲート・関数ポインタの得る方法を調べてください。

    // 具体的なコードを書くのは、準備の手間があるのでこの時点では避けます。
    // 個人的には、C++/CLI で進めていって大丈夫かなと不安を感じますが…。

    • 回答としてマーク Snow13 2013年12月7日 11:19
    2013年12月7日 10:46
    モデレータ
  • ご返信ありがとうございます。

    「共有する」ということがポイントなのですね。勉強になります。

    コードを書いていただければ有り難いですが、後は自分で

    調べていきます。よって、 Azulean さんのご返信を回答とさせて

    いただきます。具体的な道を示していただきありがとうございました。

    2013年12月7日 11:19