app.xaml
<Application.Resources>
<local:AppBootstrapper xmlns:local="clr-namespace:Kyms" x:Key="Bootstrapper" />
<local:LocalizedStrings xmlns:local="clr-namespace:Kyms" x:Key="LocalizedStrings"/>
<ResourceDictionary x:Key="CustomDictionary">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Styles/Styles.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
my resource in a Styles/Styles.xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006">
<Style x:Key="MyColorButton" TargetType="Button">
.......
</Style>
In ViewModel (use caliburn micro MVVM Pattern) if i call this code
App.Current.Resources.MergedDictionaries[0]
App.Current.Resources.MergedDictionaries.Count = 0
but not it works, why?
I'm resolved
in my AppBootstrapper.cs i'm insert
private void LoadDictionary()
{
var dictionaries = App.Current.Resources.MergedDictionaries;
dictionaries.Clear();
var themeStyles = new ResourceDictionary { Source = new Uri("/MyAssemblyName;component/Styles/Styles.xaml", UriKind.RelativeOrAbsolute) };
dictionaries.Add(themeStyles);
}
And it works!
Related
I'm new to Uno Platform and I need help adding Google Maps to my app. Since MapControl doesn't provide WASM support, it seems like I need to embedd a javascript component. This should be possible according to https://platform.uno/blog/how-to-embed-javascript-components-in-c-built-uno-webassembly-web-applications/ . But I can't seem to make it work.
I have a javascript file containing the following, taken directly from the official google maps site. Also not sure where to put the map API key.
// Initialize and add the map
function initMap() {
// The location of Uluru
const uluru = { lat: -25.344, lng: 131.036 };
// The map, centered at Uluru
const map = new google.maps.Map(document.getElementById("map"), {
zoom: 4,
center: uluru,
});
// The marker, positioned at Uluru
const marker = new google.maps.Marker({
position: uluru,
map: map,
});
}
I also have this class, tried doing it similar to part 3 in the official Uno Platform Embedd Javascript Components guide.
public class GoogleMapsController : FrameworkElement
{
public GoogleMapsController()
{
LoadJavaScript();
}
private async void LoadJavaScript()
{
await this.ExecuteJavascriptAsync("initMap()");
}
}
To display the map in the xaml page:
<local:GoogleMapsController x:Name="googlemaps" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="4" />
When I run the app the map doesn't show up. Does anyone know what I am doing wrong?
The following shows up in the web console
fail: Windows.UI.Core.CoreDispatcher[0]
dotnet.js:1 Dispatcher unhandled exception
dotnet.js:1 System.ApplicationException
dotnet.js:1 at TestMaps.GoogleMapsController.LoadJavaScript () <0x2f90540 + 0x000e6> in <filename unknown>:0
dotnet.js:1 at System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.<ThrowAsync>b__7_0 (System.Object state) <0x2f9ba10 + 0x00010> in <filename unknown>:0
dotnet.js:1 at Windows.UI.Core.CoreDispatcherSynchronizationContext+<>c__DisplayClass3_0.<Post>b__0 () <0x2f9b9d0 + 0x00014> in <filename unknown>:0
dotnet.js:1 at Windows.UI.Core.CoreDispatcher.DispatchItems () <0x2a8a0a0 + 0x001e6> in <filename unknown>:0
If I change the LoadJavaScript() to synchronous in GoogleMapsController, the following shows up in the web console.
dotnet.js:1 Error #1 "ReferenceError: google is not defined" executing javascript: "
put_char # dotnet.js:1
dotnet.js:1 (function(element) {
put_char # dotnet.js:1
dotnet.js:1 initMap()
put_char # dotnet.js:1
dotnet.js:1 })(Uno.UI.WindowManager.current.getView(38002));
put_char # dotnet.js:1
dotnet.js:1 "
Got it working, I will probably write a blogpost on it later.
Get Google Maps API key
You can follow any tutorial available on the web on how to get an API key via the Google Developer Console. It involves creating a project, adding required billing information, creating API key and then enabling Google Maps API for it. Make sure to restrict the API key when you plan to go public so that someone does not misuse your key for their purposes and use up your billing this way.
Add a custom index.html to your WASM project
Because Google Maps requires loading an external JavaScript library, it is useful to load it ahead of time when your app first loads. to do that, add a index.html file to the root of your WASM project with the following content:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<script type="text/javascript" src="./require.js"></script>
<script type="text/javascript" src="./mono-config.js"></script>
<script type="text/javascript" src="./uno-config.js"></script>
<script type="text/javascript" src="./uno-bootstrap.js"></script>
<script async type="text/javascript" src="./dotnet.js"></script>
<!-- Google Maps libs here: -->
<script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
<script type="text/javascript" src="https://maps.googleapis.com/maps/api/js?key=MYAPIKEY&libraries=&v=weekly"></script>
$(ADDITIONAL_CSS)
$(ADDITIONAL_HEAD)
</head>
<body>
<div id="uno-body" class="container-fluid uno-body">
<div class="uno-loader"
loading-position="bottom"
loading-alert="none">
<!-- Logo: change src to customize the logo -->
<img class="logo"
src=""
title="Uno is loading your application" />
<progress></progress>
<span class="alert"></span>
</div>
</div>
<noscript>
<p>This application requires Javascript and WebAssembly to be enabled.</p>
</noscript>
</body>
</html>
Note that most of the code is boilerplate that is the default in Uno WASM projects (see here). Replace MYAPIKEY with your API key from developer console.
Now you need to instruct the WASM bootstrapper to use this custom index.html. Double-click the .csproj in the Solution Explorer and add the following:
<PropertyGroup>
<WasmShellIndexHtmlPath>index.html</WasmShellIndexHtmlPath>
</PropertyGroup>
Create custom Maps element
Similarly to the tutorial in docs, I created the following element:
#if __WASM__
using System;
using Uno.UI.Runtime.WebAssembly;
using Windows.UI;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Media;
namespace UnoWasmGoogleMaps
{
[HtmlElement("div")]
public class GoogleMapView : FrameworkElement
{
private readonly string _mapObjectName = "map_" + Guid.NewGuid().ToString().Replace("-", "");
public GoogleMapView()
{
Background = new SolidColorBrush(Colors.Transparent);
LoadMap();
}
private void LoadMap()
{
var javascript = $#"var {_mapObjectName} = new google.maps.Map(element, {{ center: {{lat: -34.397, lng: 150.644}}, zoom: 8 }});";
this.ExecuteJavascript(javascript);
}
}
}
#endif
Note I am using a custom unique variable name to store the map object created by Google Maps API. You can use this variable name to manipulate the map and call some functions on it.
Use the element
Finally, you can use the element in your app like this:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<local:GoogleMapView />
</Grid>
Result
Can you please help me with following code?
In Webkit family, I see the shadow element (in developer tools), but doesn't appear at all on screen.
In Firefox, it crashes!
I suspect namespaces, because it works great for HTML elements like HTMLDivElement.
<?xml version="1.0" encoding="UTF-8"?>
<html>
<script>
var proto = Object.create(SVGTSpanElement.prototype);
proto.attachedCallback = function() {
if (!this.shadowRoot) this.createShadowRoot();
this.shadowRoot.appendChild(document.createTextNode('superb!!!!!'));
}
var XLang = document.registerElement('lang-tspan', { prototype: proto, extends: 'tspan' });
</script>
<svg width="300" height="100"><text x="10" y="20">This is <tspan is="lang-tspan">my substitution</tspan></text></svg>
</html>
I wanted to add the statusbar plugin, so I can change the android's statusbar color. The only problem is: it doesnt work. I've tried several things, but it still don't work.
This is my HTML, as you can see I tried several things.
<html>
<head>
<script>
function color() {
StatusBar.backgroundColorByHexString("#ECF0F1");
}
function colorc() {
if (StatusBar.isVisible) {
document.getElementById("p").style.color = "green";
}
else {
document.getElementById("p").style.color = "red";
}
}
function changer() {
document.getElementById("p").style.color = "pink";
}
</script>
</head>
<body onload="color();">
<p id="p">hi</p>
<button onclick="StatusBar.hide();">hide</button>
<button onclick="StatusBar.show();">show</button>
<button onclick="color();">color</button>
<button onclick="colorc();">color check</button>
<button onclick="changer();">hi</button>
</body>
</html>
And this is my config
<widget xmlns="http://www.w3.org/ns/widgets"
xmlns:gap="http://phonegap.com/ns/1.0" id="com.not.working.bar"
version="1.0.0">
<name>Statusbar</name>
<description></description>
<gap:plugin name="org.apache.cordova.statusbar" version="0.1.4"
source="pgb"/>
<preference name="StatusBarBackgroundColor" value="#ECF0F1" />
<preference name="phonegap-version" value="3.0.0"/>
<preference name="orientation" value="default"/>
<preference name="target-device" value="universal"/>
<preference name="android-minSdkVersion" value="21"/>
<preference name="android-installLocation" value="auto"/>
<access origin="*"/>
</widget>
The only thing that doesn't work is the color. I can hide/show the bar, check if it's showing or hiding, but I can't manage to change the color. PLEASE HELP
https://github.com/apache/cordova-plugin-statusbar
Use version 1.0+ of the StatusBar plugin, like this: cordova plugin add https://github.com/apache/cordova-plugin-statusbar. You can check the version by cordova plugin ls.
When you add the StatusBar plugin as cordova plugin add org.apache.cordova.statusbar then you get v0.1.10. You can see in this file#0.1.10 that none of the color methods are implemented on Android. On the other hand #1.0.0 the backgroundColorByHexString is implemented.
I have a WPF desktop application. In it I have a menu option that shows a help window. The help window simply wraps a System.Windows.Controls.WebBrowser control.
Now I can create a simple HTML page to display my help content. So far this all works fine.
The problem comes about when I try to include an image, such as my company's logo with an "img" tag in the HTML file. The logo is embedded as a resource in the executable.
Can anyone tell me how to get an embedded image onto an HTLM page?
The following does NOT work:
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<table style="width: 100%;">
<tr>
<td>
</td>
<td style="align-content:center">
<img alt="My Logo" src="../Images/MyLogo_3115-2935.jpg" width="500"/>
</td>
<td> </td>
</tr>
</table>
</body>
</html>
Specifying an absolute path to the image will work. For example, in the HTML
<img alt="My Logo" src="file:///C:/Images/MyLogo_3115-2935.jpg" width="500"/>
This solution would not work with an application resource. You would have to create a file locally from the resource, at least temporarily. Or alternatively copy the file locally with the app.
Another, more complex solution would be to embed a webserver into your application, or somewhere else that responds to web request. For more information see HttpListener
I thank user2509738 for his reply, however, I wanted to use an image that was embedded instead of deploying a separate file. I came up with my own solution and I'll post it here for completeness.
I used the HtmlAgilityPack found at http://htmlagilitypack.codeplex.com to make parsing a little easier.
Here is the HelpWindow.Xaml
<Window x:Class="POC_WpfHelpWindowUsingHtml.HelpWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="HelpWindow" Height="600" Width="800">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<WebBrowser x:Name="helpBrowser"/>
<StackPanel x:Name="controlPanel"
Grid.Row="1"
Orientation="Horizontal"
HorizontalAlignment="Right">
<Button x:Name="closeButton"
Margin="0,10,20,10" Width="100"
Content="Close"
Click="closeButton_Click"/>
</StackPanel>
</Grid>
</Window>
Here is the HTML content in the form of a file: HelpContent.html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
<table style="width: 100%;">
<tr>
<td>
</td>
<td style="align-content:center">
<img id="imgMyLogo" alt="My Logo" src="Images/MyLogo.png" width="100"/>
</td>
<td> </td>
</tr>
</table>
</body>
</html>
Here is the HelpWindow.xaml.cs file with most of the magic
using System;
using System.IO;
using System.Windows;
using HtmlAgilityPack;
using System.Reflection;
namespace POC_WpfHelpWindowUsingHtml
{
/// <summary>
/// Interaction logic for HelpWindow.xaml
/// </summary>
public partial class HelpWindow : Window
{
public HelpWindow()
{
InitializeComponent();
Uri uri = new Uri(#"/HelpContent.html", UriKind.Relative);
System.Windows.Resources.StreamResourceInfo info
= System.Windows.Application.GetContentStream(uri);
Stream streamOriginalPage = info.Stream;
Stream streamModifiedPage = new MemoryStream();
StreamWriter streamWriter = new StreamWriter(streamModifiedPage);
// this is from HtmlAgilityPack
HtmlDocument doc = new HtmlDocument();
doc.Load(streamOriginalPage);
// find all the img elements
foreach (HtmlNode node in doc.DocumentNode.SelectNodes("//img"))
{
// change the image reference to an embedded string
string src = node.GetAttributeValue("src", "");
src = ConvertImageToEmbeddedString(src);
if (!string.IsNullOrWhiteSpace(src))
{
node.SetAttributeValue("src", src);
}
}
// save the changes
doc.Save(streamWriter);
streamWriter.Flush();
streamModifiedPage.Position = 0;
// send the Html content to the WebBrowser component
helpBrowser.NavigateToStream(streamModifiedPage);
}
private void closeButton_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
private string ConvertImageToEmbeddedString(string source)
{
// find the image type
string imageType = "png";
if (source.ToLower().IndexOf("jpg") > -1) imageType = "jpg";
if (source.ToLower().IndexOf("ico") > -1) imageType = "ico";
// initialize the return value
string ret = "";
if (source.Length > 4)
{
if (!source.StartsWith("/"))
{
source = "/" + source;
}
// get the embedded image as a stream
Stream stream = Application.GetResourceStream(
new Uri("pack://application:,,," + source)).Stream;
if (stream != null)
{
// put the image into an Image object
System.Drawing.Image image = System.Drawing.Image.FromStream(stream);
if (image != null)
{
// prepend the text for Data URI scheme
ret = "data:image/" + imageType + ";base64,";
using (MemoryStream ms = new MemoryStream())
{
// convert the image into a MemoryStream
image.Save(ms, image.RawFormat);
// convert the image to base64 encoding
ret += Convert.ToBase64String(ms.ToArray());
}
}
}
}
return ret;
}
}
}
I copied the following code from a tutorial, but still couldn't figure out whether I made a mistake somewhere or whether it has to do with the browser support. Thanks in advance for pointing out to me on what happened!
<html>
<head>
<script type="text/javascript">
function loadXMLDoc(dname)
{
if(window.XMLHttpRequest)
{
xhttp = new XMLHttpRequest();
}
else
{
xttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xhttp.open("GET", dname, false);
xhttp.send();
return xhttp.responseXML;
}
function change(text)
{
var xmlDoc = loadXMLDoc("dom.xml");
var x = xmlDoc.getElementsByTagName("title")[0].childNodes[0];
x.nodeValue = text;
var y = xmlDoc.getElementsByTagName("title");
for(i=0; i<y.length; i++)
{
document.write(y[i].childNodes[0].nodeValue+"<br />");
}
}
function remove(node)
{
xmlDoc = loadXMLDoc("dom.xml");
var y = xmlDoc.getElementsByTagName(node)[0];
xmlDoc.documentElement.removeChild(y);
alert("The element "+node+" has been removed!");
}
function prove(u)
{
var x = xmlDoc.getElementsByTagName(u);
for (i=0; i<x.length; i++)
{
document.write(x[i].childNodes[0].nodeValue);
document.write("<br />");
}
</script>
</head>
<body>
<input type="button" value="remove" onclick="remove('book')" />
<input type="button" value="prove it" onclick="prove('book')" />
</body>
</html>
------------Update-------------------------
Here's an XML file that may help:
<bookstore>
<book category="cooking">
<title lang="en">Everyday Italian</title>
<author>Giada</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="cooking">
<title lang="en">Book 2</title>
<author>Giada</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="cooking">
<title lang="en">Book 3</title>
<author>Giada</author>
<year>2005</year>
<price>30.00</price>
</book>
</bookstore>
I think the problem might be because of document.write
Any document.write statement that runs after the page finishes loading
will create a new page and overwrite all of the content of the current
page.
Also there is a } missing after the for statement of prove function
Try using innerHTML on a div or some html element to overcome this issue.
Other than that I don't find any issues with your code
The script is looking for a file named "dom.xml"
If you put that file in the same directory as the page above, you'll get different results.
It looks like you are missing a bracket for the last function prove or the for loop.
Also you may want to declare
var xmlDoc = loadXMLDoc("dom.xml");
outside of each function, or add it to prove()