Unify MFC messages and Qt signals-slots/events - c++

I've got an assignment to create sort of a multi-platform C++ GUI library. It wraps different GUI frameworks on different platforms. The library itself provides an interface via which the user communicates uniformly (using the same code) regardless of the platform he's using - typically it's MFC on Windows and Qt elsewhere.
Currently, I'm stuck on messages/events. So far, I've been using MFC messages and Qt signals to get notified of the user's interaction and had no problems with that. The user creates a list box (which contains p_lb - pointer subclassed QListWidget on Qt or CListBox on MFC) and then he ties his own DoSomething() handler with an event.
User's code:
ListBox lb;
lb.AddDoubleClickHandler([](int i) { DoSomething(i); });
This is what it looks like in the library:
// p_lb represents platform list box - QListWidget/CListBox
// handler's int parameter represents the index of the item, that has been double-clicked
void ListBox::AddDoubleClickHandler(const std::function<void(int)>& handler) {
#ifdef _MFC_PLATFORM
// only pushes the handler into the internal list
p_lb->double_click_handlers_.push_back(handler);
#elif _QT_PLATFORM
p_lb->connect(p_lb, &QListWidget::itemDoubleClicked, p_lb,
[handler, p_lb](QListWidgetItem* item) { handler(p_lb->row(item)); });
#endif
}
In MFC I need to call the handlers stored in the list manually:
#ifdef _MFC_PLATFORM
class MyMFCListBox : public CListBox {
public:
std::list<std::function<void(int)>> double_click_handlers_;
afx_msg void OnItemDoubleClick() {
for (const auto& h : double_click_handlers_) {
h(GetCaretIndex()); // call user's stored handler when the event occurs
}
}
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(MyMFCListBox, CListBox)
ON_CONTROL_REFLECT(LBN_DBLCLK, &MyMFCListBox::OnItemDoubleClick)
END_MESSAGE_MAP()
#endif // _MFC_PLATFORM
As I said, this works fine, but now I need to be able to notify UI control about some external event manually (and asynchronously) - using something like MFC's PostMessage. The use case would be something like updating progress bar based on some external computing (which is not necessarily done using this library). E.g. I'm uploading some photos, after each one of them I send a message to the ProgressBar about the current number of uploaded photos and number of all the photos, but I return immediately without waiting for the handler to execute. Also, there needs to be a way of adding new custom events by the user. I know that Qt has a similar method - postEvent, but is this the right way to implement it? Or should I implement my own signal-slot system? I cannot imagine how this would work. Any suggestions are appreciated.

Related

click event of WindowsFormsControlLibrary button in MFC Dialog based app

I'm using some Windows Forms Control Library elements in my app.
My question is:
How to perform button click event which this element comes from Windows Forms Control Library?
So, I can get *library* textbox value in ProgramDlg.cpp file like this:
void CMFCApplication1Dlg::OnBnClickedButton1()
{
// TODO: Add your control notification handler code here
AfxMessageBox(CString(m_ctrl1.GetControl()->textBox1->Text));
// m_ctrl1.GetControl()->button1->Click();
// how can I write this above line to perform click event?
}
I defined m_ctrl1 in ProgramDlg.h:
// ....
public:
CMFCApplication1Dlg(CWnd* pParent = NULL); // standard constructor
// Data member for the .NET User Control:
CWinFormsControl<WindowsFormsControlLibrary1::UserControl1> m_ctrl1;
// ....
p.s sorry for my bad english.
Thanks.
I solved my problem by visiting this link.
Hope to be useful for other developers.

Event system similar to C# (.NET?)

In C# (at least using .NET, but I think it's general), you can create events like this: Understanding events and event handlers in C#.
Is there a similar mechanism for C++?
PS: I've never liked signal/slot system, so please don't suggest it, since I'm already using it and would love to switch to something else.
The event mechanism in C# is really just a formal, language implemented version of the Observer Pattern. This pattern can be implemented in any language, including C++. There are many examples of implementations in C++.
The largest and most common implementation is probably Boost.Signals and Boost.Signals2, though you explicitly mentioned not liking the signal/slot style implementations.
Event.h can be downloaded from the link bellow, it provides a .NET like Event implemented in C++: http://www.codeproject.com/Tips/1069718/Sharp-Tools-A-NET-like-Event-in-Cplusplus
An example of its usage:
#include "Event.h" // This lib consists of only one file, just copy it and include it in your code.
// an example of an application-level component which perform part of the business logic
class Cashier
{
public:
Sharp::Event<void> ShiftStarted; // an event that pass no argument when raised
Sharp::Event<int> MoneyPaid; // will be raised whenever the cashier receives money
void Start(); // called on startup, perform business logic and eventually calls ProcessPayment()
private:
// somewhere along the logic of this class
void ProcessPayment(int amount)
{
// after some processing
MoneyPaid(amount); // this how we raise the event
}
};
// Another application-level component
class Accountant
{
public:
void OnMoneyPaid(int& amount);
};
// The main class that decide on the events flow (who handles which events)
// it is also the owner of the application-level components
class Program
{
// the utility level components(if any)
//(commented)DataStore databaseConnection;
// the application-level components
Cashier cashier1;
Accountant accountant1;
//(commented)AttendanceManager attMan(&databaseConnection) // an example of injecting a utility object
public:
Program()
{
// connect the events of the application-level components to their handlers
cashier1.MoneyPaid += Sharp::EventHandler::Bind( &Accountant::OnMoneyPaid, &accountant1);
}
~Program()
{
// it is recommended to always connect the event handlers in the constructor
// and disconnect in the destructor
cashier1.MoneyPaid -= Sharp::EventHandler::Bind( &Accountant::OnMoneyPaid, &accountant1);
}
void Start()
{
// start business logic, maybe tell all cashiers to start their shift
cashier1.Start();
}
};
void main()
{
Program theProgram;
theProgram.Start();
// as you can see the Cashier and the Accountant know nothing about each other
// You can even remove the Accountant class without affecting the system
// You can add new components (ex: AttendanceManager) without affecting the system
// all you need to change is the part where you connect/disconnect the events
}
If boost is not an option, I implemented events in c++ here. The semantics is almost the same as in .NET . It's a compact implementation but uses quite advanced C++ features: a modern C++11 compiler is required.

How to get event notification in an ATL ActiveX control when a specified registry value changes for use as a windows mobile ActiveX control

I have an activex control that I display in a webbrowser control on a windows mobile device using a C# forms app.
The activex control is working. I want to add a feature to the activex control where an event is raised in the activex control when a registry value changes state.
I have implemented the same (but reversed) functionality on the forms side using RegistryState with the following code. With this code my .net forms app gets notified when a registry value changes. The forms code is below.
private void TestContainer_Load(object sender, EventArgs e)
{
RegistryKey rk = Registry.LocalMachine.OpenSubKey("MyKey");
state = new RegistryState("HKEY_LOCAL_MACHINE\\MyKey", "MessageToHostForm");
state.Changed += new ChangeEventHandler(state_Changed);
}
void state_Changed(object sender, ChangeEventArgs args)
{
RegistryKey rk = Registry.LocalMachine.OpenSubKey("MyKey");
if (rk == null)
{
// No value available in the created/opened subkey
}
else
{
string strOut = (string)rk.GetValue("MessageToHostForm");
button3.Text = strOut;
rk.Close();
}
}
I would like to create the same functionality in the ActiveX control where an event in the control gets fired when a registry value changes.
The problen is ... from what I can tell ... Registry.State is not available in an ATL C++ app :(
How can I make my ATL ActiveX control respond to changes in registry values. (I don't want to use polling)
Is this posible?
Thanks for at least reading this... Long I know...
To avoid polling, you can use CRegKey::NotifyChangeKeyValue, which is in turn built on top of RegNotifyChangeKeyValue. You can have an event signaled with a change under the key. So your worker thread might be waiting for this event [possibly among others] and once your wait is satisfied you would do whatever you do in your state_Changed above.
You also have sample code there: http://msdn.microsoft.com/en-us/library/ms724892%28VS.85%29.aspx
UPD. I realized mobile platform is in question - things are somewhat different there, but still you have an event based option:
MSDN: CeFindFirstRegChange http://msdn.microsoft.com/en-us/library/aa914423.aspx
Code Snippet: Windows CE: Monitor for Registry Changes

CDockingManager GetPaneList() causes assertion failure in wincore.cpp?

So I thought this would be pretty simple, but I forgot it's MFC. Instead of registering a notification listener for data model changes that would possibly require a GUI update on each individual control I figure why not register it once and then send a message to all the open dock panes and allow them to update their controls as needed on their own terms for efficiency.
My callback function for handling the notification from the server looks something like this:
void CMainFrame::ChangeCallback(uint32_t nNewVersion, const std::vector<uint32_t>& anChangedObjectTypes)
{
CObList panes;
GetDockingManager()->GetPaneList(panes); // assert failure
if (!panes.IsEmpty())
{
POSITION pos = panes.GetHeadPosition();
while (pos)
{
CDockablePane* pPane = dynamic_cast<CDockablePane*>(panes.GetNext(pos));
if (pPane)
pPane->PostMessage(DM_REFRESH, nNewVersion);
}
}
}
The error I am getting is an assertion failure on line 926 of wincore.cpp
CHandleMap* pMap = afxMapHWND();
ASSERT(pMap != NULL); // right here
There is a comment below this saying this can happen if you pass controls across threads however this is a single threaded MFC application and this is all being done from the main frame.
Does anyone know what else can cause this?
If there is another way to go about sending a message to all the open CDockablePane derived windows in MFC that works as well ...
Here's the obvious workaround that I didn't want to have to do but after hours of debugging and no response here I guess this is a viable answer:
I added std::vector<CDockPane*> m_dockList; to the members of CMainFrame
Now after each call to AddPane in various places that can create and open new dock panes I make a subsequent call to push_back and then I override CDockablePane::OnClose like so:
CMainFrame* pMainFrame = reinterpret_cast<CMainFrame*>(AfxGetMainWnd());
if (pMainFrame)
{
std::vector<CDockPane*>::const_iterator found(
std::find(pMainFrame->DockList()->begin(), pMainFrame->DockList()->end(), this));
if (found != pMainFrame->DockList()->end())
pMainFrame->DockList()->erase(found);
}
CDockablePane::OnClose();
Now this list will only contain pointers to open dock panes which allows me to handle the event notification in my callback and simply do a for loop and PostMessage to each.

Disable/Enable Ribbon Buttons for MFC Feature Pack

I am using the MFC Feature Pack and I have some buttons on a ribbon bar, instances of CMFCRibbonButton. The problem is that I would like to enable and disable some of them in certain conditions, but at runtime. How can I do this? because there is no specific method for this...I heard that a solution would be to attach/detach the event handlers at runtime, but I do not know how...
When you create the CMFCRibbonButton object you have to specify the associated command ID (see the documentation for the CMFCRibbonButton constructor here). Enabling and disabling of ribbon buttons is then done using the usual command update mechanism in MFC, using the CCmdUI class.
For example, if you have a ribbon button whose command ID is ID_MYCOMMAND and you want to handle this command in your application's view class, you should add these functions to the class:
// MyView.h
class CMyView : public CView {
// ...
private:
afx_msg void OnMyCommand();
afx_msg void OnUpdateMyCommand(CCmdUI* pCmdUI);
DECLARE_MESSAGE_MAP()
};
and implement them in the .cpp file:
// MyView.cpp
void CMyView::OnMyCommand() {
// add command handler code.
}
void CMyView::OnUpdateMyCommand(CCmdUI* pCmdUI) {
BOOL enable = ...; // set flag to enable or disable the command.
pCmdUI->Enable(enable);
}
You should also add ON_COMMAND and ON_UPDATE_COMMAND_UI entries to the message map for the CMyView class:
// MyView.cpp
BEGIN_MESSAGE_MAP(CMyView, CView)
ON_COMMAND(ID_MYCOMMAND, &CMyView::OnMyCommand)
ON_UPDATE_COMMAND_UI(ID_MYCOMMAND, &CMyView::OnUpdateMyCommand)
END_MESSAGE_MAP()
For more information on message maps in MFC, refer to TN006: Message Maps in MSDN.
I hope this helps!
ChrisN gave a pretty perfect answer. You can see an example of exactly how this is done by downloading the VS2008 Sample Pack from here, and opening the MSOffice2007Demo solution.
When running the sample, look at the "Thumbnails" checkbox in the View tab of the ribbon, it's disabled.
This is controlled by CMSOffice2007DemoView::OnUpdateViewThumb which calls pCmdUI->Enable(FALSE);. You can change this to call TRUE or FALSE at runtime to enable/disable the button respectively.