Skip to content

Commit 33dc858

Browse files
authored
Adding Windows Forms and WPF sample apps along with .NET preview (0.9.515) (#27)
Adding Windows Forms and WPF sample apps along with .NET preview (0.9.515) - Added WebView2WpfBrowser project - Added WebView2WindowsForms project - Both projects are configured to use the latest 0.9.515-prerelease nuget package with .NET preview PR by peiche
1 parent c8fed2e commit 33dc858

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+1710
-278
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
# VS Code
2+
.vs/
23
.vscode/

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
# WebView2 Samples
22

3-
This repository contain examples of how to use the [WebView 2 API](https://docs.microsoft.com/microsoft-edge/hosting/webview2). Please use [WebView2Samples issue tracker](https://github.com/MicrosoftEdge/WebView2Samples/issues) for sample issues and [WebViewFeedback issue tracker](https://github.com/MicrosoftEdge/WebViewFeedback/issues) for general WebView2 issues and feedback.
3+
This repository contain examples of how to use the [WebView 2 API](https://aka.ms/webview2). Please use [WebView2Samples issue tracker](https://github.com/MicrosoftEdge/WebView2Samples/issues) for sample issues and [WebViewFeedback issue tracker](https://aka.ms/webviewfeedback) for general WebView2 issues and feedback.
44

55
## 1. Getting Started Guide
66

77
This Sample is found within the [GettingStartedGuide](https://github.com/MicrosoftEdge/WebView2Samples/tree/master/GettingStartedGuide) directory.
88

99
Start with the [Microsoft Edge WebView2 getting-started guide](https://docs.microsoft.com/microsoft-edge/hosting/webview2/gettingstarted) to learn how to setup a WebView within a Win32 application.
1010

11-
## 2. WebView2 API Sample
11+
## 2. WebView2 API Samples
1212

13-
This Sample is found within the [WebView2APISample](WebView2APISample) directory.
13+
The Samples demonstrate all of the WebView2 SDK's features and their API use patterns. As we add more features to the WebView2 SDK, we will regularly update our sample applications. See [WebView2Samples.sln](./WebView2Samples.sln) that includes all three projects below:
1414

15-
The Microsoft Edge WebView2APISample is a comprehensive example of our SDK's capabilities. As we add more functionality, we will update the API Sample.
15+
* Win32 C/C++ Sample can be found within [WebView2APISample](./WebView2APISample) directory.
16+
* WPF Sample can be found within [WebView2WpfBrowser](./WebView2WpfBrowser) directory.
17+
* Windows Forms Sample can be found within [WebView2WindowsFormsBrowser](./WebView2WindowsFormsBrowser) directory.
1618

1719
## 3. WebView2 Browser Example
1820

1921
Clone the [WebView2Browser](https://github.com/MicrosoftEdge/WebView2Browser) by running `git clone https://github.com/MicrosoftEdge/WebView2Browser.git` to get started.
2022

2123
Follow the [WebView2Browser guide](https://github.com/MicrosoftEdge/WebView2Browser) to learn how to build an application that utilizes multiple WebViews.
22-
23-

WebView2APISample/App.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ int APIENTRY wWinMain(HINSTANCE hInstance,
161161
}
162162
}
163163

164-
new AppWindow(initialUri);
164+
new AppWindow(IDM_CREATION_MODE_WINDOWED, initialUri);
165165

166166
int retVal = RunMessagePump();
167167

@@ -210,18 +210,19 @@ static int RunMessagePump()
210210
}
211211

212212
// Make a new thread.
213-
void CreateNewThread()
213+
void CreateNewThread(UINT creationModeId)
214214
{
215215
DWORD threadId;
216-
HANDLE thread = CreateThread(nullptr, 0, ThreadProc, nullptr,
217-
STACK_SIZE_PARAM_IS_A_RESERVATION, &threadId);
216+
HANDLE thread = CreateThread(nullptr, 0, ThreadProc,
217+
reinterpret_cast<LPVOID>(creationModeId),
218+
STACK_SIZE_PARAM_IS_A_RESERVATION, &threadId);
218219
s_threads.insert(std::pair<DWORD, HANDLE>(threadId, thread));
219220
}
220221

221222
// This function is the starting point for new threads. It will open a new app window.
222223
static DWORD WINAPI ThreadProc(void* pvParam)
223224
{
224-
new AppWindow();
225+
new AppWindow(reinterpret_cast<UINT>(pvParam));
225226
return RunMessagePump();
226227
}
227228

WebView2APISample/App.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ extern HINSTANCE g_hInstance;
88
extern int g_nCmdShow;
99
extern bool g_autoTabHandle;
1010

11-
void CreateNewThread();
11+
void CreateNewThread(UINT creationModeId);

WebView2APISample/AppWindow.cpp

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,22 @@
2727
#include "ViewComponent.h"
2828

2929
using namespace Microsoft::WRL;
30-
DEFINE_ENUM_FLAG_OPERATORS(AppWindow::InitializeWebViewFlags);
3130
static constexpr size_t s_maxLoadString = 100;
3231
static constexpr UINT s_runAsyncWindowMessage = WM_APP;
3332

3433
static thread_local size_t s_appInstances = 0;
3534

3635
// Creates a new window which is a copy of the entire app, but on the same thread.
3736
AppWindow::AppWindow(
37+
UINT creationModeId,
3838
std::wstring initialUri,
3939
std::function<void()> webviewCreatedCallback,
4040
bool customWindowRect,
4141
RECT windowRect,
4242
bool shouldHaveToolbar)
43-
: m_initialUri(initialUri), m_onWebViewFirstInitialized(webviewCreatedCallback)
43+
: m_creationModeId(creationModeId),
44+
m_initialUri(initialUri),
45+
m_onWebViewFirstInitialized(webviewCreatedCallback)
4446
{
4547
++s_appInstances;
4648

@@ -67,11 +69,12 @@ AppWindow::AppWindow(
6769
m_toolbar.Initialize(m_mainWindow);
6870
}
6971

72+
UpdateCreationModeMenu();
7073
ShowWindow(m_mainWindow, g_nCmdShow);
7174
UpdateWindow(m_mainWindow);
7275

7376
RunAsync([this] {
74-
InitializeWebView(kDefaultOption);
77+
InitializeWebView();
7578
});
7679
}
7780

@@ -283,14 +286,14 @@ bool AppWindow::ExecuteAppCommands(WPARAM wParam, LPARAM lParam)
283286
case IDM_EXIT:
284287
CloseAppWindow();
285288
return true;
286-
case IDM_REINIT:
287-
InitializeWebView(kDefaultOption);
288-
return true;
289-
case IDM_REINIT_WINDOWLESS_DCOMP_VISUAL:
290-
InitializeWebView(kWindowlessDcompVisual);
289+
case IDM_CREATION_MODE_WINDOWED:
290+
case IDM_CREATION_MODE_VISUAL_DCOMP:
291+
case IDM_CREATION_MODE_VISUAL_WINCOMP:
292+
m_creationModeId = LOWORD(wParam);
293+
UpdateCreationModeMenu();
291294
return true;
292-
case IDM_REINIT_WINDOWLESS_WINCOMP_VISUAL:
293-
InitializeWebView(kWindowlessWincompVisual);
295+
case IDM_REINIT:
296+
InitializeWebView();
294297
return true;
295298
case IDM_TOGGLE_FULLSCREEN_ALLOWED:
296299
{
@@ -304,10 +307,10 @@ bool AppWindow::ExecuteAppCommands(WPARAM wParam, LPARAM lParam)
304307
return true;
305308
}
306309
case IDM_NEW_WINDOW:
307-
new AppWindow();
310+
new AppWindow(m_creationModeId);
308311
return true;
309312
case IDM_NEW_THREAD:
310-
CreateNewThread();
313+
CreateNewThread(m_creationModeId);
311314
return true;
312315
case IDM_SET_LANGUAGE:
313316
ChangeLanguage();
@@ -358,7 +361,7 @@ std::function<void()> AppWindow::GetAcceleratorKeyFunction(UINT key)
358361
switch (key)
359362
{
360363
case 'N':
361-
return [this] { new AppWindow(); };
364+
return [this] { new AppWindow(m_creationModeId); };
362365
case 'Q':
363366
return [this] { CloseAppWindow(); };
364367
case 'S':
@@ -369,7 +372,7 @@ std::function<void()> AppWindow::GetAcceleratorKeyFunction(UINT key)
369372
}
370373
};
371374
case 'T':
372-
return [this] { CreateNewThread(); };
375+
return [this] { CreateNewThread(m_creationModeId); };
373376
case 'W':
374377
return [this] { CloseWebView(); };
375378
}
@@ -379,19 +382,18 @@ std::function<void()> AppWindow::GetAcceleratorKeyFunction(UINT key)
379382

380383
//! [CreateCoreWebView2Controller]
381384
// Create or recreate the WebView and its environment.
382-
void AppWindow::InitializeWebView(InitializeWebViewFlags webviewInitFlags)
385+
void AppWindow::InitializeWebView()
383386
{
384-
m_lastUsedInitFlags = webviewInitFlags;
385387
// To ensure browser switches get applied correctly, we need to close
386388
// the existing WebView. This will result in a new browser process
387389
// getting created which will apply the browser switches.
388390
CloseWebView();
389-
390-
LPCWSTR subFolder = nullptr;
391391
m_dcompDevice = nullptr;
392392
m_wincompHelper = nullptr;
393-
LPCWSTR additionalBrowserSwitches = nullptr;
394-
if (webviewInitFlags & kWindowlessDcompVisual)
393+
394+
LPCWSTR subFolder = nullptr;
395+
396+
if (m_creationModeId == IDM_CREATION_MODE_VISUAL_DCOMP)
395397
{
396398
HRESULT hr = DCompositionCreateDevice2(nullptr, IID_PPV_ARGS(&m_dcompDevice));
397399
if (!SUCCEEDED(hr))
@@ -405,7 +407,7 @@ void AppWindow::InitializeWebView(InitializeWebViewFlags webviewInitFlags)
405407
return;
406408
}
407409
}
408-
else if (webviewInitFlags & kWindowlessWincompVisual)
410+
else if (m_creationModeId == IDM_CREATION_MODE_VISUAL_WINCOMP)
409411
{
410412
HRESULT hr = CreateWinCompCompositor();
411413
if (!SUCCEEDED(hr))
@@ -421,7 +423,6 @@ void AppWindow::InitializeWebView(InitializeWebViewFlags webviewInitFlags)
421423
}
422424
//! [CreateCoreWebView2EnvironmentWithOptions]
423425
auto options = Microsoft::WRL::Make<CoreWebView2EnvironmentOptions>();
424-
CHECK_FAILURE(options->put_AdditionalBrowserArguments(additionalBrowserSwitches));
425426
if(!m_language.empty())
426427
CHECK_FAILURE(options->put_Language(m_language.c_str()));
427428
HRESULT hr = CreateCoreWebView2EnvironmentWithOptions(
@@ -448,7 +449,7 @@ void AppWindow::InitializeWebView(InitializeWebViewFlags webviewInitFlags)
448449
}
449450
}
450451

451-
// This is the callback passed to CreateWebViewEnvironmnetWithDetails.
452+
// This is the callback passed to CreateWebViewEnvironmentWithOptions.
452453
// Here we simply create the WebView.
453454
HRESULT AppWindow::OnCreateEnvironmentCompleted(
454455
HRESULT result, ICoreWebView2Environment* environment)
@@ -539,7 +540,7 @@ void AppWindow::ReinitializeWebView()
539540
// Save the settings component from being deleted when the WebView is closed, so we can
540541
// copy its properties to the next settings component.
541542
m_oldSettingsComponent = MoveComponent<SettingsComponent>();
542-
InitializeWebView(m_lastUsedInitFlags);
543+
InitializeWebView();
543544
}
544545

545546
void AppWindow::ReinitializeWebViewWithNewBrowser()
@@ -559,7 +560,7 @@ void AppWindow::ReinitializeWebViewWithNewBrowser()
559560
// Make sure the browser process inside webview is closed
560561
ProcessComponent::EnsureProcessIsClosed(webviewProcessId, 2000);
561562

562-
InitializeWebView(m_lastUsedInitFlags);
563+
InitializeWebView();
563564
}
564565

565566
void AppWindow::RestartApp()
@@ -637,8 +638,7 @@ void AppWindow::RegisterEventHandlers()
637638
CHECK_FAILURE(args->GetDeferral(&deferral));
638639
AppWindow* newAppWindow;
639640

640-
newAppWindow = new AppWindow(L"");
641-
641+
newAppWindow = new AppWindow(m_creationModeId, L"");
642642
newAppWindow->m_isPopupWindow = true;
643643
newAppWindow->m_onWebViewFirstInitialized = [args, deferral, newAppWindow]() {
644644
CHECK_FAILURE(args->put_NewWindow(newAppWindow->m_webView.get()));
@@ -742,7 +742,7 @@ void AppWindow::CloseWebView(bool cleanupUserDataFolder)
742742
// developers specify userDataFolder during WebView environment
743743
// creation, they would need to pass in that explicit value here.
744744
// For more information about userDataFolder:
745-
// https://docs.microsoft.com/microsoft-edge/hosting/webview2/reference/webview2.idl#createwebview2environmentwithdetails
745+
// https://docs.microsoft.com/microsoft-edge/webview2/reference/win32/0-9-488/webview2-idl#createwebview2environmentwithoptions
746746
WCHAR userDataFolder[MAX_PATH] = L"";
747747
// Obtain the absolute path for relative paths that include "./" or "../"
748748
_wfullpath(
@@ -752,7 +752,7 @@ void AppWindow::CloseWebView(bool cleanupUserDataFolder)
752752
std::wstring message = L"Are you sure you want to clean up the user data folder at\n";
753753
message += userDataFolderPath;
754754
message += L"\n?\nWarning: This action is not reversible.\n\n";
755-
message += L"Click No if there are other open WebView instnaces.\n";
755+
message += L"Click No if there are other open WebView instances.\n";
756756

757757
if (MessageBox(m_mainWindow, message.c_str(), L"Cleanup User Data Folder", MB_YESNO) ==
758758
IDYES)
@@ -959,3 +959,15 @@ HRESULT AppWindow::CreateWinCompCompositor()
959959
}
960960
return hr;
961961
}
962+
963+
void AppWindow::UpdateCreationModeMenu()
964+
{
965+
HMENU hMenu = GetMenu(m_mainWindow);
966+
CheckMenuRadioItem(
967+
hMenu,
968+
IDM_CREATION_MODE_WINDOWED,
969+
IDM_CREATION_MODE_VISUAL_WINCOMP,
970+
m_creationModeId,
971+
MF_BYCOMMAND);
972+
}
973+

WebView2APISample/AppWindow.h

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class AppWindow
2424
{
2525
public:
2626
AppWindow(
27+
UINT creationModeId,
2728
std::wstring initialUri = L"https://www.bing.com/",
2829
std::function<void()> webviewCreatedCallback = nullptr,
2930
bool customWindowRect = false,
@@ -57,14 +58,9 @@ class AppWindow
5758

5859
void RunAsync(std::function<void(void)> callback);
5960

60-
enum InitializeWebViewFlags
61-
{
62-
kDefaultOption = 0,
63-
kWindowlessDcompVisual = 1 << 0,
64-
kWindowlessWincompVisual = 1 << 1,
65-
};
6661
private:
6762
static PCWSTR GetWindowClass();
63+
6864
static INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
6965

7066
static LRESULT CALLBACK
@@ -76,7 +72,7 @@ class AppWindow
7672
bool ExecuteAppCommands(WPARAM wParam, LPARAM lParam);
7773

7874
void ResizeEverything();
79-
void InitializeWebView(InitializeWebViewFlags webviewInitFlags);
75+
void InitializeWebView();
8076
HRESULT OnCreateEnvironmentCompleted(HRESULT result, ICoreWebView2Environment* environment);
8177
HRESULT OnCreateCoreWebView2ControllerCompleted(HRESULT result, ICoreWebView2Controller* controller);
8278
HRESULT DeleteFileRecursive(std::wstring path);
@@ -86,6 +82,7 @@ class AppWindow
8682
void CloseWebView(bool cleanupUserDataFolder = false);
8783
void CloseAppWindow();
8884
void ChangeLanguage();
85+
void UpdateCreationModeMenu();
8986
std::wstring GetLocalPath(std::wstring path);
9087

9188
void DeleteAllComponents();
@@ -96,6 +93,7 @@ class AppWindow
9693
HWND m_mainWindow = nullptr;
9794
Toolbar m_toolbar;
9895
std::function<void()> m_onWebViewFirstInitialized;
96+
DWORD m_creationModeId = 0;
9997

10098
// The following is state that belongs with the webview, and should
10199
// be reinitialized along with it. Everything here is undefined when
@@ -106,10 +104,6 @@ class AppWindow
106104

107105
// All components are deleted when the WebView is closed.
108106
std::vector<std::unique_ptr<ComponentBase>> m_components;
109-
110-
// This state is preserved between WebViews so we can recreate
111-
// a new WebView based on the settings of the old one.
112-
InitializeWebViewFlags m_lastUsedInitFlags;
113107
std::unique_ptr<SettingsComponent> m_oldSettingsComponent;
114108

115109
std::wstring m_language;

WebView2APISample/HostObjectSample.idl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@ library HostObjectSampleLibrary
1515
[propget] HRESULT Property([out, retval] BSTR* stringResult);
1616
[propput] HRESULT Property([in] BSTR stringValue);
1717

18+
[propget] HRESULT IndexedProperty(INT index, [out, retval] BSTR * stringResult);
19+
[propput] HRESULT IndexedProperty(INT index, [in] BSTR stringValue);
20+
1821
// Demonstrate native calling back into JavaScript.
1922
HRESULT CallCallbackAsynchronously([in] IDispatch* callbackParameter);
23+
2024
};
2125
//! [AddHostObjectInterface]
2226

WebView2APISample/HostObjectSampleImpl.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,20 @@ STDMETHODIMP HostObjectSample::put_Property(BSTR stringValue)
3636
return S_OK;
3737
}
3838

39+
STDMETHODIMP HostObjectSample::get_IndexedProperty(INT index, BSTR* stringResult)
40+
{
41+
std::wstring result(L"[");
42+
result = result + std::to_wstring(index) + L"] is " + m_propertyValues[index] + L".";
43+
*stringResult = SysAllocString(result.c_str());
44+
return S_OK;
45+
}
46+
47+
STDMETHODIMP HostObjectSample::put_IndexedProperty(INT index, BSTR stringValue)
48+
{
49+
m_propertyValues[index] = stringValue;
50+
return S_OK;
51+
}
52+
3953
STDMETHODIMP HostObjectSample::CallCallbackAsynchronously(IDispatch* callbackParameter)
4054
{
4155
wil::com_ptr<IDispatch> callbackParameterForCapture = callbackParameter;

WebView2APISample/HostObjectSampleImpl.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "stdafx.h"
88

99
#include <functional>
10+
#include <map>
1011
#include <string>
1112
#include <wrl\client.h>
1213

@@ -29,6 +30,8 @@ class HostObjectSample : public Microsoft::WRL::RuntimeClass<
2930
// Demonstrate getting and setting a property.
3031
STDMETHODIMP get_Property(BSTR* stringResult) override;
3132
STDMETHODIMP put_Property(BSTR stringValue) override;
33+
STDMETHODIMP get_IndexedProperty(INT index, BSTR* stringResult) override;
34+
STDMETHODIMP put_IndexedProperty(INT index, BSTR stringValue) override;
3235

3336
// Demonstrate native calling back into JavaScript.
3437
STDMETHODIMP CallCallbackAsynchronously(IDispatch* callbackParameter) override;
@@ -47,6 +50,7 @@ class HostObjectSample : public Microsoft::WRL::RuntimeClass<
4750

4851
private:
4952
std::wstring m_propertyValue;
53+
std::map<INT, std::wstring> m_propertyValues;
5054
wil::com_ptr<IDispatch> m_callback;
5155
RunCallbackAsync m_runCallbackAsync;
5256
wil::com_ptr<ITypeLib> m_typeLib;

0 commit comments

Comments
 (0)