Moved into src folder

This commit is contained in:
Florian Rappl
2023-04-01 23:44:25 +02:00
parent 3470a70572
commit 2367035acd
357 changed files with 5 additions and 16 deletions

View File

@@ -0,0 +1,42 @@
<template class="task-template">
<div id="about-modal" class="about modal">
<div class="about-wrapper">
<header class="about-header">
<img class="about-logo" src="assets/img/about.png" srcset="assets/img/about.png 1x, assets/img/about@2x.png 2x" alt="Electron.NET API Demos">
</header>
<main class="about-sections">
<section class="about-section play-along">
<h2>Play Along</h2>
<p>Use the demo snippets in an Electron app of your own. The <a href="https://github.com/electron/electron-quick-start">Electron Quick Start<span class="u-visible-to-screen-reader">(opens in new window)</span></a> app is a bare-bones setup that pairs with these demos. Follow the instructions here to get it going.
To activate Electron.NET include the <a href="https://www.nuget.org/packages/ElectronNET.API/" target="_blank">ElectronNET.API NuGet package</a> in your ASP.NET Core app.
<p>
<code class="language-bash">PM> Install-Package ElectronNET.API</code>
</p>
Then include the UseElectron WebHostBuilder-Extension into the Program.cs-file of your ASP.NET Core project.
<pre>
<code class="csharp">public static IWebHost BuildWebHost(string[] args)
{
return WebHost.CreateDefaultBuilder(args)
.UseElectron(args)
.UseStartup<Startup>()
.Build();
}</code>
</pre>
</section>
<section class="about-section about-code">
<h2>About the Code</h2>
<p>The source code of this app has been organized with ease of navigation in mind.
You will find for each API Category the source code into the Controllers.
This Demo-App based on the <a href="https://github.com/electron/electron-api-demos" target="_blank">Electron API Demos</a>.</p>
</section>
<footer class="about-section footer">
<div class="rainbow-button-wrapper">
<button id="get-started" class="about-button modal-hide">Get Started</button>
</div>
</footer>
</main>
</div>
</div>
</template>

View File

@@ -0,0 +1,187 @@
<template class="task-template">
<section id="app-sys-information-section" class="section js-section u-category-system">
<header class="section-header">
<div class="section-wrapper">
<h1>
<svg class="section-icon"><use xlink:href="assets/img/icons.svg#icon-system"></use></svg>
Get app and system information
</h1>
<h3>With Electron.NET you can gather information about the user's system, app or screen.</h3>
<p>You find the sample source code in <code>Controllers\AppSysInformationController.cs</code>.</p>
</div>
</header>
<div class="demo">
<div class="demo-wrapper">
<button id="app-info-demo-toggle" class="js-container-target demo-toggle-button">
Get app information
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="app-info">View Demo</button>
<span class="demo-response" id="got-app-info"></span>
</div>
<p>The main process <code>Electron.App</code> can be used to get the path at which your app is located on the user's computer.</p>
<p>In this example, to get that information from the renderer process, we use the <code>ipcRenderer</code> to send a message to the main process requesting the app's path.</p>
<h5>Renderer Process (JavaScript)</h5>
<pre><code class="javascript">const { ipcRenderer } = require("electron");
document.getElementById("app-info").addEventListener("click", () => {
ipcRenderer.send("app-info");
});
ipcRenderer.on("got-app-path", (event, path) => {
const message = `This app is located at: ${path}`;
document.getElementById('got-app-info').innerHTML = message
});</code></pre>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">Electron.IpcMain.On("app-info", async (args) =>
{
string appPath = await Electron.App.GetAppPathAsync();
var mainWindow = Electron.WindowManager.BrowserWindows.First();
Electron.IpcMain.Send(mainWindow, "got-app-path", appPath);
});</code></pre>
</div>
</div>
</div>
<div class="demo">
<div class="demo-wrapper">
<button id="app-version-demo-toggle" class="js-container-target demo-toggle-button">
Get version information
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Both</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="version-info">View Demo</button>
<span class="demo-response" id="got-version-info"></span>
</div>
<p>The <code>process</code> module is built into Node.js (therefore you can use this in the renderer processes) and in Electron.NET apps this object has a few more useful properties on it.</p>
<p>The example below gets the version of Electron in use by the app.</p>
<p>See the <a href="http://electron.atom.io/docs/api/process">process documentation<span class="u-visible-to-screen-reader">(opens in new window)</span></a> for more.</p>
<h5>Renderer Process (JavaScript)</h5>
<pre><code class="javascript">document.getElementById('version-info').addEventListener('click', () => {
const electronVersion = process.versions.electron;
const message = `This app is using Electron version: ${electronVersion}`;
document.getElementById('got-version-info').innerHTML = message;
});</code></pre>
<div class="demo-protip">
<h2>ProTip</h2>
<strong>Find versions of Chromium, Node.js and V8.</strong>
<p>Electron also includes versions of Chromium, Node.js and V8 within the <code>process.versions</code> object. You can get there quickly by opening up developer tools in an Electron app and typing <code>process.versions</code>.</p>
<pre><code class="language-js">// Returns version of Chromium in use
process.versions.chrome
// Returns version of V8 in use
process.versions.v8
// Returns version of Node in use
process.versions.node</code></pre>
</div>
</div>
</div>
</div>
<div class="demo">
<div class="demo-wrapper">
<button id="sys-info-demo-toggle" class="js-container-target demo-toggle-button">
Get system information
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Both</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="sys-info">View Demo</button>
<span class="demo-response" id="got-sys-info"></span>
</div>
<p>In the example below we require the module and then return the location of the home directory.</p>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">Electron.IpcMain.On("sys-info", async (args) =>
{
string homePath = await Electron.App.GetPathAsync(PathName.home);
var mainWindow = Electron.WindowManager.BrowserWindows.First();
Electron.IpcMain.Send(mainWindow, "got-sys-info", homePath);
});</code></pre>
</div>
</div>
</div>
<div class="demo">
<div class="demo-wrapper">
<button id="screen-info-demo-toggle" class="js-container-target demo-toggle-button">
Get screen information
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Both</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="screen-info">View Demo</button>
<span class="demo-response" id="got-screen-info"></span>
</div>
<p>The <code>Electron.Screen</code> retrieves information about screen size, displays, cursor position, etc. In the example below we retrieve the dimensions of the monitor in use.</p>
<p>See the full <a href="http://electron.atom.io/docs/api/screen">screen documentation<span class="u-visible-to-screen-reader">(opens in new window)</span></a> for more.</p>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">Electron.IpcMain.On("screen-info", async (args) =>
{
var display = await Electron.Screen.GetPrimaryDisplayAsync();
var mainWindow = Electron.WindowManager.BrowserWindows.First();
Electron.IpcMain.Send(mainWindow, "got-screen-info", display.Size);
});</code></pre>
<div class="demo-protip">
<h2>ProTip</h2>
<strong>Differences in dimensions.</strong>
<p>The <code>.Size</code> property in the example returns the raw dimensions of the screen but because of system menu bars this may not be the actual space available for your app.</p>
<p>To get the dimensions of the available screen space use the <code>.WorkAreaSize</code> property. Using <code>.workArea</code> will return the coordinates as well as the dimensions of the available screen space.</p>
</div>
</div>
</div>
</div>
<script>
(function(){
const { ipcRenderer } = require("electron");
document.getElementById("app-info").addEventListener("click", () => {
ipcRenderer.send("app-info");
});
ipcRenderer.on("got-app-path", (event, path) => {
const message = `This app is located at: ${path}`;
document.getElementById('got-app-info').innerHTML = message;
});
document.getElementById('version-info').addEventListener('click', () => {
const electronVersion = process.versions.electron;
const message = `This app is using Electron version: ${electronVersion}`;
document.getElementById('got-version-info').innerHTML = message;
});
document.getElementById('sys-info').addEventListener('click', () => {
ipcRenderer.send("sys-info");
});
ipcRenderer.on("got-sys-info", (event, homeDir) => {
const message = `Your system home directory is: ${homeDir}`;
document.getElementById('got-sys-info').innerHTML = message;
});
document.getElementById("screen-info").addEventListener("click", () => {
ipcRenderer.send("screen-info");
});
ipcRenderer.on("got-screen-info", (event, size) => {
const message = `Your screen is: ${size.width}px x ${size.height}px`;
document.getElementById('got-screen-info').innerHTML = message;
});
}());
</script>
</section>
</template>

View File

@@ -0,0 +1,297 @@
<template class="task-template">
<section id="clipboard-section" class="section js-section u-category-system">
<header class="section-header">
<div class="section-wrapper">
<h1>
<svg class="section-icon"><use xlink:href="assets/img/icons.svg#icon-system"></use></svg>
Clipboard
</h1>
<h3>The <code>Electron.Clipboard</code> provides methods to perform copy and paste operations.</h3>
<p>This module also has methods for copying text as markup (HTML) to the clipboard.</p>
<p>You find the sample source code in <code>Controllers\ClipboardController.cs</code>.</p>
</div>
</header>
<div class="demo">
<div class="demo-wrapper">
<button id="copy-to-demo-toggle" class="js-container-target demo-toggle-button">
Copy
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Both</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="copy-to">Copy</button>
<input class="demo-input" id="copy-to-input" aria-label="Click copy" placeholder="Click copy."></input>
</div>
<p>In this example we copy a phrase to the clipboard. After clicking 'Copy' use the text area to paste (CMD + V or CTRL + V) the phrase from the clipboard.</p>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">Electron.IpcMain.On("copy-to", (text) =>
{
Electron.Clipboard.WriteText(text.ToString());
});</code></pre>
<div class="demo-protip">
<h2>ProTip</h2>
<strong>Electron.js Support in Electron.NET.</strong>
<p>The <code>clipboard</code> module is built into Electron.js (therefore you can use this in the renderer processes).</p>
<pre><code class="language-js">const clipboard = require('electron').clipboard;
const copyBtn = document.getElementById('copy-to');
const copyInput = document.getElementById('copy-to-input');
copyBtn.addEventListener('click', () => {
if (copyInput.value !== '') copyInput.value = '';
copyInput.placeholder = 'Copied! Paste here to see.';
clipboard.writeText('Electron Demo!');
})</code></pre>
</div>
</div>
</div>
</div>
<div class="demo">
<div class="demo-wrapper">
<button id="paste-to-demo-toggle" class="js-container-target demo-toggle-button">
Paste
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Both</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="paste-to">Paste</button>
<span class="demo-response" id="paste-from"></span>
</div>
<p>In this example we copy a string to the clipboard and then paste the results into a message above.</p>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">Electron.IpcMain.On("paste-to", async (text) =>
{
Electron.Clipboard.WriteText(text.ToString());
string pasteText = await Electron.Clipboard.ReadTextAsync();
var mainWindow = Electron.WindowManager.BrowserWindows.First();
Electron.IpcMain.Send(mainWindow, "paste-from", pasteText);
});</code></pre>
<div class="demo-protip">
<h2>ProTip</h2>
<strong>Electron.js Support in Electron.NET.</strong>
<p>The <code>clipboard</code> module is built into Electron.js (therefore you can use this in the renderer processes).</p>
<pre><code class="language-js">const clipboard = require('electron').clipboard;
const pasteBtn = document.getElementById('paste-to');
pasteBtn.addEventListener('click', () => {
clipboard.writeText('What a demo!');
const message = `Clipboard contents: ${clipboard.readText()}`;
document.getElementById('paste-from').innerHTML = message;
})</code></pre>
</div>
</div>
</div>
</div>
<div class="demo">
<div class="demo-wrapper">
<button id="copy-to-demo-toggle" class="js-container-target demo-toggle-button">
Copy Image
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Both</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="copy-image-to">Copy</button>
<textarea id="paste-image-div" onpaste="console.log('onpastefromhtml')"></textarea>
<div class="demo-image-box" id="image-paste-div"></div>
</div>
<p>In this example we copy an image to the Clipboard. After clicking 'Copy' use the text area to paste (CMD + V or CTRL + V) the image from the clipboard.</p>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">Electron.IpcMain.On("copy-image-to", async (test) =>
{
var nativeImage = NativeImage.CreateFromDataURL(test.ToString());
Electron.Clipboard.WriteImage(nativeImage);
});</code></pre>
<div class="demo-protip">
<h2>ProTip</h2>
<strong>Electron.js Support in Electron.NET.</strong>
<p>The <code>clipboard</code> module is built into Electron.js (therefore you can use this in the renderer processes).</p>
<pre><code class="language-js">const { nativeImage, clipboard } = require('electron');
const copyBtn = document.getElementById('copy-to-image');
const imageDataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACmklEQVR42oVTX0hTURj/zr2b07utMR0st2m30UxZJRHpWNDjRkUPvgUZQdGD4Esq6yWf9EVx9iL4EAiBVNDDHqxs9RBI+ZcorLVSkemcNdmct827Oe92Ot1b270i9MHHOfc73/f7fvd3zofggGU/OD1k8RJ3E2/6Gw4TnyYerDoXei3PR/82uwtOliydlJbtoA1OhtY7AFVZxDOc3YRiegkELsRjPjKKMYxoz3+NlAAyc00sBuhTm9ztavNFoBizhIqVbYrZOOzHp0DYmhkHhHt1reGIePTrfeOQ2tzaranzAqI1cmJlw1IYF3KwF30F+a0Fv8Ed7kHbU40elc4SYOxXGJoxibmDl++D70W/AkeMvewX94VsEvjV57yQjrWhxNuTfsbW0lVlaynxHbw6AL4Jn9QSkdb4QIzQ4TfmIbsxP4zibxpmjE6vS33EfCjzEn+MFHrkuTikQsFZFJt07BxtvWSgVBXwX8MyQYU8xOcmObQ+cWLH6rpAANQy2RU3LKssX0tR2IfY7DsOrQbsM7VnHK5Kg04S69o8+J62HEpAfpbjMvBjcXkWrTw77jeyxq7qeqOUdH0J2u7pwNFsUbyF5cVNCAxkwPe4QfxORVKQXNsZRt+esJ5KvSpQ26hnKrQ0JBI5GLvLSSCntFLxl12x+NYDA5hqNJDnC/Dze4bPpgtt4o+Gxo8NGS10t9lOA6VCkEhViCByE4ur96C4j2BrVYDtWMHvvLHWIwJ8flTPEqp91dZie40Ng0ZbFIVKxCT+JqsEsscjSEYpUkyRpwy9p2+uR0pSfxqrYxFGnYxB6NDXCIzOKEDlHyCSkctQkEmpIJ1U8TxHj5L0kebb0fIwye3jQ5uH9PUiQIpxxoCnSSx49k5UMc6/ARiuBRgyrEC3AAAAAElFTkSuQmCC';
copyBtn.addEventListener('click', () => {
var image = nativeImage.CreateFromDataURL(imageDataUrl);
clipboard.WriteImage(image);
})
</code></pre>
</div>
</div>
</div>
</div>
<div class="demo">
<div class="demo-wrapper">
<button id="copy-to-demo-toggle" class="js-container-target demo-toggle-button">
Paste Image
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Both</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="paste-image-from">Paste</button>
<img class="demo-image-box" id="paste-image-from-div"></img>
</div>
<p>In this example we paste an image from the Clipboard. After clicking 'Paste', if your clipboard contained an image, the image will show up next to the paste button.</p>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">Electron.IpcMain.On("paste-image-to", async test =>
{
var nativeImage = await Electron.Clipboard.ReadImageAsync();
var mainWindow = Electron.WindowManager.BrowserWindows.First();
Electron.IpcMain.Send(mainWindow, "paste-image-from", JsonConvert.SerializeObject(nativeImage));
});</code></pre>
<div class="demo-protip">
<h2>ProTip</h2>
<strong>Electron.js Support in Electron.NET.</strong>
<p>The <code>clipboard</code> module is built into Electron.js (therefore you can use this in the renderer processes).</p>
<pre><code class="language-js">const { nativeImage, clipboard } = require('electron');
const pasteBtn = document.getElementById('paste-to-image');
copyBtn.addEventListener('click', () => {
const image = clipboard.readImage();
document.getElementById('image-holder').src = image.toDataURL();
})</code></pre>
</div>
</div>
</div>
</div>
<script>
(function () {
const { ipcRenderer, nativeImage } = require("electron");
document.getElementById("copy-to").addEventListener("click", () => {
document.getElementById('copy-to-input').placeholder = 'Copied! Paste here to see.';
ipcRenderer.send("copy-to", "Electron.NET Demo!");
});
document.getElementById("paste-to").addEventListener("click", () => {
ipcRenderer.send("paste-to", "What a demo!");
});
document.getElementById("copy-image-to").addEventListener("click", () => {
ipcRenderer.send("copy-image-to", 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAACmklEQVR42oVTX0hTURj/zr2b07utMR0st2m30UxZJRHpWNDjRkUPvgUZQdGD4Esq6yWf9EVx9iL4EAiBVNDDHqxs9RBI+ZcorLVSkemcNdmct827Oe92Ot1b270i9MHHOfc73/f7fvd3zofggGU/OD1k8RJ3E2/6Gw4TnyYerDoXei3PR/82uwtOliydlJbtoA1OhtY7AFVZxDOc3YRiegkELsRjPjKKMYxoz3+NlAAyc00sBuhTm9ztavNFoBizhIqVbYrZOOzHp0DYmhkHhHt1reGIePTrfeOQ2tzaranzAqI1cmJlw1IYF3KwF30F+a0Fv8Ed7kHbU40elc4SYOxXGJoxibmDl++D70W/AkeMvewX94VsEvjV57yQjrWhxNuTfsbW0lVlaynxHbw6AL4Jn9QSkdb4QIzQ4TfmIbsxP4zibxpmjE6vS33EfCjzEn+MFHrkuTikQsFZFJt07BxtvWSgVBXwX8MyQYU8xOcmObQ+cWLH6rpAANQy2RU3LKssX0tR2IfY7DsOrQbsM7VnHK5Kg04S69o8+J62HEpAfpbjMvBjcXkWrTw77jeyxq7qeqOUdH0J2u7pwNFsUbyF5cVNCAxkwPe4QfxORVKQXNsZRt+esJ5KvSpQ26hnKrQ0JBI5GLvLSSCntFLxl12x+NYDA5hqNJDnC/Dze4bPpgtt4o+Gxo8NGS10t9lOA6VCkEhViCByE4ur96C4j2BrVYDtWMHvvLHWIwJ8flTPEqp91dZie40Ng0ZbFIVKxCT+JqsEsscjSEYpUkyRpwy9p2+uR0pSfxqrYxFGnYxB6NDXCIzOKEDlHyCSkctQkEmpIJ1U8TxHj5L0kebb0fIwye3jQ5uH9PUiQIpxxoCnSSx49k5UMc6/ARiuBRgyrEC3AAAAAElFTkSuQmCC');
});
document.getElementById("paste-image-from").addEventListener("click", () => {
ipcRenderer.send("paste-image-to");
});
ipcRenderer.on("paste-from", (sender, text) => {
const message = `Clipboard contents: ${text}`;
document.getElementById("paste-from").innerText = message;
});
ipcRenderer.on("paste-image-from", (sender, data) => {
console.log(data);
var data = JSON.parse(data);
const ni = nativeImage.createEmpty();
for (var i in data) {
var scaleFactor = i;
var bytes = data[i];
var buff = Buffer.from(bytes, 'base64');
ni.addRepresentation({ scaleFactor: scaleFactor, buffer: buff });
}
document.getElementById("paste-image-from-div").src = ni.toDataURL();
});
var PasteImage = function (el) {
this._el = el;
this._listenForPaste();
};
PasteImage.prototype._getURLObj = function () {
return window.URL || window.webkitURL;
};
PasteImage.prototype._pasteImage = function (image) {
this.emit('paste-image', image);
};
PasteImage.prototype._pasteImageSource = function (src) {
var self = this,
image = new Image();
image.onload = function () {
self._pasteImage(image);
};
image.src = src;
};
PasteImage.prototype._onPaste = function (e) {
// We need to check if event.clipboardData is supported (Chrome & IE)
if (e.clipboardData && e.clipboardData.items) {
// Get the items from the clipboard
var items = e.clipboardData.items;
// Loop through all items, looking for any kind of image
for (var i = 0; i < items.length; i++) {
if (items[i].type.indexOf('image') !== -1) {
// We need to represent the image as a file
var blob = items[i].getAsFile();
// Use a URL or webkitURL (whichever is available to the browser) to create a
// temporary URL to the object
var URLObj = this._getURLObj();
var source = URLObj.createObjectURL(blob);
// The URL can then be used as the source of an image
this._pasteImageSource(source);
// Prevent the image (or URL) from being pasted into the contenteditable element
e.preventDefault();
}
}
}
};
PasteImage.prototype._listenForPaste = function () {
var self = this;
self._origOnPaste = self._el.onpaste;
self._el.addEventListener('paste', function (e) {
self._onPaste(e);
// Preserve an existing onpaste event handler
if (self._origOnPaste) {
self._origOnPaste.apply(this, arguments);
}
});
};
PasteImage.prototype.on = function (event, callback) {
this._callback = callback;
};
PasteImage.prototype.emit = function (event, arg) {
this._callback(arg);
};
var pasteImage = new PasteImage(document.getElementById('paste-image-div'));
pasteImage.on('paste-image', function (image) {
document.getElementById('image-paste-div').appendChild(image);
});
}());
</script>
</section>
</template>

View File

@@ -0,0 +1,113 @@
<template class="task-template">
<section id="crash-hang-section" class="section js-section u-category-windows">
<header class="section-header">
<div class="section-wrapper">
<h1>
<svg class="section-icon"><use xlink:href="assets/img/icons.svg#icon-windows"></use></svg>
Handling Window Crashes and Hangs
</h1>
<h3>The <code>Electron.WindowManager</code> will emit events when the renderer process crashes or hangs. You can listen for these events and give users the chance to reload, wait or close that window.</h3>
<p>You find the sample source code in <code>Controllers\CrashHangController.cs</code>.</p>
</div>
</header>
<div class="demo">
<div class="demo-wrapper">
<button id="new-window-crashes-demo-toggle" class="js-container-target demo-toggle-button">Relaunch window after the process crashes
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="process-crash">View Demo</button>
</div>
<p>In this demo we create a new window and provide a link that will force a crash using <code>process.crash()</code>.</p>
<p>The window is listening for the crash event and when the event occurs it prompts the user with two options: reload or close.</p>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">var browserWindow = await Electron.WindowManager.CreateWindowAsync(viewPath);
browserWindow.WebContents.OnCrashed += async (killed) => {
var options = new MessageBoxOptions("This process has crashed.") {
Type = MessageBoxType.info,
Title = "Renderer Process Crashed",
Buttons = new string[] { "Reload", "Close" }
};
var result = await Electron.Dialog.ShowMessageBoxAsync(options);
if (result.Response == 0)
{
browserWindow.Reload();
} else
{
browserWindow.Close();
}
};</code></pre>
</div>
</div>
</div>
<div class="demo">
<div class="demo-wrapper">
<button id="new-window-hangs-demo-toggle" class="js-container-target demo-toggle-button">Relaunch window after the process hangs
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="process-hang">View Demo</button>
</div>
<p>In this demo we create a new window and provide a link that will force the process to hang using <code>process.hang()</code>.</p>
<p>The window is listening for the process to become officially unresponsive (this can take up to 30 seconds). When this event occurs it prompts the user with two options: reload or close.</p>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">var browserWindow = await Electron.WindowManager.CreateWindowAsync();
browserWindow.OnUnresponsive += async () => {
var options = new MessageBoxOptions("This process is hanging.")
{
Type = MessageBoxType.info,
Title = "Renderer Process Hanging",
Buttons = new string[] { "Reload", "Close" }
};
var result = await Electron.Dialog.ShowMessageBoxAsync(options);
if (result.Response == 0)
{
browserWindow.Reload();
}
else
{
browserWindow.Close();
}
};</code></pre>
<div class="demo-protip">
<h2>ProTip</h2>
<strong>Wait for the process to become responsive again.</strong>
<p>A third option in the case of a process that is hanging is to wait and see if the problem resolves, allowing the process to become responsive again. To do this, use the <code>BrowserWindow</code> event 'OnResponsive' as shown below.</p>
<pre><code class="csharp">browserWindow.OnResponsive += () =>
{
// Do something when the window is responsive again
};</code></pre>
</div>
</div>
</div>
</div>
<script type="text/javascript">
(function(){
const { ipcRenderer } = require("electron");
document.getElementById("process-crash").addEventListener("click", () => {
ipcRenderer.send("process-crash");
});
document.getElementById("process-hang").addEventListener("click", () => {
ipcRenderer.send("process-hang");
});
}());
</script>
</section>
</template>

View File

@@ -0,0 +1,24 @@
<style>
body {
padding: 20px;
font-family: system, -apple-system, '.SFNSText-Regular', 'SF UI Text', 'Lucida Grande', 'Segoe UI', Ubuntu, Cantarell, sans-serif;
color: #fff;
background-color: #8aba87;
text-align: center;
font-size: 40px;
}
#crash {
color: white;
opacity: 0.7;
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
font-size: 12px;
text-decoration: none;
}
</style>
<p>Click the text below to crash and then reload this process.</p>
<a id="crash" href="javascript:process.crash()">Crash this process</a>

View File

@@ -0,0 +1,29 @@
<style>
body {
padding: 20px;
font-family: system, -apple-system, '.SFNSText-Regular', 'SF UI Text', 'Lucida Grande', 'Segoe UI', Ubuntu, Cantarell, sans-serif;
color: #fff;
background-color: #8aba87;
text-align: center;
}
p {
font-size: 32px;
}
#crash {
color: white;
opacity: 0.7;
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
font-size: 12px;
text-decoration: none;
}
</style>
<p>Click the text below to hang and then reload this process.</p>
<small>(This will take up to 30 seconds.)</small>
<a id="crash" href="javascript:process.hang()">Hang this process</a>

View File

@@ -0,0 +1,126 @@
<template class="task-template">
<section id="desktop-capturer-section" class="section js-section u-category-media">
<header class="section-header">
<div class="section-wrapper">
<h1>
<svg class="section-icon"><use xlink:href="assets/img/icons.svg#icon-media"></use></svg>
Take a screenshot
</h1>
<h3>The <code>desktopCapturer</code> module in Electron can be used to access any media, such as audio, video, screen and window, that is available through the <code>getUserMedia</code> web API in Chromium.</h3>
<p>Open the <a href="http://electron.atom.io/docs/api/desktop-capturer">full API documentation<span class="u-visible-to-screen-reader">(opens in new window)</span></a> in your browser.</p>
</div>
</header>
<div class="demo">
<div class="demo-wrapper">
<button id="print-pdf-demo-toggle" class="js-container-target demo-toggle-button">Take a Screenshot
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux | Process: Renderer</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="screen-shot">View Demo</button>
<span class="demo-response is-selectable" id="screenshot-path"></span>
</div>
<p>This demo uses the <code>desktopCapturer</code> module to gather screens in use and select the entire screen and take a snapshot of what is visible.</p>
<p>Clicking the demo button will take a screenshot of your current screen and open it in your default viewer.</p>
<h5>Renderer Process (JavaScript)</h5>
<pre><code class="javascript">const electron = require('electron');
const desktopCapturer = electron.desktopCapturer;
const electronScreen = electron.screen;
const shell = electron.shell;
const fs = require('fs');
const os = require('os');
const path = require('path');
const screenshot = document.getElementById('screen-shot');
const screenshotMsg = document.getElementById('screenshot-path');
screenshot.addEventListener('click', function (event) {
screenshotMsg.textContent = 'Gathering screens...';
const thumbSize = determineScreenShotSize();
let options = { types: ['screen'], thumbnailSize: thumbSize }
desktopCapturer.getSources(options, function (error, sources) {
if (error) return console.log(error);
sources.forEach(function (source) {
if (source.name === 'Entire screen' || source.name === 'Screen 1') {
const screenshotPath = path.join(os.tmpdir(), 'screenshot.png');
fs.writeFile(screenshotPath, source.thumbnail.toPng(), function (error) {
if (error) return console.log(error);
shell.openExternal('file://' + screenshotPath);
const message = `Saved screenshot to: ${screenshotPath}`;
screenshotMsg.textContent = message;
});
}
})
})
})
function determineScreenShotSize () {
const screenSize = electronScreen.getPrimaryDisplay().workAreaSize;
const maxDimension = Math.max(screenSize.width, screenSize.height);
return {
width: maxDimension * window.devicePixelRatio,
height: maxDimension * window.devicePixelRatio
}
}</code></pre>
</div>
</div>
</div>
<script type="text/javascript">
(function(){
const electron = require('electron');
const desktopCapturer = electron.desktopCapturer;
const electronScreen = electron.screen;
const shell = electron.shell;
const fs = require('fs');
const os = require('os');
const path = require('path');
const screenshot = document.getElementById('screen-shot');
const screenshotMsg = document.getElementById('screenshot-path');
screenshot.addEventListener('click', function (event) {
screenshotMsg.textContent = 'Gathering screens...';
const thumbSize = determineScreenShotSize();
let options = { types: ['screen'], thumbnailSize: thumbSize }
desktopCapturer.getSources(options, function (error, sources) {
if (error) return console.log(error);
sources.forEach(function (source) {
if (source.name === 'Entire screen' || source.name === 'Screen 1') {
const screenshotPath = path.join(os.tmpdir(), 'screenshot.png');
fs.writeFile(screenshotPath, source.thumbnail.toPng(), function (error) {
if (error) return console.log(error);
shell.openExternal('file://' + screenshotPath);
const message = `Saved screenshot to: ${screenshotPath}`;
screenshotMsg.textContent = message;
});
}
})
})
})
function determineScreenShotSize () {
const screenSize = electronScreen.getPrimaryDisplay().workAreaSize;
const maxDimension = Math.max(screenSize.width, screenSize.height);
return {
width: maxDimension * window.devicePixelRatio,
height: maxDimension * window.devicePixelRatio
}
}
}());
</script>
</section>
</template>

View File

@@ -0,0 +1,222 @@
<template class="task-template">
<section id="dialogs-section" class="section js-section u-category-native-ui">
<header class="section-header">
<div class="section-wrapper">
<h1>
<svg class="section-icon"><use xlink:href="assets/img/icons.svg#icon-native-ui"></use></svg>
Use system dialogs
</h1>
<h3>The <code>Electron.Dialog</code> in Electron.NET allows you to use native system dialogs for opening files or directories, saving a file or displaying informational messages.</h3>
<p>This is a main process module because this process is more efficient with native utilities and it allows the call to happen without interupting the visible elements in your page's renderer process.</p>
<p>You find the sample source code in <code>Controllers\DialogsController.cs</code>.</p>
</div>
</header>
<div class="demo">
<div class="demo-wrapper">
<button id="open-file-demo-toggle" class="js-container-target demo-toggle-button">
Open a File or Directory
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="select-directory">View Demo</button>
<span class="demo-response" id="selected-file"></span>
</div>
<p>In this demo, the <code>ipcRenderer</code> is used to send a message from the renderer process instructing the main process to launch the open file (or directory) dialog. If a file is selected, the main process can send that information back to the renderer process.</p>
<h5>Renderer Process (JavaScript)</h5>
<pre><code class="language-js">const { ipcRenderer } = require("electron");
document.getElementById("select-directory").addEventListener("click", () => {
ipcRenderer.send("select-directory");
});
ipcRenderer.on("select-directory-reply", (sender, path) => {
document.getElementById("selected-file").innerText = `You selected: ${path}`;;
});</code></pre>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">Electron.IpcMain.On("select-directory", async (args) => {
var mainWindow = Electron.WindowManager.BrowserWindows.First();
var options = new OpenDialogOptions {
Properties = new OpenDialogProperty[] {
OpenDialogProperty.openFile,
OpenDialogProperty.openDirectory
}
};
string[] files = await Electron.Dialog.ShowOpenDialogAsync(mainWindow, options);
Electron.IpcMain.Send(mainWindow, "select-directory-reply", files);
});</code></pre>
</div>
</div>
</div>
<div class="demo">
<div class="demo-wrapper">
<button id="error-dialog-demo-toggle" class="js-container-target demo-toggle-button">
Error Dialog
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button id="error-dialog" class="demo-button">View Demo</button>
</div>
<p>In this demo, the <code>ipcRenderer</code> is used to send a message from the renderer process instructing the main process to launch the error dialog.</p>
<h5>Renderer Process (JavaScript)</h5>
<pre><code class="language-js">const { ipcRenderer } = require("electron");
document.getElementById("error-dialog").addEventListener("click", () => {
ipcRenderer.send("error-dialog");
});</code></pre>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">Electron.IpcMain.On("error-dialog", (args) =>
{
Electron.Dialog.ShowErrorBox("An Error Message", "Demonstrating an error message.");
});</code></pre>
</div>
</div>
</div>
<div class="demo">
<div class="demo-wrapper">
<button id="information-dialog-demo-toggle" class="js-container-target demo-toggle-button">
Information Dialog
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="information-dialog">View Demo</button>
<span class="demo-response" id="info-selection"></span>
</div>
<p>In this demo, the <code>ipcRenderer</code> is used to send a message from the renderer process instructing the main process to launch the information dialog. Options may be provided for responses which can then be relayed back to the renderer process.</p>
<p>Note: The <code>title</code> property is not displayed in macOS.</p>
<p>An information dialog can contain an icon, your choice of buttons, title and message.</p>
<h5>Renderer Process (JavaScript)</h5>
<pre><code class="javascript">document.getElementById("information-dialog").addEventListener("click", () => {
ipcRenderer.send("information-dialog");
});
ipcRenderer.on("information-dialog-reply", (sender, index) => {
let message = 'You selected ';
if(index == 0) {
message += 'yes.'
} else {
message += 'no.'
}
document.getElementById("info-selection").innerText = message;
});</code></pre>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">Electron.IpcMain.On("information-dialog", async (args) =>
{
var options = new MessageBoxOptions("This is an information dialog. Isn't it nice?")
{
Type = MessageBoxType.info,
Title = "Information",
Buttons = new string[] { "Yes", "No" }
};
var result = await Electron.Dialog.ShowMessageBoxAsync(options);
var mainWindow = Electron.WindowManager.BrowserWindows.First();
Electron.IpcMain.Send(mainWindow, "information-dialog-reply", result.Response);
});</code></pre>
</div>
</div>
</div>
<div class="demo">
<div class="demo-wrapper">
<button id="save-dialog-demo-toggle" class="js-container-target demo-toggle-button">
Save Dialog
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="save-dialog">View Demo</button>
<span class="demo-response" id="file-saved"></span>
</div>
<p>In this demo, the <code>ipcRenderer</code> is used to send a message from the renderer process instructing the main process to launch the save dialog. It returns the path selected by the user which can be relayed back to the renderer process.</p>
<h5>Renderer Process (JavaScript)</h5>
<pre><code class="javascript">const { ipcRenderer } = require("electron");
document.getElementById("save-dialog").addEventListener("click", () => {
ipcRenderer.send("save-dialog");
});
ipcRenderer.on("save-dialog-reply", (sender, path) => {
if (!path) path = 'No path';
document.getElementById('file-saved').innerHTML = `Path selected: ${path}`;
});</code></pre>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">Electron.IpcMain.On("save-dialog", async (args) =>
{
var mainWindow = Electron.WindowManager.BrowserWindows.First();
var options = new SaveDialogOptions
{
Title = "Save an Image",
Filters = new FileFilter[]
{
new FileFilter { Name = "Images", Extensions = new string[] {"jpg", "png", "gif" } }
}
};
var result = await Electron.Dialog.ShowSaveDialogAsync(mainWindow, options);
Electron.IpcMain.Send(mainWindow, "save-dialog-reply", result);
});</code></pre>
</div>
</div>
</div>
<script>
(function(){
const { ipcRenderer } = require("electron");
document.getElementById("select-directory").addEventListener("click", () => {
ipcRenderer.send("select-directory");
});
ipcRenderer.on("select-directory-reply", (sender, path) => {
document.getElementById("selected-file").innerText = `You selected: ${path}`;
});
document.getElementById("error-dialog").addEventListener("click", () => {
ipcRenderer.send("error-dialog");
});
document.getElementById("information-dialog").addEventListener("click", () => {
ipcRenderer.send("information-dialog");
});
ipcRenderer.on("information-dialog-reply", (sender, index) => {
let message = 'You selected ';
if(index == 0) {
message += 'yes.'
} else {
message += 'no.'
}
document.getElementById("info-selection").innerText = message;
});
document.getElementById("save-dialog").addEventListener("click", () => {
ipcRenderer.send("save-dialog");
});
ipcRenderer.on("save-dialog-reply", (sender, path) => {
if (!path) path = 'No path';
document.getElementById('file-saved').innerHTML = `Path selected: ${path}`;
});
}());
</script>
</section>
</template>

View File

@@ -0,0 +1,105 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="assets/css/variables.css">
<link rel="stylesheet" href="assets/css/nativize.css">
<link rel="stylesheet" href="assets/css/global.css">
<link rel="stylesheet" href="assets/css/about.css">
<link rel="stylesheet" href="assets/css/nav.css">
<link rel="stylesheet" href="assets/css/section.css">
<link rel="stylesheet" href="assets/css/demo.css">
<link rel="stylesheet" href="assets/css/github.css">
<link rel="stylesheet" href="assets/css/highlight.min.css">
</head>
<body>
<nav class="nav js-nav">
<header class="nav-header">
<h1 class="nav-title">Electron.NET <strong>API Demos</strong></h1>
</header>
<div class="nav-item u-category-windows">
<h5 class="nav-category">
<svg class="nav-icon"><use xlink:href="assets/img/icons.svg#icon-windows"></use></svg>
Windows
</h5>
<button type="button" id="button-windows" data-section="windows" class="nav-button">Create and manage <em>windows</em></button>
<button type="button" id="button-crash-hang" data-section="crash-hang" class="nav-button">Handling window <em>crashes and hangs</em></button>
</div>
<div class="nav-item u-category-menu">
<h5 class="nav-category">
<svg class="nav-icon"><use xlink:href="assets/img/icons.svg#icon-menu"></use></svg>
Menus
</h5>
<button type="button" id="button-menus" data-section="menus" class="nav-button">Customize <em>menus</em> </button>
<button type="button" id="button-shortcuts" data-section="shortcuts" class="nav-button">Register keyboard <em>shortcuts</em></button>
</div>
<div class="nav-item u-category-native-ui">
<h5 class="nav-category">
<svg class="nav-icon"><use xlink:href="assets/img/icons.svg#icon-native-ui"></use></svg>
Native User Interface
</h5>
<button type="button" id="button-ex-links-file-manager" data-section="ex-links-file-manager" class="nav-button">Open <em>external links</em> or system <em>file manager</em></button>
<button type="button" id="button-notifications" data-section="notifications" class="nav-button"> Notifications with and without custom <em>image</em></button>
<button type="button" id="button-dialogs" data-section="dialogs" class="nav-button">Use system <em>dialogs</em></button>
<button type="button" id="button-tray" data-section="tray" class="nav-button">Put your app in the <em>tray</em></button>
</div>
<div class="nav-item u-category-communication">
<h5 class="nav-category">
<svg class="nav-icon"><use xlink:href="assets/img/icons.svg#icon-communication"></use></svg>
Communication
</h5>
<button type="button" id="button-ipc" data-section="ipc" class="nav-button">Communicate between the <em>two processes</em></button>
<button type="button" id="button-hosthook" data-section="hosthook" class="nav-button">Execute your own <em>TypeScript code</em></button>
</div>
<div class="nav-item u-category-system">
<h5 class="nav-category">
<svg class="nav-icon"><use xlink:href="assets/img/icons.svg#icon-system"></use></svg>
System
</h5>
<button type="button" id="button-app-sys-information" data-section="app-sys-information" class="nav-button">Get app or user <em>system information</em></button>
<button type="button" id="button-clipboard" data-section="clipboard" class="nav-button">Copy and paste from the <em>clipboard</em></button>
<!-- <button type="button" id="button-protocol" data-section="protocol" class="nav-button">Launch app from <em>protocol handler</em></button> -->
</div>
<div class="nav-item u-category-update">
<h5 class="nav-category">
<svg class="nav-icon"><use xlink:href="assets/img/icons.svg#icon-update"></use></svg>
Update
</h5>
<button type="button" id="button-update" data-section="update" class="nav-button">Enable apps to <em>update</em> themselves
</button>
</div>
<div class="nav-item u-category-media">
<h5 class="nav-category">
<svg class="nav-icon"><use xlink:href="assets/img/icons.svg#icon-media"></use></svg>
Media
</h5>
<button type="button" id="button-pdf" data-section="pdf" class="nav-button"><em>Print</em> to PDF</button>
<button type="button" id="button-desktop-capturer" data-section="desktop-capturer" class="nav-button">Take a <em>screenshot</em></button>
</div>
<footer class="nav-footer">
<button type="button" id="button-about" data-modal="about" class="nav-footer-button">About</button>
<a class="nav-footer-logo" href="https://github.com/ElectronNET/Electron.NET" aria-label="Homepage" target="_blank">
Made with <img src="assets/img/heart.jpg" style="width:14px;" /> in C#
</a>
</footer>
</nav>
<main class="content js-content is-shown"></main>
<script src="~/assets/imports.js"></script>
<script src="~/assets/ex-links.js"></script>
<script src="~/assets/nav.js"></script>
<script src="~/assets/demo-btns.js"></script>
<script src="~/assets/highlight.min.js"></script>
<script src="~/assets/code-blocks.js"></script>
</body>
</html>

View File

@@ -0,0 +1,91 @@
<template class="task-template">
<section id="hosthook-section" class="section js-section u-category-communication">
<header class="communication">
<div class="section-wrapper">
<h1>
<svg class="section-icon"><use xlink:href="assets/img/icons.svg#icon-communication"></use></svg>
Execute your own TypeScript code
</h1>
<h3>The <code>HostHook</code> API allows you to execute your own JavaScript/TypeScript code on the host process.</h3>
<p>Create first an ElectronHostHook directory via the Electron.NET CLI, with the following command: <code>electronize add hosthook</code>.</p>
<p>In this directory you can install any NPM packages and embed your own JavaScript/TypeScript code. It is also possible to respond to events from the host process.</p>
<p>You find the sample source code in <code>Controllers\HostHookController.cs</code> and in the <code>ElectronHostHook</code> folder.</p>
</div>
</header>
<div class="demo">
<div class="demo-wrapper">
<button id="async-msg-demo-toggle" class="js-container-target demo-toggle-button">
Execute TypeScript code
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="start-hoosthook-button">Create Excel-File</button>
<span class="demo-response" id="hoosthook-reply"></span>
</div>
<p>Use <code>Electron.HostHook.CallAsync</code> to execute asynchronously your own JavaScript/TypeScript code, that expect a result value.</p>
<p>This example execute the TypeScript code, that listening on "create-excel". The TypeScript code use a NPM Package names exceljs, to create a Excel file and reply a success message.</p>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">Electron.IpcMain.On("start-hoosthook", async (args) =>
{
var mainWindow = Electron.WindowManager.BrowserWindows.First();
var options = new OpenDialogOptions
{
Properties = new OpenDialogProperty[]
{
OpenDialogProperty.openDirectory
}
};
var folderPath = await Electron.Dialog.ShowOpenDialogAsync(mainWindow, options);
var resultFromTypeScript = await Electron.HostHook.CallAsync<string>("create-excel-file", folderPath);
Electron.IpcMain.Send(mainWindow, "excel-file-created", resultFromTypeScript);
});</code>
</pre>
<h5>index.ts from ElectronHostHook-Folder (TypeScript)</h5>
<pre>
<code class="typescript">import * as Electron from "electron";
import { Connector } from "./connector";
import { ExcelCreator } from "./excelCreator";
export class HookService extends Connector {
constructor(socket: SocketIO.Socket, public app: Electron.App) {
super(socket, app);
}
onHostReady(): void {
// execute your own JavaScript Host logic here
this.on("create-excel", async (path, done) => {
const excelCreator: ExcelCreator = new ExcelCreator();
const result: string = await excelCreator.create(path);
done(result);
});
}
}</code>
</pre>
</div>
</div>
</div>
<script>
(function(){
const { ipcRenderer } = require("electron");
document.getElementById("start-hoosthook-button").addEventListener("click", () => {
ipcRenderer.send("start-hoosthook");
});
ipcRenderer.on('excel-file-created', (event, arg) => {
const message = `Asynchronous message reply: ${arg}`;
document.getElementById('hoosthook-reply').innerHTML = message;
});
}());
</script>
</section>
</template>

View File

@@ -0,0 +1,104 @@
<template class="task-template">
<section id="ipc-section" class="section js-section u-category-communication">
<header class="communication">
<div class="section-wrapper">
<h1>
<svg class="section-icon"><use xlink:href="assets/img/icons.svg#icon-communication"></use></svg>
Communication between processes
</h1>
<h3>The <code>ipc</code> (inter-process communication) module allows you to send and receive synchronous and asynchronous messages between the main and renderer processes.</h3>
<p>There is a version of this module available for both processes: <code>Electron.IpcMain</code> and <code>ipcRenderer</code>.</p>
<p>You find the sample source code in <code>Controllers\IpcController.cs</code>.</p>
</div>
</header>
<div class="demo">
<div class="demo-wrapper">
<button id="async-msg-demo-toggle" class="js-container-target demo-toggle-button">
Asynchronous messages
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Both</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="async-msg">Ping</button>
<span class="demo-response" id="async-reply"></span>
</div>
<p>Using <code>ipcRenderer</code> to send messages between processes asynchronously is the preferred method since it will return when finished without blocking other operations in the same process.</p>
<p>This example sends a "ping" from this process (renderer) to the main process. The main process then replies with "pong".</p>
<h5>Renderer Process (JavaScript)</h5>
<pre><code class="javascript">const { ipcRenderer } = require("electron");
document.getElementById("async-msg").addEventListener("click", () => {
ipcRenderer.send("async-msg", 'ping');
});
ipcRenderer.on('asynchronous-reply', (event, arg) => {
const message = `Asynchronous message reply: ${arg}`;
document.getElementById('async-reply').innerHTML = message;
});</code></pre>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">Electron.IpcMain.On("async-msg", (args) =>
{
var mainWindow = Electron.WindowManager.BrowserWindows.First();
Electron.IpcMain.Send(mainWindow, "asynchronous-reply", "pong");
});</code></pre>
</div>
</div>
</div>
<div class="demo">
<div class="demo-wrapper">
<button id="sync-msg-demo-toggle" class="js-container-target demo-toggle-button">
Synchronous messages
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Both</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="sync-msg">Ping</button>
<span class="demo-response" id="sync-reply"></span>
</div>
<p>You can use the <code>ipcRenderer</code> module to send synchronous messages between processes as well, but note that the synchronous nature of this method means that it <b>will block</b> other operations while completing its task.</p>
<p>This example sends a synchronous message, "ping", from this process (renderer) to the main process. The main process then replies with "pong".</p>
<h5>Renderer Process (JavaScript)</h5>
<pre><code class="javascript">const { ipcRenderer } = require("electron");
document.getElementById("sync-msg").addEventListener("click", () => {
const reply = ipcRenderer.sendSync("sync-msg", "ping");
const message = `Synchronous message reply: ${reply}`;
document.getElementById('sync-reply').innerHTML = message;
});</code></pre>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">Electron.IpcMain.OnSync("sync-msg", (args) =>
{
return "pong";
});</code></pre>
</div>
</div>
</div>
<script>
(function(){
const { ipcRenderer } = require("electron");
document.getElementById("async-msg").addEventListener("click", () => {
ipcRenderer.send("async-msg", 'ping');
});
ipcRenderer.on('asynchronous-reply', (event, arg) => {
const message = `Asynchronous message reply: ${arg}`;
document.getElementById('async-reply').innerHTML = message;
});
document.getElementById("sync-msg").addEventListener("click", () => {
const reply = ipcRenderer.sendSync("sync-msg", "ping");
const message = `Synchronous message reply: ${reply}`;
document.getElementById('sync-reply').innerHTML = message;
});
}());
</script>
</section>
</template>

View File

@@ -0,0 +1,185 @@
<template class="task-template">
<section id="menus-section" class="section js-section u-category-menu">
<header class="section-header">
<div class="section-wrapper">
<h1>
<svg class="section-icon"><use xlink:href="assets/img/icons.svg#icon-menu"></use></svg>
Customize Menus
</h1>
<h3>The <code>Electron.Menu</code> and <code>MenuItem</code> can be used to create custom native menus.</h3>
<p>There are two kinds of menus: the application (top) menu and context (right-click) menu.</p>
<p>You find the sample source code in <code>Controllers\MenusController.cs</code>.</p>
</div>
</header>
<div class="demo">
<div class="demo-wrapper">
<button id="application-menu-demo-toggle" class="js-container-target demo-toggle-button">
Create an application menu
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
</button>
<div class="demo-box">
<p>The <code>Electron.Menu</code> and <code>MenuItem</code> allow you to customize your application menu. If you don't set any menu, Electron will generate a minimal menu for your app by default.</p>
<p>This app uses the code below to set the application menu. If you click the 'View' option in the application menu and then the 'App Menu Demo', you'll see an information box displayed.</p>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">var menu = new MenuItem[] {
new MenuItem { Label = "Edit", Submenu = new MenuItem[] {
new MenuItem { Label = "Undo", Accelerator = "CmdOrCtrl+Z", Role = MenuRole.undo },
new MenuItem { Label = "Redo", Accelerator = "Shift+CmdOrCtrl+Z", Role = MenuRole.redo },
new MenuItem { Type = MenuType.separator },
new MenuItem { Label = "Cut", Accelerator = "CmdOrCtrl+X", Role = MenuRole.cut },
new MenuItem { Label = "Copy", Accelerator = "CmdOrCtrl+C", Role = MenuRole.copy },
new MenuItem { Label = "Paste", Accelerator = "CmdOrCtrl+V", Role = MenuRole.paste },
new MenuItem { Label = "Select All", Accelerator = "CmdOrCtrl+A", Role = MenuRole.selectall }
}
},
new MenuItem { Label = "View", Submenu = new MenuItem[] {
new MenuItem
{
Label = "Reload",
Accelerator = "CmdOrCtrl+R",
Click = () =>
{
// on reload, start fresh and close any old
// open secondary windows
Electron.WindowManager.BrowserWindows.ToList().ForEach(browserWindow => {
if(browserWindow.Id != 1)
{
browserWindow.Close();
}
else
{
browserWindow.Reload();
}
});
}
},
new MenuItem
{
Label = "Toggle Full Screen",
Accelerator = "CmdOrCtrl+F",
Click = async () =>
{
bool isFullScreen = await Electron.WindowManager.BrowserWindows.First().IsFullScreenAsync();
Electron.WindowManager.BrowserWindows.First().SetFullScreen(!isFullScreen);
}
},
new MenuItem
{
Label = "Open Developer Tools",
Accelerator = "CmdOrCtrl+I",
Click = () => Electron.WindowManager.BrowserWindows.First().WebContents.OpenDevTools()
},
new MenuItem
{
Type = MenuType.separator
},
new MenuItem
{
Label = "App Menu Demo",
Click = async () => {
var options = new MessageBoxOptions("This demo is for the Menu section, showing how to create a clickable menu item in the application menu.");
options.Type = MessageBoxType.info;
options.Title = "Application Menu Demo";
await Electron.Dialog.ShowMessageBoxAsync(options);
}
}
}
},
new MenuItem { Label = "Window", Role = MenuRole.window, Submenu = new MenuItem[] {
new MenuItem { Label = "Minimize", Accelerator = "CmdOrCtrl+M", Role = MenuRole.minimize },
new MenuItem { Label = "Close", Accelerator = "CmdOrCtrl+W", Role = MenuRole.close }
}
},
new MenuItem { Label = "Help", Role = MenuRole.help, Submenu = new MenuItem[] {
new MenuItem
{
Label = "Learn More",
Click = async () => await Electron.Shell.OpenExternalAsync("https://github.com/ElectronNET")
}
}
}
};
Electron.Menu.SetApplicationMenu(menu);</code></pre>
<div class="demo-protip">
<h2>ProTip</h2>
<strong>Know operating system menu differences.</strong>
<p>When designing an app for multiple operating systems it's important to be mindful of the ways application menu conventions differ on each operating system.</p>
<p>For instance, on Windows, accelerators are set with an <code>&</code>. Naming conventions also vary, like between "Settings" or "Preferences". Below are resources for learning operating system specific standards.</p>
<ul>
<li><a href="https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/OSXHIGuidelines/MenuBarMenus.html#//apple_ref/doc/uid/20000957-CH29-SW1">macOS<span class="u-visible-to-screen-reader">(opens in new window)</span></a></li>
<li><a href="https://msdn.microsoft.com/en-us/library/windows/desktop/bb226797(v=vs.85).aspx">Windows<span class="u-visible-to-screen-reader">(opens in new window)</span></a></li>
<li><a href="https://developer.gnome.org/hig/stable/menu-bars.html.en">Linux<span class="u-visible-to-screen-reader">(opens in new window)</span></a></li>
</ul>
</div>
</div>
</div>
</div>
<div class="demo">
<div class="demo-wrapper">
<button id="context-menu-demo-toggle" class="js-container-target demo-toggle-button">
Create a context menu
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="context-menu">View Demo</button>
</div>
<p>A context, or right-click, menu can be created with the <code>Electron.Menu.SetContextMenu()</code> and <code>MenuItem</code> as well. You can right-click anywhere in this app or click the demo button to see an example context menu.</p>
<p>In this demo we use the <code>ipcRenderer</code> module to show the context menu when explicitly calling it from the renderer process.</p>
<p>See the full <a href="http://electron.atom.io/docs/api/web-contents/#event-context-menu">context-menu event documentation</a> for all the available properties.</p>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">var menu = new MenuItem[]
{
new MenuItem
{
Label = "Hello",
Click = async () => await Electron.Dialog.ShowMessageBoxAsync("Electron.NET rocks!")
},
new MenuItem { Type = MenuType.separator },
new MenuItem { Label = "Electron.NET", Type = MenuType.checkbox, Checked = true }
};
var mainWindow = Electron.WindowManager.BrowserWindows.First();
Electron.Menu.SetContextMenu(mainWindow, menu);
Electron.IpcMain.On("show-context-menu", (args) => {
Electron.Menu.ContextMenuPopup(mainWindow);
});</code></pre>
<h5>Renderer Process (JavaScript)</h5>
<pre><code class="javascript">const { ipcRenderer } = require("electron");
window.addEventListener('contextmenu', (e) => {
e.preventDefault()
ipcRenderer.send('show-context-menu');
}, false);</code></pre>
</div>
</div>
</div>
<script type="text/javascript">
(function(){
const { ipcRenderer } = require("electron");
const contextMenuBtn = document.getElementById('context-menu')
contextMenuBtn.addEventListener('click', function () {
ipcRenderer.send('show-context-menu');
})
window.addEventListener('contextmenu', (e) => {
e.preventDefault()
ipcRenderer.send('show-context-menu');
}, false);
}());
</script>
</section>
</template>

View File

@@ -0,0 +1,78 @@
<template class="task-template">
<section id="notifications-section" class="section js-section u-category-native-ui">
<header class="notifications">
<div class="section-wrapper">
<h1>
<svg class="section-icon"><use xlink:href="assets/img/icons.svg#icon-notification"></use></svg>
Desktop notifications
</h1>
<h3>The <code>Electron.Notification</code> in Electron.NET allows you to add basic desktop notifications.</h3>
<p>Electron conveniently allows developers to send notifications with the <a href="https://notifications.spec.whatwg.org/">HTML5 Notification API</a>, using the currently running operating systems native notification APIs to display it.</p>
<p><b>Note:</b> Since this is an HTML5 API it is only available in the renderer process.</p>
<p>You find the sample source code in <code>Controllers\NotificationsController.cs</code>.</p>
</div>
</header>
<div class="demo">
<div class="demo-wrapper">
<button id="basic-notification-demo-toggle" class="js-container-target demo-toggle-button">Basic notification
<div class="demo-meta u-avoid-clicks">Supports: Win 7+, macOS, Linux (that supports libnotify)<span class="demo-meta-divider">|</span> Process: Renderer</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="basic-noti">View demo</button>
</div>
<p>This demo demonstrates a basic notification. Text only.</p>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">var options = new NotificationOptions("Basic Notification", "Short message part")
{
OnClick = async () => await Electron.Dialog.ShowMessageBoxAsync("Notification clicked")
};
Electron.Notification.Show(options);</code></pre>
</div>
</div>
</div>
<div class="demo">
<div class="demo-wrapper">
<button id="advanced-notification-demo-toggle" class="js-container-target demo-toggle-button">Notification with image
<div class="demo-meta u-avoid-clicks">Supports: Win 7+, macOS, Linux (that supports libnotify) <span class="demo-meta-divider">|</span> Process: Renderer</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="advanced-noti">View demo</button>
</div>
<p>This demo demonstrates a basic notification. Both text and a image</p>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">var options = new NotificationOptions("Notification with image", "Short message plus a custom image")
{
OnClick = async () => await Electron.Dialog.ShowMessageBoxAsync("Notification clicked"),
Icon = "/assets/img/programming.png"
};
Electron.Notification.Show(options);</code></pre>
</div>
</div>
</div>
<script type="text/javascript">
(function(){
const { ipcRenderer } = require("electron");
document.getElementById("basic-noti").addEventListener("click", () => {
ipcRenderer.send("basic-noti");
});
document.getElementById("advanced-noti").addEventListener("click", () => {
ipcRenderer.send("advanced-noti");
});
}());
</script>
</section>
</template>

View File

@@ -0,0 +1,79 @@
<template class="task-template">
<section id="pdf-section" class="section js-section u-category-media">
<header class="section-header">
<div class="section-wrapper">
<h1>
<svg class="section-icon"><use xlink:href="assets/img/icons.svg#icon-media"></use></svg>
Print to PDF
</h1>
<h3>The <code>BrowserWindow</code> class in Electron.NET has a property, <code>WebContents</code>, which allows your app to print as well as print to PDF.</h3>
<p>You find the sample source code in <code>Controllers\PdfController.cs</code>.</p>
</div>
</header>
<div class="demo">
<div class="demo-wrapper">
<button id="print-pdf-demo-toggle" class="js-container-target demo-toggle-button">
Print to PDF
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="print-pdf">View Demo</button>
<span class="demo-response is-selectable" id="pdf-path"></span>
</div>
<p>To demonstrate the print to PDF functionality, the demo button above will save this page as a PDF and, if you have a PDF viewer, open the file.</p>
<p>In a real-world application you're more likely to add this to application menu, but for the purposes of the demo we've set it to the demo button.</p>
<h5>Renderer Process (JavaScript)</h5>
<pre><code class="javascript">const { ipcRenderer } = require("electron");
document.getElementById("print-pdf").addEventListener("click", () => {
ipcRenderer.send("print-pdf");
});</code></pre>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">Electron.IpcMain.On("print-pdf", async (args) => {
BrowserWindow mainWindow = Electron.WindowManager.BrowserWindows.First();
var saveOptions = new SaveDialogOptions
{
Title = "Save an PDF-File",
DefaultPath = await Electron.App.GetPathAsync(PathName.documents),
Filters = new FileFilter[]
{
new FileFilter { Name = "PDF", Extensions = new string[] { "pdf" } }
}
};
var path = await Electron.Dialog.ShowSaveDialogAsync(mainWindow, saveOptions);
if (await mainWindow.WebContents.PrintToPDFAsync(path))
{
await Electron.Shell.OpenExternalAsync("file://" + path);
}
else
{
Electron.Dialog.ShowErrorBox("Error", "Failed to create pdf file.");
}
});</code></pre>
<div class="demo-protip">
<h2>ProTip</h2>
<strong>Use a print style sheet.</strong>
<p>You can create a stylesheet targeting printing to optimize the look of what your users print. Below is the stylesheet used in this app, located in <code>assets/css/print.css</code>.</p>
</div>
</div>
</div>
</div>
<script>
(function(){
const { ipcRenderer } = require("electron");
document.getElementById("print-pdf").addEventListener("click", () => {
ipcRenderer.send("print-pdf");
});
}());
</script>
</section>
</template>

View File

@@ -0,0 +1,71 @@
<template class="task-template">
<section id="ex-links-file-manager-section" class="section js-section u-category-native-ui">
<header class="section-header">
<div class="section-wrapper">
<h1>
<svg class="section-icon"><use xlink:href="assets/img/icons.svg#icon-native-ui"></use></svg>
Open external links and the file manager
</h1>
<h3>The <code>Electron.Shell</code> in Electron.NET allows you to access certain native elements like the file manager and default web browser.</h3>
<p>This module works in both the main and renderer process.</p>
<p>You find the sample source code in <code>Controllers\ShellController.cs</code>.</p>
</div>
</header>
<div class="demo">
<div class="demo-wrapper">
<button id="open-file-manager-demo-toggle" class="js-container-target demo-toggle-button">
Open Path in File Manager
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Both</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="open-file-manager">View Demo</button>
</div>
<p>This demonstrates using the <code>Electron.Shell</code> to open the system file manager at a particular location.</p>
<p>Clicking the demo button will open your file manager at the root.</p>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">string path = await Electron.App.GetPathAsync(PathName.home);
await Electron.Shell.ShowItemInFolderAsync(path);</code></pre>
</div>
</div>
</div>
<div class="demo">
<div class="demo-wrapper">
<button id="open-ex-links-demo-toggle" class="js-container-target demo-toggle-button">
Open External Links
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Both</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="open-ex-links">View Demo</button>
</div>
<p>If you do not want your app to open website links <em>within</em> the app, you can use the <code>Electron.Shell</code> to open them externally. When clicked, the links will open outside of your app and in the user's default web browser.</p>
<p>When the demo button is clicked, the electron website will open in your browser.<p>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">await Electron.Shell.OpenExternalAsync("https://github.com/ElectronNET");</code></pre>
</div>
</div>
</div>
<script>
(function(){
const { ipcRenderer } = require("electron");
document.getElementById("open-file-manager").addEventListener("click", () => {
ipcRenderer.send("open-file-manager");
});
document.getElementById("open-ex-links").addEventListener("click", () => {
ipcRenderer.send("open-ex-links");
});
}());
</script>
</section>
</template>

View File

@@ -0,0 +1,74 @@
<template class="task-template">
<section id="shortcuts-section" class="section js-section u-category-menu">
<header class="section-header">
<div class="section-wrapper">
<h1>
<svg class="section-icon"><use xlink:href="assets/img/icons.svg#icon-menu"></use></svg>
Keyboard Shortcuts
</h1>
<h3>The <code>Electron.GlobalShortcut</code> and <code>MenuItem</code> can be used to define keyboard shortcuts.</h3>
<p>
In Electron.NET, keyboard shortcuts are called accelerators.
They can be assigned to actions in your application's Menu,
or they can be assigned globally so they'll be triggered even when
your app doesn't have keyboard focus.
</p>
<p>You find the sample source code in <code>Controllers\ShortcutsController.cs</code>.</p>
</div>
</header>
<div class="demo">
<div class="demo-wrapper">
<button id="shortcuts-demo-toggle" class="js-container-target demo-toggle-button">Register a global keyboard shortcut
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
</button>
<div class="demo-box">
<p>
To try this demo, press <kbd class="normalize-to-platform">CommandOrControl+Alt+K</kbd> on your keyboard.
</p>
<p>
Global shortcuts are detected even when the app doesn't have
keyboard focus.
</p>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">Electron.GlobalShortcut.Register("CommandOrControl+Alt+K", async () => {
var options = new MessageBoxOptions("You pressed the registered global shortcut keybinding.")
{
Type = MessageBoxType.info,
Title = "Success!"
};
await Electron.Dialog.ShowMessageBoxAsync(options);
});
Electron.App.WillQuit += (args) => Task.Run(() => Electron.GlobalShortcut.UnregisterAll());</code></pre>
<div class="demo-protip">
<h2>ProTip</h2>
<strong>Avoid overriding system-wide keyboard shortcuts.</strong>
<p>
When registering global shortcuts, it's important to be aware of
existing defaults in the target operating system, so as not to
override any existing behaviors. For an overview of each
operating system's keyboard shortcuts, view these documents:
</p>
<ul>
<li><a class="u-exlink" href="https://developer.apple.com/library/mac/documentation/UserExperience/Conceptual/OSXHIGuidelines/Keyboard.html">macOS</a></li>
<li><a class="u-exlink" href="http://windows.microsoft.com/en-us/windows-10/keyboard-shortcuts">Windows</a></li>
<li><a class="u-exlink" href="https://developer.gnome.org/hig/stable/keyboard-input.html.en">Linux</a></li>
</ul>
</div>
</div>
</div>
</div>
</section>
</template>

View File

@@ -0,0 +1,100 @@
<template class="task-template">
<section id="tray-section" class="section js-section u-category-native-ui">
<header class="section-header">
<div class="section-wrapper">
<h1>
<svg class="section-icon"><use xlink:href="assets/img/icons.svg#icon-native-ui"></use></svg>
Tray
</h1>
<h3>The <code>Electron.Tray</code> allows you to create an icon in the operating system's notification area.</h3>
<p>This icon can also have a context menu attached.</p>
<p>You find the sample source code in <code>Controllers\TrayController.cs</code>.</p>
</div>
</header>
<div class="demo">
<div class="demo-wrapper">
<button id="tray-demo-toggle" class="js-container-target demo-toggle-button">
Tray
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux | Process: Main</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="put-in-tray">View Demo</button>
<span class="demo-response" id="tray-countdown"></span>
</div>
<p>The demo button sends a message to the main process using the <code>ipcRenderer</code>. In the main process the app is told to place an icon, with a context menu, in the tray.</p>
<p>In this example the tray icon can be removed by clicking 'Remove' in the context menu or selecting the demo button again.</p>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">Electron.IpcMain.On("put-in-tray", (args) => {
if (Electron.Tray.Items.Count == 0)
{
var menu = new MenuItem
{
Label = "Remove",
Click = () => Electron.Tray.Destroy()
};
Electron.Tray.Show("/Assets/electron_32x32.png", menu);
Electron.Tray.SetToolTip("Electron Demo in the tray.");
}
else
{
Electron.Tray.Destroy();
}
});</code></pre>
<h5>Renderer Process (JavaScript)</h5>
<pre><code class="javascript">const { ipcRenderer } = require("electron");
let trayOn = false;
document.getElementById("put-in-tray").addEventListener("click", () => {
ipcRenderer.send("put-in-tray");
let message = '';
if(trayOn) {
trayOn = false;
} else {
trayOn = true;
message = 'Click demo again to remove.'
}
document.getElementById('tray-countdown').innerHTML = message;</code></pre>
<div class="demo-protip">
<h2>ProTip</h2>
<strong>Tray support in Linux.</strong>
<p>On Linux distributions that only have app indicator support, users will need to install <code>libappindicator1</code> to make the tray icon work. See the <a href="http://electron.atom.io/docs/api/tray">full API documentation<span class="u-visible-to-screen-reader">(opens in new window)</span></a> for more details about using Tray on Linux.</p>
</div>
</div>
</div>
</div>
<script>
(function(){
const { ipcRenderer } = require("electron");
let trayOn = false;
document.getElementById("put-in-tray").addEventListener("click", () => {
ipcRenderer.send("put-in-tray");
let message = '';
if(trayOn) {
trayOn = false;
} else {
trayOn = true;
message = 'Click demo again to remove.'
}
document.getElementById('tray-countdown').innerHTML = message;
});
}());
</script>
</section>
</template>

View File

@@ -0,0 +1,60 @@
<template class="task-template">
<section id="update-section" class="section js-section u-category-update">
<header class="section-header">
<div class="section-wrapper">
<h1>
<svg class="section-icon"><use xlink:href="assets/img/icons.svg#icon-update"></use></svg>
Update
</h1>
<h3>The <code>Electron.AutoUpdater</code> allows you to automatically update your application.</h3>
<p>To publish your updates you just need simple file hosting, it does not require a dedicated server.</p>
<p>You find the sample source code in <code>Controllers\UpdateController.cs</code>.</p>
</div>
</header>
<div class="demo">
<div class="demo-wrapper">
<button id="tray-demo-toggle" class="js-container-target demo-toggle-button">
Auto Update this App
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux | Process: Main</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="btn-update">View Demo</button>
<span class="demo-response" id="demo-reply"></span>
</div>
<p>The demo button call the <code>Electron.AutoUpdater.CheckForUpdatesAndNotifyAsync()</code> in the main process.</p>
<p>This will immediately download an update, then install in the background when the app quits.</p>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">Electron.IpcMain.On("auto-update", async (args) =>
{
var currentVersion = await Electron.App.GetVersionAsync();
var updateCheckResult = await Electron.AutoUpdater.CheckForUpdatesAndNotifyAsync();
var availableVersion = updateCheckResult.UpdateInfo.Version;
string information = $"Current version: {currentVersion} - available version: {availableVersion}";
var mainWindow = Electron.WindowManager.BrowserWindows.First();
Electron.IpcMain.Send(mainWindow, "auto-update-reply", information);
});
</code></pre>
</div>
</div>
</div>
<script>
(function(){
const { ipcRenderer } = require("electron");
document.getElementById("btn-update").addEventListener("click", () => {
ipcRenderer.send("auto-update");
});
ipcRenderer.on('auto-update-reply', (event, message) => {
document.getElementById('demo-reply').innerHTML = message;
});
}());
</script>
</section>
</template>

View File

@@ -0,0 +1,32 @@
<style>
body {
font-family: system, -apple-system, '.SFNSText-Regular', 'SF UI Text', 'Lucida Grande', 'Segoe UI', Ubuntu, Cantarell, sans-serif;
color: #fff;
background-color: #8aba87;
text-align: center;
font-size: 40px;
}
h2 {
padding: 0;
margin: 0;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
#close {
color: white;
opacity: 0.7;
position: absolute;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
font-size: 12px;
text-decoration: none;
}
</style>
<h2>Hello World!</h2>
<a id="close" href="javascript:window.close()">Close this Window</a>

View File

@@ -0,0 +1,201 @@
<template class="task-template">
<section id="windows-section" class="section js-section u-category-windows">
<header class="section-header">
<div class="section-wrapper">
<h1>
<svg class="section-icon"><use xlink:href="assets/img/icons.svg#icon-windows"></use></svg>
Create and Manage Windows
</h1>
<h3>The <code>Electron.WindowManager</code> in Electron.NET allows you to create a new browser window or manage an existing one.</h3>
<p>Each browser window is a separate process, known as the renderer process. This process, like the main process that controls the life cycle of the app, has full access to the .NET Core and Node.js APIs.
<p>You find the sample source code in <code>Controllers\WindowsController.cs</code>.</p>
</div>
</header>
<div class="demo">
<div class="demo-wrapper">
<button id="new-window-demo-toggle" class="js-container-target demo-toggle-button">
Create a new window
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="new-window">View Demo</button>
</div>
<p>The <code>Electron.WindowManager</code> gives you the ability to create new windows in your app. This main process can be used from the renderer process with the <code>Electron.IpcMain</code>, as is shown in this demo.</p>
<p>There are a lot of options when creating a new window. A few are in this demo, but visit the <a href="http://electron.atom.io/docs/api/browser-window">documentation<span class="u-visible-to-screen-reader">(opens in new window)</span></a> for the full list.</p>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">await Electron.WindowManager.CreateWindowAsync();</code></pre>
<div class="demo-protip">
<h2>ProTip</h2>
<strong>Use an invisible browser window to run background tasks.</strong>
<p>You can set a new browser window to not be shown (be invisible) in order to use that additional renderer process as a kind of new thread in which to run C# in the background of your app. You do this by setting the <code>show</code> property to <code>false</code> when defining the new window.</p>
<pre>
<code class="csharp">
public async void ElectronBootstrap()
{
var browserWindow = await Electron.WindowManager.CreateWindowAsync(new BrowserWindowOptions
{
Show = false
});
browserWindow.OnReadyToShow += () => browserWindow.Show();
}
</code></pre>
</div>
</div>
</div>
</div>
<div class="demo">
<div class="demo-wrapper">
<button id="manage-window-demo-toggle" class="js-container-target demo-toggle-button">
Manage window state
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="manage-window">View Demo</button>
<span class="demo-response" id="manage-window-reply"></span>
</div>
<p>In this demo we create a new window and listen for <code>OnMove</code> and <code>OnResize</code> events on it. Click the demo button, change the new window and see the dimensions and position update here, above.</p>
<p>There are a lot of methods for controlling the state of the window such as the size, location, and focus status as well as events to listen to for window changes. Visit the <a href="http://electron.atom.io/docs/api/browser-window">documentation<span class="u-visible-to-screen-reader">(opens in new window)</span></a> for the full list.</p>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">public IActionResult Index()
{
Electron.IpcMain.On("manage-window", async (args) => {
var browserWindow = await Electron.WindowManager.CreateWindowAsync();
browserWindow.OnMove += UpdateReply;
browserWindow.OnResize += UpdateReply;
});
return View();
}
private async void UpdateReply()
{
var browserWindow = Electron.WindowManager.BrowserWindows.Last();
var size = await browserWindow.GetSizeAsync();
var position = await browserWindow.GetPositionAsync();
string message = $"Size: {size[0]},{size[1]} Position: {position[0]},{position[1]}";
var mainWindow = Electron.WindowManager.BrowserWindows.First();
Electron.IpcMain.Send(mainWindow, "manage-window-reply", message);
}</code></pre>
</div>
</div>
</div>
<div class="demo">
<div class="demo-wrapper">
<button id="using-window-events-demo-toggle" class="js-container-target demo-toggle-button">
Window events: blur and focus
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="listen-to-window">View Demo</button>
<button class="demo-button disappear" id="focus-on-modal-window">Focus on Demo</button>
</div>
<p>In this demo, we create a new window and listen for <code>OnBlur</code> event on it. Click the demo button to create a new modal window, and switch focus back to the parent window by clicking on it. You can click the <i>Focus on Demo</i> button to switch focus to the modal window again.</p>
<h5>Main Process (C#)</h5>
<pre><code class="csharp">var mainBrowserWindow = Electron.WindowManager.BrowserWindows.First();
var browserWindow = await Electron.WindowManager.CreateWindowAsync();
browserWindow.OnFocus += () => Electron.IpcMain.Send(mainBrowserWindow, "listen-to-window-focus");
browserWindow.OnBlur += () => Electron.IpcMain.Send(mainBrowserWindow, "listen-to-window-blur");
Electron.IpcMain.On("listen-to-window-set-focus", (x) => browserWindow.Focus());</code></pre>
</div>
</div>
</div>
<div class="demo">
<div class="demo-wrapper">
<button class="js-container-target demo-toggle-button">
Create a frameless window
<div class="demo-meta u-avoid-clicks">Supports: Win, macOS, Linux <span class="demo-meta-divider">|</span> Process: Main</div>
</button>
<div class="demo-box">
<div class="demo-controls">
<button class="demo-button" id="frameless-window">View Demo</button>
</div>
<p>
A frameless window is a window that has no <a href="https://developer.mozilla.org/en-US/docs/Glossary/Chrome">"chrome"</a>,
such as toolbars, title bars, status bars, borders, etc. You can make a browser window frameless by setting
<code>Frame</code> to <code>false</code> when creating the window.
</p>
<h5>Main Process</h5>
<pre><code class="csharp">var options = new BrowserWindowOptions
{
Frame = false
};
await Electron.WindowManager.CreateWindowAsync(options);</code></pre>
<p>Windows can have a transparent background, too. By setting the <code>Transparent</code> option to <code>true</code>, you can also make your frameless window transparent:</p>
<pre>
<code class="csharp">var options = new BrowserWindowOptions
{
Frame = false,
Transparent = true
};
await Electron.WindowManager.CreateWindowAsync(options);</code></pre>
<p>
For more details, see the <a href="http://electron.atom.io/docs/api/frameless-window/">Frameless Window</a> documentation.
</p>
</div>
</div>
</div>
<script>
(function(){
const { ipcRenderer } = require("electron");
document.getElementById("new-window").addEventListener("click", () => {
ipcRenderer.send("new-window");
});
document.getElementById("manage-window").addEventListener("click", () => {
ipcRenderer.send("manage-window");
});
ipcRenderer.on("manage-window-reply", (sender, data) => {
document.getElementById("manage-window-reply").innerText = data;
});
document.getElementById("listen-to-window").addEventListener("click", () => {
ipcRenderer.send("listen-to-window");
});
document.getElementById('focus-on-modal-window').addEventListener("click", () => {
ipcRenderer.send("listen-to-window-set-focus");
});
ipcRenderer.on("listen-to-window-focus", (sender, data) => {
const focusModalBtn = document.getElementById('focus-on-modal-window');
focusModalBtn.classList.add('disappear');
focusModalBtn.classList.remove('smooth-appear');
});
ipcRenderer.on("listen-to-window-blur", (sender, data) => {
const focusModalBtn = document.getElementById('focus-on-modal-window');
focusModalBtn.classList.add('smooth-appear');
focusModalBtn.classList.remove('disappear');
});
document.getElementById('frameless-window').addEventListener("click", () => {
ipcRenderer.send("frameless-window");
});
}());
</script>
</section>
</template>