I want to Enable a button from the main form whenever a second form has been closed. I've read something about invokes, but didn't understood much.
How could I achieve this?
When you close the second form, its FormClosed event is automatically raised. Before an event is raised, you can register an event handler to events. This enables you to write code, which is automatically executed, when the event occurrs.
Registering an event handler in .NET is realised by adding a delegate instance to the event. A delegate is a type that describes a method signature. If you instantiate a delegate with gcnew you associate it with a function in your code. You can call the delegate by yourself (which is not needed here) or you can pass it to some other code, which then can invoke it. The latter one is used for events.
For your case that means:
Look at the FormClosed event's delegate type. It is the FormClosedEventHandler which is defined as delegate void FormClosedEventHandler(Object^ sender, FormClosedEventArgs^ e)
This means you must implement a method returning nothing (void) and accepting two arguments: a System::Object and a System::Windows::Forms::FormClosedEventArgs
Instantiate a FormClosedEventHandler delegate and associate it with your method
Register to the FormClosed event on the second form and enable the button in the event handler.
An example:
ref class MainForm
{
...
// event handler function (compatible to the FormClosedEventHandler delegate)
void OnSecondFormClosed(Object^ sender, FormClosedEventArgs^ e)
{
myButton->Enabled = true;
}
void DoSomethingWithSecondForm(Form^ secondForm)
{
// get a disabled Button
myButton->Enabled = false;
// create an event handler by instantiating a delegate
FormClosedEventHandler^ handler = gcnew FormClosedEventHandler(this, &MainForm::OnSecondFormClosed);
// register event handler
secondForm->FormClosed += handler;
}
...
}
(I did not compile the code, but this is how it works in general)
When both involved forms are created from within the same thread, there is no need to do some additional Invoke. Otherwise you must put changes to controls into the same thread that created the control. You can achieve this by passing a delegate to Control::Invoke or Control::BeginInvoke.
// event handler function (compatible to the FormClosedEventHandler delegate)
void OnSecondFormClosed(Object^ sender, FormClosedEventArgs^ e)
{
if (myButton->InvokeRequired)
{
// create a delegate to call the same event handler again
FormClosedEventHandler^ handler = gcnew FormClosedEventHandler(this, &MainForm::OnSecondFormClosed);
// BeginInvoke causes the delegate to be called asynchronously from the UI thread
myButton->BeginInvoke(handler, sender, e);
// nothing to be done here, the actual work happens when the delegate is actually called
return;
}
myButton->Enabled = true;
}
Related
I have a form (which I'll call MainForm) embedded with a TabControl. Every time the user creates a new tab it is filled with an instance of a pre-built Panel (which I'll call MyPanel) which contains many controls.
My MyPanel class has a private variable bool save_state which is set to false every time one of the (editable) controls is edited and set to true when the user "saves" the state of the panel.
I want a visual flag to keep track of tabs that have unsaved changes (e.g. the tab "Tab1" will instead display the text "Tab1 *" if it has unsaved changes). So I want to set up the event handler in my MainForm which can call a method in MyPanel to add the handler to each control.
Since not all my controls use the same EventHandler type (for example, I also need to track DataGridViewRowsAddedEvent, among others), I currently have several methods adding the appropriate handler to the corresponding controls (one for each type of Event Handler), each of which is running the same code (i.e. set the save_state bit to false and append " *" to the tab text.
For example, in MainForm.cpp I have:
#include "MyPanel.h"
void markUnsaved(void) {
// set panel bit to false
// append " *" to tab text if we haven't already
}
void MainForm::handler1(Object ^sender, EventArgs ^e) {
markUnsaved();
}
void MainForm::handler2(Object ^sender, DataGridViewRowsAddedEventArgs ^e) {
markUnsaved();
}
void Main::FormaddNewPanelToTab(int tab_index) {
// check index is valid ...
// make the new panel
MyPanel ^new_panel = gcnew MyPanel();
new_panel->addEventHandlerToControls(gcnew EventHandler(this, &MainForm::handler1));
new_panel->addDgvEventHandlerToControls(gcnew DataGridViewRowsAddedEventHandler(this, &MainForm::handler2));
// rest of code...
}
Though this currently works as intended, this (along with the several other Event Handler types I have to manage) makes my code look really silly.
I am hoping to be able to have have a single event handler in MainForm and a single method in MyPanel which type-casts the Event Handler passed and adds it to all the controls with the appropriate types.
I have tried doing simple casts such as:
void MyPanel::addHandlerToControls(EventHandler ^handler) {
control_NUD->ValueChanged += handler; // this works because ValueChanged is of type EventHandler
control_DGV->RowsAdded += (DataGridViewRowsAddedEventHandler ^)handler; // this compiles but throws an exception
// rest of the code...
}
to no avail.
Any help would be greatly appreciated!
I know this is maybe a bit late for answer but I'd want to show how would I solve this.
Firs of all I suggest to get rid from idea of casting event handlers. Kind of such approach may work in C# (with some adjustments) but as far as I know it's not possible in C++ /CLI.
I'd go for adding new event to a MyPanel class that will be invoked every time when the data on a panel is changed. But to avoid adding a lot of different handlers to a control events in a MyPanel class it's better to create one generic method that will handle all the neccessary control's events and fire new event. Maybe this sounds messy, let me show the code:
public ref class MyPanel
{
// Add a new event
public:
event EventHandler^ DataChanged;
// Add a method that will fire new event
// this methid will be invoked on every control's event that you'll subscribe
private:
generic <typename T>
void DataChangedHandler(System::Object^ sender, T e)
{
// Fire the event
DataChanged(this, EventArgs::Empty);
}
// Once the controls are initialized you may add the event handlers
// I put it in a constructor only for example
MyPanel()
{
control_NUD->ValueChanged += gcnew EventHandler(this, &MyPanel::DataChangedHandler<EventArgs^>);
control_DGV->RowsAdded += gcnew DataGridViewRowsAddedEventHandler(this, &MyPanel::DataChangedHandler<DataGridViewRowsAddedEventArgs^>);
// and so on...
}
}
/// And now in a main form we only need to subscribe to a DataChanged event
public ref class MainForm
{
//...
// the handler
void MyHandler(Object^ sender, EventArgs^ e)
{
markUnsaved();
}
void FormaddNewPanelToTab(int tab_index)
{
// make the new panel
MyPanel ^new_panel = gcnew MyPanel();
new_panel->DataChanged += gcnew EventHandler(this, &MainForm::MyHandler);
}
//...
}
Hope this helps.
I Set an int Variable for IDC_EDIT1 Control.
now i Want Change it With a Function, But when clicking on Button, Show an Error!
void test()
{
CthDlg d;
d.m_text1 = 5;
d.UpdateData(FALSE);
}
void CthDlg::OnBnClickedOk()
{
// TODO: Add your control notification handler code here
// pThread = AfxBeginThread(ThreadFunction, THREAD_PRIORITY_NORMAL);
test();
}
In the test function you define a completely new instance of the CthDlg class, and try to modify it. That will not work as it's not properly created, and also have no relation with the actual dialog being displayed.
Instead, if test is a stand-alone (not member) function then you should pass the actual dialog instance as an argument, and use that.
For example
void tesy(CthDlg& dlg)
{
dlg.m_text1 = ...;
dlg.UpdateData(FALSE);
}
void CthDlg::OnBnClickedOk()
{
test(*this);
}
The controls are created when you call DoModal or Create.
And therefore calling UpdateData will only succeed when the Dialog is created.
This is the usual sequence: The value members may be set before you Launch the Control. The data is transferred when the Dialog is created and transfered back from the controls into the data members when the Dialog is closed with OnOK.
Well I have this code in my Managed C++/Cli in Visual Studio 2008, I want to be able to access the windows forms items inside of the callback of the Thread Function, and I can't, it generates an error.
Is there another way to do that? to be able to modify the GUI stuff inside of a method of the WinForms Class by using the Thread function callback ?
This example shows what I want to do.
I need to use a thread because I want to have the other things in the Forms to be accessible, and without using threads everything just freezes until everything is done, and the "Login" function it calls, takes some time because it does HTTP Requests. and after this HTTP Request I set the values that I got from it in a Form Element.
void Login(){
this->btn_next->Enabled = false;
this->login_accounts_facebook->Enabled = false; //This gives an error probably because of accessing "this->"
if(this->clb_contas->CheckedItems->Count <= 0){
//...
}
}
System::Void test_login_Click(System::Object^ sender, System::EventArgs^ e) {
ThreadStart^ start = gcnew ThreadStart(this, &Login_Test::Login);
Thread^ t = gcnew Thread(start);
t->Start();
}
Does anybody know how could I do that? if you think this can't be done and you want to suggest something something to make the GUI available while doing the process, I'm open for suggestions.
I hope I was being clear enough.
Thanks in advance.
All UI related code should be executed on the UI thread. In your case, that means that only the code you denoted with //... should be run on a separate thread. Extract that long-running code in its own method and pass that method to ThreadStart instead of Login(). Then you'll need to arrange for a way for the worker thread to notify the UI thread if and when it's complete.
Update:
Here's a crude example of how to modify your code. I would prefer to extract the long running operation in its own class if it is of sufficient complexity, but I think you get the idea.
The call to BeginInvoke ensures that LongRunningOperationComplete will be executed on the form's UI thread. You can use the same approach to call other methods that update the UI to indicate progress, even while the time-consuming operation is still running. If those methods require more parameters, you can create different delegates with the appropriate signature, and pass those parameters in the call to BeginInvoke. See here for how to do that.
// Same signature as LongRunningOperationComplete
delegate void MyInvokeDelegate();
void LongRunningOperation() {
for (int i=0; i < 100; i++) {
Thread::Sleep(100);
// The actual work that you're doing
}
// Operation complete. Update UI.
this->BeginInvoke(gcnew MyInvokeDelegate(this, &Login_Test::LongRunningOperationComplete));
}
void LongRunningOperationComplete() {
this->btn_next->Enabled = true;
this->login_accounts_facebook->Enabled = true;
}
System::Void StartMyLongRunningOperation() {
ThreadStart^ start = gcnew ThreadStart(this, &Login_Test::LongRunningOperation);
Thread^ t = gcnew Thread(start);
t->Start();
}
void Login() {
this->btn_next->Enabled = false;
this->login_accounts_facebook->Enabled = false; //This gives an error probably because of accessing "this->"
if(this->clb_contas->CheckedItems->Count <= 0){
StartMyLongRunningOperation();
}
}
System::Void test_login_Click(System::Object^ sender, System::EventArgs^ e) {
Login();
}
I have an RPC thread that is calling back to me from that thread. I need to somehow inform Qt that it needs to make a function call from the main thread. In straight Windows I could do this by using a custom message and then posting that message to the message queue, e.g., I could create a WM_CALLFUNCTION message and pass the function pointer through wParam and the parameter (class pointer) through lParam.
Has anyone an idea how I could do this with Qt? I've come across QCustomEvent but I have no idea how to use it or how to process it. Any help would be hugely appreciated!
Edit:
In the end I went with QMetaObject::invokeMethod which works perfectly.
Using custom events generally involves creating your own QEvent subclass, overriding customEvent() in the QObject class that will receive the event (often the main window class) and some code that "posts" the event from your thread to the receiver.
I like to implement the event posting code as a method of the receiver class. That way, the caller only has to know about the recevier object and not any of the "Qt" specifics. The caller will invoke this method which will then essentially post a message to itself. Hopefully the code below will make it clearer.
// MainWindow.h
...
// Define your custom event identifier
const QEvent::Type MY_CUSTOM_EVENT = static_cast<QEvent::Type>(QEvent::User + 1);
// Define your custom event subclass
class MyCustomEvent : public QEvent
{
public:
MyCustomEvent(const int customData1, const int customData2):
QEvent(MY_CUSTOM_EVENT),
m_customData1(customData1),
m_customData2(customData2)
{
}
int getCustomData1() const
{
return m_customData1;
}
int getCustomData2() const
{
return m_customData2;
}
private:
int m_customData1;
int m_customData2;
};
public:
void postMyCustomEvent(const int customData1, const int customData2);
....
protected:
void customEvent(QEvent *event); // This overrides QObject::customEvent()
...
private:
void handleMyCustomEvent(const MyCustomEvent *event);
The customData1 and customData2 are there to demonstrate how you might pass some data along in your event. They don't have to be ints.
// MainWindow.cpp
...
void MainWindow::postMyCustomEvent(const int customData1, const int customData2)
{
// This method (postMyCustomEvent) can be called from any thread
QApplication::postEvent(this, new MyCustomEvent(customData1, customData2));
}
void MainWindow::customEvent(QEvent * event)
{
// When we get here, we've crossed the thread boundary and are now
// executing in the Qt object's thread
if(event->type() == MY_CUSTOM_EVENT)
{
handleMyCustomEvent(static_cast<MyCustomEvent *>(event));
}
// use more else ifs to handle other custom events
}
void MainWindow::handleMyCustomEvent(const MyCustomEvent *event)
{
// Now you can safely do something with your Qt objects.
// Access your custom data using event->getCustomData1() etc.
}
I hope I didn't leave anything out. With this in place, code in some other thread just needs to get a pointer to a MainWindow object (let's call it mainWindow) and call
mainWindow->postMyCustomEvent(1,2);
where, just for our example, 1 and 2 can be any integer data.
In Qt 3, the usual way to communicate
with the GUI thread from a non-GUI
thread was by posting a custom event
to a QObject in the GUI thread. In Qt
4, this still works and can be
generalized to the case where one
thread needs to communicate with any
other thread that has an event loop.
To ease programming, Qt 4 also allows
you to establish signal--slot
connections across threads. Behind the
scenes, these connections are
implemented using an event. If the
signal has any parameters, these are
also stored in the event. Like
previously, if the sender and receiver
live in the same thread, Qt makes a
direct function call.
--
http://doc.qt.nokia.com/qq/qq14-threading.html#signalslotconnectionsacrossthreads
I'm trying to use a custom event in my WxWidgets C++ application, like described here.
In the constructor of my wxApp:
Connect(wxID_ANY, wxCommandEventHandler(APP::OnMyEvent));
Then the function that should catch the event:
void APP::OnMyEvent(wxCommandEvent& event)
{
exit(0); //testing
}
Finally, to test it:
wxCommandEvent MyEvent(wxEVT_COMMAND_BUTTON_CLICKED);
wxPostEvent(this, MyEvent);
I launch the thing...but it seems that the event is not posted or not caught.
Does someone understand this behaviour ?
You appear to be using the following overload of Connect:
void Connect(wxEventType eventType, wxObjectEventFunction function,
wxObject* userData = NULL, wxEvtHandler* eventSink = NULL)
If so, then should an event of type wxID_ANY happen (never?), then the connected function will be called.
Perhaps you need:
Connect(wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(APP::OnMyEvent));