Detecting CTRL+Wheel with CHtmlView - mfc

I know how to set the zoom factor for a CHtmlView:
HRESULT CChristianLifeMinistryHtmlView::SetZoomFactor(long iZoom, bool bRefreshBrowser /*true*/)
{
HRESULT hr = S_OK;
VARIANT vZoom;
m_lZoomFactor = iZoom;
if (bRefreshBrowser)
{
vZoom.vt = VT_I4;
vZoom.lVal = iZoom;
hr = ExecWB(OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, &vZoom, nullptr);
}
return hr;
}
HRESULT CChristianLifeMinistryHtmlView::ExecWB(OLECMDID cmdID, OLECMDEXECOPT cmdexecopt, VARIANT *pvaIn, VARIANT *pvaOut)
{
HRESULT hr;
ASSERT(m_pBrowserApp != NULL);
hr = m_pBrowserApp->ExecWB(cmdID, cmdexecopt, pvaIn, pvaOut);
return hr;
}
I have just introduced a CStatusBar into the main editor that encompasses this view and one of my users has stated that they use CTRL + Wheel to change the zoom factor.
I have my menu structure with associated hotkeys that the user can use to change the zoom, thus my status bar pane is updated to the right value they selected.
But when they use the CTRL + Wheel to change the zoom my application is not detecting this. So they zoom in or out to a scale and my status bar pane is staying at the original zoom factor.
With MFC and the CHtmlView web browser control how to I detect when they have changed the zoom using CTRL + Wheel so I can update my status pane?

If this is CHtmlView with Doc/View structure, use PreTranslateMessage to catch messages.
Documentation for WM_MOUSEWHEEL suggests several macros for finding the state of virtual keys and wheel movement:
BOOL CMyHtmlView::PreTranslateMessage(MSG* pmsg)
{
if(pmsg->message == WM_MOUSEWHEEL)
{
int fwKeys = GET_KEYSTATE_WPARAM(pmsg->wParam);
int zDelta = GET_WHEEL_DELTA_WPARAM(pmsg->wParam);
if (fwKeys & MK_CONTROL)
{
//mousewheel + control key is down
TRACE("%d %d\n", zDelta, zDelta / WHEEL_DELTA);
//update statusbar, or return TRUE to handle this manually
}
}
return CHtmlView::PreTranslateMessage(pmsg);
}
CHtmlView also has its own CHtmlView::ExecWB method to set and get the zoom value etc.
CHtmlView::OnUpdateUI should also send notification for the change.
But the browser may not send a signal at the right time. Just make a timer to wait 1 second after detecting CTRL+WHEEL. Example:
BEGIN_MESSAGE_MAP(CMyHtmlView, CHtmlView)
ON_WM_TIMER()
END_MESSAGE_MAP()
const int ID_TIMER_ZOOM = 1;
BOOL CMyHtmlView::PreTranslateMessage(MSG* pmsg)
{
if(pmsg->message == WM_MOUSEWHEEL)
if (GET_KEYSTATE_WPARAM(pmsg->wParam) & MK_CONTROL)
SetTimer(ID_TIMER_ZOOM, 1000, NULL); //start timer for detecting zoom
return CHtmlView::PreTranslateMessage(pmsg);
}
void CMyHtmlView::OnTimer(UINT_PTR timer_id)
{
if(timer_id == ID_TIMER_ZOOM)
{
//get the zoom value
VARIANT vZoom;
vZoom.vt = VT_I4;
vZoom.lVal = 0;
ExecWB(OLECMDID_OPTICAL_ZOOM, OLECMDEXECOPT_DONTPROMPTUSER, nullptr, &vZoom);
TRACE("zoom %d\n", vZoom.lVal);
//kill the timer
KillTimer(timer_id);
}
}

Related

WIN32 - Create a WebView2 synchronously does not work - I don't get a white rectangle for my integrated web browser

In brief, I explain you my problem. In my application, I've a button to load the browser. When I click on the button, it opens and creates well the webBrowser (white rectangle).
BUT when I try to create the web Browser when I open a window (who contains different components : buttons, edittext,...) of my application through the Event WM_SHOWWINDOW, I don't see my web browser EXCEPTED IF I put in comment all the loop "while" in the constructor EdgeBrowser.
Why ? Can you give me a solution please ? It would be friendly.
I created a class EdgeBrowser with a constructor who receives the handle of my WIN32 component (HWND component). In my constructor, I do something like that :
EdgeBrowser::EdgeBrowser(HWND hwnd)
{
_hwnd = hwnd;
EdgeBrowser::_beginAsyncOperation = true;
this->CreateWebView();
while (EdgeBrowser::_beginAsyncOperation)
{
Sleep(10);
MSG msg;
for (int nmsg = 0; nmsg < 50; ++nmsg)
{
int rc = PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
if (rc == 0)
{
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
And in a part of my "CreateWebView" function with the different listeners :
void EdgeBrowser::CreateWebView
{
// other code
HRESULT hr = CreateCoreWebView2EnvironmentWithOptions(nullptr, userDataDir.c_str(),nullptr,Microsoft::WRL::Callback<ICoreWebView2CreateCoreWebView2EnvironmentCompletedHandler>(this,&EdgeBrowser::OnCreateCoreWebView2EnvironmentCompletedHandler).Get());
}
HRESULT EdgeBrowser::OnEnvironmentReadyCompletedHandler(HRESULT result, ICoreWebView2Environment* env)
{
HRESULT createBrowserControlsResult = m_uiEnv->CreateCoreWebView2Controller(this->_thisHandler, Microsoft::WRL::Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>(this,&EdgeBrowser::OnCreateCoreWebView2ControllerCompletedHandler).Get());
HRESULT createBrowserOptionsResult = m_uiEnv->CreateCoreWebView2Controller(this->_thisHandler,Microsoft::WRL::Callback<ICoreWebView2CreateCoreWebView2ControllerCompletedHandler>( this,&EdgeBrowser::OnCreateCoreWebView2ControllerOptionsCompletedHandler).Get());
}
HRESULT EdgeBrowser::OnCreateCoreWebView2ControllerOptionsCompletedHandler(HRESULT result, ICoreWebView2Controller* host)
{
m_optionsController = host;
HRESULT getOptWebViewResult = m_optionsController->get_CoreWebView2(&m_optionsWebView);
//other code
EdgeBrowser::_beginAsyncOperation = false;
return S_OK;
}
The WebView2 control requires a message loop to run on the UI thread on which it is created (see this page for more info on WebView2 threading). You can see the WebView2APISample sample app as a sample C++ Win32 HWND based application.

How to wait for IWebBrowser2::ExecWB() to finish when printing from a WebBrowser control?

I'm using the WebBrowser control in my CDialog-based MFC window. The window allows printing, using code similar to this:
CComPtr<IWebBrowser2> pWebBrowser = this->GetIWebBrowser2();
if(pWebBrowser)
{
HRESULT hr;
COleVariant varNull;
if(SUCCEEDED(hr = pWebBrowser->ExecWB(
bDoPreview ? OLECMDID_PRINTPREVIEW : OLECMDID_PRINT,
OLECMDEXECOPT_PROMPTUSER, varNull, varNull)))
{
//All good
bRes = TRUE;
}
}
IWebBrowser2* GetIWebBrowser2()
{
IWebBrowser2* pBrowser = NULL;
LPUNKNOWN unknown = m_browser.GetControlUnknown();
if(unknown)
{
unknown->QueryInterface(IID_IWebBrowser2,(void **)&pBrowser);
if(unknown)
{
unknown->Release();
}
}
return pBrowser;
}
It works, except that if the document is large enough, pWebBrowser->ExecWB() seems to return right away and all the printing is done asynchronously. So in that case if the user closes my window (that houses this WebBrowser control) printing is aborted midway.
Thus my question, how do I wait for the printing to finish before I can allow closing of the host window?

Tracking tooltip in CScrollView?

In a standard C++/MFC MDI doc/view project, I want to implement a tracking tooltip in the view (the tabbed view windows which generally occupy most of the main frame window). So, in class MyAppView, I have a member CToolTipCtrl tooltip. Function MyAppView::OnInitialUpdate() contains the initialization
BOOL ok0 = tooltip.Create(this, TTS_ALWAYSTIP);
CRect clientRect; GetClientRect(&clientRect);
BOOL ok2 = tooltip.AddTool(this, LPSTR_TEXTCALLBACK, &clientRect, 1234/*tool ID*/);
tooltip.Activate(TRUE);
to make the entire client area of the view be the "tool". The message map contains an entry
ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnNeedToolTipText)
and the function OnNeedToolTipText is defined as
BOOL MyAppView::OnNeedToolTipText(UINT id, NMHDR *pNMHDR, LRESULT *pResult)
{
UNREFERENCED_PARAMETER(id);
NMTTDISPINFO *pTTT = (NMTTDISPINFO *)pNMHDR;
UINT_PTR nID = pNMHDR->idFrom;
BOOL bRet = FALSE;
if(nID == 1234)
{
// Come here when text is needed for tracking tooltip
}
if(pTTT->uFlags & TTF_IDISHWND)
{
// idFrom is actually the HWND of the tool
nID = ::GetDlgCtrlID((HWND)nID);
if(nID)
{
_stprintf_s(pTTT->szText, sizeof(pTTT->szText) / sizeof(TCHAR),
_T("Control ID = %d"), nID);
pTTT->hinst = AfxGetResourceHandle();
bRet = TRUE;
}
}
*pResult = 0;
return bRet;
}
What happens is that only placing the mouse on the menu items (File, Edit, View, Window, Help) causes the code to enter OnNeedToolTipText, with an ID of 0-5. Moving the mouse into the client area (the view) does nothing.
How can I get the tooltip to appear in the client area of the view only?
Visual Studio 2017; C++; 64-bit Windows 7
In order to solve the problem you need to do the following:
BOOL CMyAppView::PreTranslateMessage(MSG* pMsg)
{
switch (pMsg->message)
{
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
case WM_LBUTTONDOWN:
case WM_RBUTTONDOWN:
case WM_MBUTTONDOWN:
case WM_LBUTTONUP:
case WM_RBUTTONUP:
case WM_MBUTTONUP:
case WM_MOUSEMOVE:
if (m_pToolTip->GetSafeHwnd () != NULL)
{
m_pToolTip->RelayEvent(pMsg);
}
break;
}
return CScrollView::PreTranslateMessage(pMsg);
}
If you want a tracking tooltip in a view, these are the steps to follow:
Create tooltip and add the tool.
void CToolTipDemoView::OnInitialUpdate()
{
// ...
m_toolTip.Create(this, TTS_ALWAYSTIP | TTS_NOANIMATE);
m_toolTip.AddTool(this, _T("Doesn't matter"));
}
Handle WM_MOUSEMOVE message. First, call _TrackMouseEvent in order to further receive WM_MOUSELEAVE and activate the tooltip. Second, update the tooltip text, and show it at mouse pointer coordinates.
void CToolTipDemoView::OnMouseMove(UINT nFlags, CPoint point)
{
if (!m_bTrackingMouseLeave)
{
TRACKMOUSEEVENT tme = { 0 };
tme.cbSize = sizeof(TRACKMOUSEEVENT);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = m_hWnd;
::_TrackMouseEvent(&tme);
m_toolTip.Activate(TRUE);
m_bTrackingMouseLeave = TRUE;
}
if (m_pointLastMousePos != point)
{
CString strText;
strText.Format(_T("x = %d y = %d"), point.x, point.y);
m_toolTip.UpdateTipText(strText, this);
m_toolTip.Popup();
m_pointLastMousePos = point;
}
CScrollView::OnMouseMove(nFlags, point);
}
Handle WM_MOUSELEAVE and deactivate the tooltip.
void CCToolTipDemoView::OnMouseLeave()
{
m_bTrackingMouseLeave = FALSE;
// mouse pointer leaves the window so deactivate the tooltip
m_toolTip.Activate(FALSE);
CScrollView::OnMouseLeave();
}
Notes:
there is no more necessary to handle TTN_NEEDTEXT.
also, there is no more necessary to override PreTranslateMessage
So I went back to see what I could be missing. I wrote this stuff over 10 years ago. I had also overridden a CWnd member
virtual INT_PTR OnToolHitTest( CPoint point, TOOLINFO* pTI ) const;
With:
INT_PTR HERichView::OnToolHitTest( CPoint point, TOOLINFO* pTI ) const
{
pTI->hwnd = m_hWnd;
pTI->uId = point.x + ( point.y << 16 );
CRect rect;
GetClientRect( rect );
pTI->rect= rect;
pTI->lpszText= LPSTR_TEXTCALLBACK;
return pTI->uId;
}
And I checked, it won't work without this. So your:
ON_NOTIFY_EX( TTN_NEEDTEXT, 0, OnToolTip )
Should get called if you add the above. And only EnableToolTips( ); Should be needed.
I have not succeeded in getting the tracking tooltip to work within MFC. The closest I have come is
In message map: ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnNeedToolTipText)
In OnInitialUpdate: BOOL ok1 = EnableTrackingToolTips(TRUE);
In override of virtual function OnToolHitTest:
pTI->hwnd = m_hWnd;
pTI->uId = (UINT_PTR)m_hWnd;
pTI->uFlags = TTF_IDISHWND | TTF_ALWAYSTIP | TTF_TRACK | TTF_NOTBUTTON | TTF_ABSOLUTE | TTF_SUBCLASS;
pTI->lpszText = LPSTR_TEXTCALLBACK;
return pTI->uId;
In OnNeedToolTipText:
NMTTDISPINFO *pTTT = (NMTTDISPINFO *)pNMHDR;
UINT_PTR nID = pNMHDR->idFrom;
BOOL bRet = FALSE;
if(pTTT->uFlags & TTF_IDISHWND)
{
// idFrom is actually the HWND of the tool
nID = ::GetDlgCtrlID((HWND)nID);
if(nID)
{
CURSORINFO ci; ci.cbSize = sizeof(CURSORINFO); // get something interesting to display
GetCursorInfo(&ci);
_stprintf_s(pTTT->szText, sizeof(pTTT->szText) / sizeof(TCHAR),
_T("Control ID = %lld at (%d, %d)"), nID, ci.ptScreenPos.x, ci.ptScreenPos.y);
pTTT->hinst = AfxGetResourceHandle();
bRet = TRUE;
}
}
*pResult = 0;
return bRet;
This produces the following peculiar behavior. When I start the app and move the mouse cursor into the client area of the CScrollView, a tooltip appears right next to the cursor.
If I move the mouse carefully (smoothly) the tooltip tracks properly. After a while, though, it disappears, and no further mouse motions, including leaving the CScrollView window and returning, make it re-appear.
I think what is happening is that when the mouse cursor moves over the tooltip window, the tooltip is turned off, permanently. This disappearance does not seem to be time-related (e g, due to auto-pop); if the mouse is left untouched, the tooltip remains indefinitely.

Sending a click event to a Paint application

I am writing an application that automatically draws something on a canvas, depending on the user's preferences.
For starters, how can I send a click event to the MS Paint application?
First, you need to find the Paint application:
static HWND findMSPaintDrawWindow(void)
{
HWND target;
target = FindWindow(TARGET_PAINT_WINDOW, NULL);
if (NULL != target)
{
target = FindWindowEx(target, NULL, TARGET_PAINT_INPUT_SUBWINDOW, NULL);
if (NULL != target)
{
target = FindWindowEx(target, NULL, NULL, NULL);
}
}
if (/*ENABLE_DEBUG_CONSOLE*/ 0)
{
char name[256];
GetClassName(target, name, 255);
printf("Detected ms paint Draw area with name [%s]\n", name);
}
return target;
}
Second, you interact with it:
static void sendMouseButton(int buttonState)
{
HWND target;
UINT buttonMode = WM_LBUTTONUP;
target = findMSPaintDrawWindow();
if (target)
{
if (buttonState)
{
buttonMode = WM_LBUTTONDOWN;
g_MouseDown = 0;
}
printf("INFO: Mouse [%d] msg %d\n", buttonState, buttonMode);
PostMessage(target, buttonMode, MK_LBUTTON, X, Y));
}
}
You can always browse the MSDN documentation for more details.
You have to search google for how to send windows message to different application. The mouse click is composed of 2 different messages: WM_LBUTTONDOWN and WM_LBUTTONUP.
http://stefanstools.sourceforge.net/SendMessage.html
http://www.codeproject.com/Articles/137/Sending-a-message-to-the-Main-Frame-Window-of-Anot

How to implement code for multiple buttons using c++ in Silverlight for Windows Embedded

I have referred the following link:
Silverlight for Windows Embedded
By referring this link i created a demo application which consist of two buttons created using Microsoft expression blend 2 tools. And then written a code referring the above site. Now my button names are "Browser Button" and "Media Button". On click of any one of the button i should able to launch the respective application. I was able to do for "Browser Button" but not for "Media Button" and if i do for "Media Button" then i am not able to do for "Browser Button".. I mean to say that how should i create event handler for both the buttons.
This is the code in c++ which i should modify
class BtnEventHandler
{
public:
HRESULT OnClick(IXRDependencyObject* source,XRMouseButtonEventArgs* args)
{
RETAILMSG(1,(L"Browser event"));
Execute(L"\\Windows\\iesample.exe",L"");
return S_OK;
}
};
// entry point for the application.
INT WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPWSTR lpCmdLine,int nCmdShow)
{
PrintMessage();
int exitCode = -1;
HRESULT hr = S_OK;
if (!XamlRuntimeInitialize())
return -1;
HRESULT retcode;
IXRApplicationPtr app;
if (FAILED(retcode=GetXRApplicationInstance(&app)))
return -1;
if (FAILED(retcode=app->AddResourceModule(hInstance)))
return -1;
XRWindowCreateParams wp;
ZeroMemory(&wp, sizeof(XRWindowCreateParams));
wp.Style = WS_OVERLAPPED;
wp.pTitle = L"Bounce Test";
wp.Left = 0;
wp.Top = 0;
XRXamlSource xamlsrc;
xamlsrc.SetResource(hInstance,TEXT("XAML"),MAKEINTRESOURCE(IDR_XAML1));
IXRVisualHostPtr vhost;
if (FAILED(retcode=app->CreateHostFromXaml(&xamlsrc, &wp, &vhost)))
return -1;
IXRFrameworkElementPtr root;
if (FAILED(retcode=vhost->GetRootElement(&root)))
return -1;
IXRButtonBasePtr btn;
if (FAILED(retcode=root->FindName(TEXT("BrowserButton"), &btn)))
return -1;
IXRDelegate<XRMouseButtonEventArgs>* clickdelegate;
BtnEventHandler handler;
if(FAILED(retcode=CreateDelegate
(&handler,&BtnEventHandler::OnClick,&clickdelegate)))
return -1;
if (FAILED(retcode=btn->AddClickEventHandler(clickdelegate)))
return -1;
UINT exitcode;
if (FAILED(retcode=vhost->StartDialog(&exitcode)))
return -1;
return exitCode;
}
I have to add event handler for both the button so that on emulator whenever i click on any one of the button i should be able to launch the respective applications.
Thanks in advance
You can create two seperate functions to be the handlers for each button.
If you want the same handler to identify which button was pressed and act accordingly you can read the following MSDN article that demonstrates that.
I have not tried this, but you can also use IXRDependencyObject::GetName of the source object to know which button was pressed.
Your handler would look like:
HRESULT OnClick(IXRDependencyObject* source,XRMouseButtonEventArgs* args)
{
BSTR pName[50];
source->GetName(pName);
if (_tcscmp(L"BrowserEvent", LPCWSTR(pName)) == 0)
{
RETAILMSG(1,(L"Browser event"));
Execute(L"\\Windows\\iesample.exe",L"");
}
else if (_tcscmp(L"BrowserEvent", LPCWSTR(pName)) == 0)
{
/* Handle another button or element */
}
return S_OK;
}