> CEF, ES6, Angular 2, TypeScript использование классов .Net Core. Создание кроссплатформенного GUI для .Net с помощью CEF
> CEF, Angular 2 использование событий классов .Net Core
Основная идея этих статей — создание кроссплатформенных приложений на CEF с использованием Angular 2 и .Net Core. Чтобы отвязаться от сервера, используем свежий WebPack и настроим на локальное использование файлов.
Для начала создадим package.json.
{
"name": "helloapp",
"version": "1.0.0",
"scripts": {
"start": "tsc && concurrently \"tsc -w\" \"lite-server\" ",
"lite": "lite-server",
"tsc": "tsc",
"tsc:w": "tsc -w"
},
"dependencies": {
"@angular/common": "~2.4.0",
"@angular/compiler": "~2.4.0",
"@angular/core": "~2.4.0",
"@angular/forms": "~2.4.0",
"@angular/http": "~2.4.0",
"@angular/platform-browser": "~2.4.0",
"@angular/platform-browser-dynamic": "~2.4.0",
"@angular/router": "~3.4.0",
"@angular/upgrade": "~2.4.0",
"angular-in-memory-web-api": "~0.2.4",
"babel-preset-es2015": "^6.22.0",
"babel-preset-es2016": "^6.22.0",
"babel-preset-react": "^6.23.0",
"bootstrap": "^3.3.7",
"core-js": "^2.4.1",
"css-loader": "^0.26.1",
"jquery": "^3.1.1",
"postcss-loader": "^1.3.0",
"raw-loader": "^0.5.1",
"reflect-metadata": "^0.1.9",
"rxjs": "^5.1.1",
"sass-loader": "^6.0.0",
"style-loader": "^0.13.1",
"systemjs": "^0.20.7",
"to-string-loader": "^1.1.5",
"ts-loader": "^2.0.0",
"typescript": "^2.1.6",
"webpack": "^2.2.0",
"webpack-fail-plugin": "^1.0.5",
"webpack-notifier": "^1.5.0",
"zone.js": "^0.7.7"
},
"devDependencies": {
"@types/core-js": "^0.9.35",
"@types/node": "^6.0.46",
"babel-core": "6.23.1",
"babel-eslint": "7.1.1",
"babel-loader": "6.3.0",
"babel-plugin-__coverage__": "^11.0.0",
"babel-polyfill": "^6.0.0",
"babel-preset-angular2": "^0.0.2",
"babel-preset-es2015": "^6.22.0",
"bootstrap-loader": "^2.0.0-beta.20",
"bootstrap-sass": "^3.3.7",
"chunk-manifest-webpack-plugin": "^1.0.0",
"concurrently": "^3.1.0",
"css-loader": "^0.26.1",
"css-to-string-loader": "^0.1.2",
"extract-text-webpack-plugin": "^2.0.0-rc.3",
"file-loader": "^0.10.0",
"html-webpack-plugin": "^2.28.0",
"jquery": "^3.1.1",
"lite-server": "^2.2.2",
"node-sass": "^4.5.0",
"resolve-url-loader": "^1.6.1",
"sass-loader": "^6.0.0",
"style-loader": "^0.13.1",
"typescript": "^2.1.5",
"url-loader": "^0.5.7"
}
}
Здесь собраны загрузчики, ссылки на необходимые файлы итд, которые были подобраны долгими исканиями.
Так же необходим tsconfig.json для компиляции TypeScript-файлов.
{
"compilerOptions": {
"target": "es6",
"module": "commonjs",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": true,
"suppressImplicitAnyIndexErrors": true,
"lib": [ "es6", "dom" ],
"types": [ "node" ],
"typeRoots": [
"node_modules/@types"
]
},
"exclude": [
"node_modules",
"wwwroot",
"**/*.spec.ts"
],
"compileOnSave": true
}
Теперь мы можем создать на основании package.json директорию node_modules с помощью команды npm install из директории приложения.
Прежде чем вызывать WebPack, нужно создать несколько файлов polyfills.ts.
import 'core-js/es6';
import 'core-js/es7/reflect';
require('zone.js/dist/zone');
if (process.env.ENV === 'production') {
// Production
} else {
// Development and test
Error['stackTraceLimit'] = Infinity;
require('zone.js/dist/long-stack-trace-zone');
}
Добавить в main.ts ссылки на Bootstap и JQuery:
import 'jquery';
import 'bootstrap-loader';
import 'bootstrap/dist/css/bootstrap.css';
import 'bootstrap/dist/css/bootstrap-theme.css';
Теперь нам доступны стили, glyphicons и тд. Теперь перейдем к самому главному, а именно webpack.config.js, на основании которого и будут собираться нужные нам файлы.
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require("extract-text-webpack-plugin");
var helpers = require('./helpers');
var babelOptions = {
"presets": [
"react",
[
"es2015",
{
"modules": false
}
],
"es2016"
]
};
module.exports = {
cache: true,
entry: {
polyfills: './app/polyfills.ts',
main: './app/main.ts',
vendor: [
'babel-polyfill'
]
},
output: {
path: './wwwroot',
filename: './scripts/[name].js',
chunkFilename: './scripts/[chunkhash].js',
publicPath: './'
},
module: {
rules: [{
test: /\.ts(x?)$/,
exclude: /node_modules/,
include: /app/,
use: [
{
loader: 'babel-loader',
options: babelOptions
},
{
loader: 'ts-loader'
}
]
}, {
test: /\.js$/,
exclude: /node_modules/,
include: /app/,
use: [
{
loader: 'babel-loader',
options: babelOptions
}
]
},
{ test: /\.html$/, loader: 'raw-loader', exclude: [helpers.root('app/index.html')] },
{ test: /\.(png|jpg|jpeg|gif|svg)$/, loader: 'url-loader', query: { limit: 25000 } },
{ test: /\.css$/, loader: 'css-to-string-loader!css-loader' },
{ test: /\.scss$/, loaders: ['style', 'css', 'postcss', 'sass'] },
{ test: /\.(woff2?|ttf|eot|svg)$/, loader: 'url-loader?limit=10000' },
{ test: /bootstrap\/dist\/js\/umd\//, loader: 'imports?jQuery=jquery' }
]
},
resolve: {
extensions: ['.ts', '.tsx', '.js','.css']
},
plugins: [
// Workaround for angular/angular#11580
new webpack.optimize.CommonsChunkPlugin({
name: ['app', 'vendor', 'polyfills']
}),
new HtmlWebpackPlugin({
template: 'app/index.html'
}),
new ExtractTextPlugin(
{
filename: 'styles.css',
disable: false,
allChunks: true
}
),
new webpack.ProvidePlugin({
jQuery: 'jquery',
$: 'jquery',
jquery: 'jquery'
})
]
};
Здесь собрано всё, чтобы использовать es6 и всё собиралось в несколько файлов в директории
'./wwwroot'. Теперь можно запустить webpack --config webpack.config.js, который и создаст нужные нам файлы.
Но нам придется подправить выходной index.html. Почему-то для:
<script type="text/javascript" src="././scripts/polyfills.js"></script>
<script type="text/javascript" src="././scripts/vendor.js">
</script><script type="text/javascript" src="././scripts/main.js"></script>
Добавляются лишние ./ — убрав ./, мы можем запустить index.html в любом браузере. Но нас интересует наш cefsimple.exe:
Укажем в адресе файла путь к index.html, например, d:\CEF\CefProgects\TestTypeScript\TestTypeScript\wwwroot\index.html
И Вуаля, мы работаем автономно, без Web-сервера. Если нужно подключиться к серверу, у нас есть все на .Net. Например, .Net Core, WCF и ODATA-клиенты.
Нужно добавить что я отказался от Routing в пользу NgSwitch
Так как в проект построен на использовании systemjs.config.js, то в нем нет поддержки require
который необходим для WebPack, что бы весь Html код интегрировался в js код.
Так как не нашел в TS условную компиляцию, для отладки необходимо закомментировать require
@Component({
selector: 'nav-menu',
template:require('./navmenu.component.html'),
styles: [require('./navmenu.component.css')]
// templateUrl: 'app/navmenu/navmenu.component.html',
// styleUrls: ['app/navmenu/navmenu.component.css']
})
И раскомментировать
// templateUrl: 'app/navmenu/navmenu.component.html',
// styleUrls: ['app/navmenu/navmenu.component.css']
исходники и программы и инструкцию использования можно найти в предыдущих статьях в самом низу.
Комментарии (12)
dmitry_pavlov
16.02.2017 12:19Статья по теме dotnet new angular and dotnet new react
Serginio1
16.02.2017 12:25Не совсем. Это ближе к ASP.NET Core, Angular 2, SignalR для чайников
Там кстати там WebPack без поддержки Es6
Хотя они все так или иначе пересекаются. Спасибо!
eletov
18.02.2017 10:46Сергей из исходников не очень понятно как запускать на отличных от windows платформах.
Serginio1
18.02.2017 11:22Да дистрибутивы CEF нужно скачать отсюда
Chromium Embedded Framework (CEF) Automated Builds
Кроме того использовать Net Core отсюда
.Net Core All downloads
Serginio1
18.02.2017 10:52Для этого нужно перекомпилировать под каждую платформу SefSimple
Плюс я не пробовал на Linux, то возможно надо будет изменить загрузку .Net Core
Вот моя первая статья Кроссплатформенное использование классов .Net из неуправляемого кода. Или аналог IDispatch на Linux
На просторах интернета было найдено решение: Hosting .NET Core Clr in your own process и simpleCoreCLRHost и
initializeCoreCLR createDelegate и
unixinterface
Суть подключения заключается в загрузке библиотеки coreclr.dll, получения нужных интерфейсов и запуск CLR Runtime Host.eletov
18.02.2017 13:29Ясно, спасибо, наверное стоит упомянуть в статье, что пример чисто теоритически кросс-платформен потому что построен на кросс-плафтформенных технологиях, но сама интеграция CEF и .NET Core у вас Windows only. Но в любом случае спасибо за статьи и ссылки, я недавно проектировал что-то похожее и остановился на electron для фронтэнда + .NET Core для интеграции с железом через DllImport, но вся коммуникация была IPC через WebSocket, т.е. приложение состоит из двух процессов. Так же рассматривали edge(для electron), но все же out of process в нашем случае лучше подошёл так как коммуникация простая(по типу событий) и вызовов немного + битность и стабильность процесса electron не страдала если приходится загружать нестабильные нативные DLL для различных железок от вендоров этих железок, так как это делает другой(.NET Core) процесс.
Serginio1
18.02.2017 13:46Спасибо! Суть то в том, что ты первый кто заинтересовался моими разработками.
Я один. При этом 1С ник, который решил интегрировать .Net в 1С. Ноги растут оттуда.
Я мог бы сделать и реальную кроссплатформенность, но главное показать как это работает.
Можно добавить использование JS объектов и функций на стороне .Net.
Что касается out of process каждый решает сам. Например по опыты в 1С приходится сочетать свои сборки и например тот же AngleSharp проще использовать на стороне 1С итд.
Можно использовать динамическую компиляцию итд.
Использовать out of process несколько сложнее.
Serginio1
18.02.2017 14:29Изначально я хотел делать аналог RPC. Его несложно сделать и на существующей модели. По аналогии с .Net Core, обмен с 1C по TCP/IP между различными устройствами
По сути внешне все остается тем же
Atreides07
Спасибо за работу. Жалко что мало плюсов набирают статьи. Но я внимательно слежу за вашими статьями и читаю с большим удовольствием. Пожалуйста, не бросайте.
Serginio1
Спасибо!