Loading Local HTML with WebView Xamarin Forms - c#

I am trying to load a local HTML page in a webview with Xamarin forms.
I am using the basic example in the dev docs although I can get a URL to load I can't get my own HTML pages to load. This only needs to be done through Android so there is no worries about about IOS and Windows.
The Xaml:
<WebView
x:Name="webviewjava"></WebView>
The code behind:
public partial class javscriptExample : ContentPage
{
public interface IBaseUrl { string Get(); }
public javscriptExample()
{
InitializeComponent();
var source = new HtmlWebViewSource();
source.BaseUrl = DependencyService.Get<IBaseUrl>().Get();
webviewjava.Source = source;
}
}
The platform specific file (LocalFile.cs):
Just to note this has been set as an Android asset.
[assembly: Dependency(typeof(LocalFiles))]
namespace maptesting.Droid
{
public class LocalFiles: IBaseUrl
{
public string Get()
{
return "file:///android_asset/";
}
}
}
and under the asset's folder there is a 'TestWebPage.html', also set as an Android asset.
Although I dont know what the problem is I have put it through debug and the base url is coming back blank. Just to be clear im not getting a file not found, the screen is simply blank.
Also, and Im not sure if this makes a difference. There is no syntax highlighting on 'IBaseUrl' in the LocalFiles.cs file. So I'm not sure if it can 'see' it.
Any ideas?

I am also suffering with the same issue,but I resolved in the following way
Use "UrlWebViewSource" instead of "HtmlWebViewSource"
var urlSource = new UrlWebViewSource();
string baseUrl = DependencyService.Get<IWebViewBaseUrl>().GetBaseUrl();
string filePathUrl = Path.Combine(baseUrl, "imprint.html");
urlSource.Url = filePathUrl;
WebBrowser.Source = urlSource;

You must check the file properties for Build Action = BundleResource
Try this code to load local html file
var source = new HtmlWebViewSource();
string url = DependencyService.Get<IBaseUrl>().GetBaseUrl();
string TempUrl = Path.Combine(url, "terms.html");
source.BaseUrl = url;
string html;
try
{
using (var sr = new StreamReader(TempUrl))
{
html = sr.ReadToEnd();
source.Html = html;
}
}
catch(Exception ex){
Console.WriteLine(ex.Message);
}
Implementations of the interface for each platform must then be provided
iOS
[assembly: Dependency(typeof(BaseUrl))]
namespace yournamespace
{
public class BaseUrl: IBaseUrl
{
public string GetBaseUrl()
{
return NSBundle.MainBundle.BundlePath;
}
}
}
Android
[assembly: Dependency (typeof(BaseUrl))]
namespace yournamespace {
public class BaseUrl_Android : IBaseUrl {
public string Get() {
return "file:///android_asset/";
}
}
}

WebView.BaseUrl only tells the WebView where to start looking for files. It's the root folder of the "web site". By default browsers will load the file index.html, so if you rename your file to index.html I believe it should load automatically.
I think this should be possible too:
webviewjava.BaseUrl = DependencyService.Get<IBaseUrl>().Get();
webviewjava.Source = "TestWebPage.html";
Here you're saying "use this location as the default place to look for files" and "look up this file and use it as the source for the HTML".

This is an old post but It may help someone looking to implement with Android, iOS and UWP with just one HTML file. With this approach you only use one HTML file for all platforms.
https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/files?tabs=vsmac#loading-files-embedded-as-resources

Im not sure if this counts but I found a work around. Instead of taking the above route I simply did this:
webviewjava.Source = "file:///android_asset/TestWebPage.html";
in the code behind, and just left out the IBaseUrl call altogether.
This works as its supposed to.

Related

Android Assets folder does not get recognized

I am doing a Xamarin Forms project with iOS and Android, and I am trying to retrieve a txt file from the assets folder for Android and from the Resources folder in iOS. It works for iOS but it doesn't work for Android. To be exact, in the Android project, the path for my text file is "Assets/versions/latestVersion.txt"
I have this Base URL for Android:
[assembly: Dependency(typeof(BaseUrl_Android))]
namespace WorkingWithWebview.Android
{
public class BaseUrl_Android : IBaseUrl
{
public string Get()
{
return "file:///android_asset/";
}
}
}
Now, in my code, I have:
string versioningFile = Xamarin.Forms.DependencyService.Get<IBaseUrl>().Get();
string path = Path.Combine(versioningFile, "versions/latestVersion.txt");
string file = File.ReadAllText(path);
My IBaseUrl is
public interface IBaseUrl { string Get(); }
It doesn't recognize the path, the error states "Could not find a part of the path /file:/android_asset/versions/latestVersion.txt".
What is the problem ? The path is clearly what i want it to be.
About Assete,it's read using an AssetManager.
You could also use File System Helpers of Xamarin.Essentials.
using (var stream = await FileSystem.OpenAppPackageFileAsync(fileName))
{
using (var reader = new StreamReader(stream))
{
var fileContents = await reader.ReadToEndAsync();
}
}
Note: Add any file into the Assets folder in the Android project and mark the Build Action as AndroidAsset

Xamarin Forms Webview Local Html src doesn't load every images from URL

I am building an application that uses the Xamarin.Forms WebView. Everything works fine on Android, iOS and UWP. But sometimes there is an image in the HtmlSource of the WebView that doesn't load. I can't find out what the problem is.
I'm trying to show this html in the WebView:
<p><img src="https://www.loi.nl/~/media/images/logos/vakgebieden/ipma.jpg" alt="Img did not load" /></p>
This code works fine on UWP and shows the correct image. But when running the exact same code on Android or iOS, the image doesn't show up.
I can't find out why the WebView doesn't load this image on Android or iOS.
These images DID load succesful on every platform: https://www.rabobank.nl/static/generic/css/images/s14/rabobank-logo.png
http://www.learnit.nl/static/nl/gratiscursus/photoshop/8/ph_07.jpg
https://www.volkswagen.nl/~/media/Volkswagen/Images/Modellen/nieuwe%20up/up-hero-menu2.ashx?bc=White&as=0&h=310&w=860
http://www.volkswagen.nl/~/media/Volkswagen/Images/Modellen/nieuwe%20up/up-hero-menu2.ashx?bc=White&as=0&h=310&w=860
The code I use to show the WebView:
var browser = new WebView();
var htmlSource = new HtmlWebViewSource();
htmlSource.Html = #"<html><head> <link rel=""stylesheet"" href=""adefault.css""> </head><body>" + c.HtmlSource + "</body></html>";
browser.Source = htmlSource;
htmlSource.BaseUrl = DependencyService.Get<IBaseURL>().Get();
browser.VerticalOptions = LayoutOptions.FillAndExpand;
browser.HorizontalOptions = LayoutOptions.FillAndExpand;
browser.WidthRequest = 100;
browser.HeightRequest = 1000;
The c.HtmlSource in this case is the code containing the <p> showed at this start of the question.
Can somebody tell me why this specific image is not loading in the Xamarin.Forms WebView on the Android and iOS platform?
The code of the class that implements IBaseURL on iOS:
[assembly: Dependency (typeof (BaseUrl_iOS))]
namespace LOI.iOS
{
public class BaseUrl_iOS : IBaseURL
{
public string Get()
{
return NSBundle.MainBundle.BundlePath;
}
}
}
The code of the class that implements IBaseURL on Android:
[assembly: Dependency (typeof (BaseUrl_Android))]
namespace LOI.Droid
{
public class BaseUrl_Android : IBaseURL
{
public string Get()
{
return "file:///android_asset/";
}
}
}
The IBaseURL interface:
public interface IBaseURL
{
string Get();
}

Unity Cloud Build: post export method

Problem:
I can't seem to figure out the right signature for Unity cloud build's post export method. According to the documentation:
The fully-qualified name of a public static method you want us to call
after we finish the Unity build process (but before Xcode). For
example: ClassName.CoolMethod or NameSpace.ClassName.CoolMethod. No
trailing parenthesis, and it can't have the same name as your
Pre-Export method! This method must accept a string parameter, which
will receive the path to the exported Unity player (or Xcode project
in the case of iOS).
Here is my code:
public static void OnPostprocessDevBuildIOS(string ExportPath)
{
var projPath = ExportPath + "/Unity-iPhone.xcodeproj/project.pbxproj";
var proj = new PBXProject();
var nativeTarget =
proj.TargetGuidByName(PBXProject.GetUnityTargetName());
var testTarget =
proj.TargetGuidByName(PBXProject.GetUnityTestTargetName());
string[] buildTargets = {nativeTarget, testTarget};
proj.ReadFromString(File.ReadAllText(projPath));
proj.SetBuildProperty(buildTargets, "ENABLE_BITCODE", "NO");
File.WriteAllText(projPath, proj.WriteToString());
}
and here is the error:
I've tried multiple test method signatures and can't seem to get anything to work. I've even tried just a method that logs out the path.
Additional Information:
Unity Version: 5.3.1f
Unity Cloud Build: 5.3.1f
Target: iOS 8.0+
Also, my cloud build settings script is located in the editor folder as required.
Ok so I got the the bitCode disabling post process to work with the following code, but only when I build manually. When I build from cloud build, with no error the app freezes at the splash screen. When I build from my local machine, the app runs just fine.
[PostProcessBuild]
public static void OnPostprocessBuild(BuildTarget buildTarget, string path)
{
if (buildTarget == BuildTarget.iOS)
{
string projPath = path + "/Unity-iPhone.xcodeproj/project.pbxproj";
PBXProject proj = new PBXProject();
proj.ReadFromString(File.ReadAllText(projPath));
string nativeTarget = proj.TargetGuidByName(PBXProject.GetUnityTargetName());
string testTarget = proj.TargetGuidByName(PBXProject.GetUnityTestTargetName());
string[] buildTargets = new string[]{nativeTarget, testTarget};
proj.SetBuildProperty(buildTargets, "ENABLE_BITCODE", "NO");
File.WriteAllText(projPath, proj.WriteToString());
}
}
I too had the same issue "splash screen stuck" right after launch....
I solved this issue. Please use the below code.
Tested in Unity 5.4.1p2 and Xcode 7.3.
using UnityEngine;
using System.Collections;
using UnityEditor;
using System.IO;
using UnityEditor.Callbacks;
#if UNITY_IOS
using UnityEditor.iOS.Xcode;
#endif
public class Postprocessor : AssetPostprocessor
{
#if UNITY_IOS
[PostProcessBuild]
public static void OnPostprocessBuild(BuildTarget buildTarget, string path)
{
if (buildTarget == BuildTarget.iOS)
{
string projPath = path + "/Unity-iPhone.xcodeproj/project.pbxproj";
PBXProject proj = new PBXProject();
proj.ReadFromString(File.ReadAllText(projPath));
string target = proj.TargetGuidByName("Unity-iPhone");
proj.SetBuildProperty(target, "ENABLE_BITCODE", "false");
File.WriteAllText(projPath, proj.WriteToString());
// Add url schema to plist file
string plistPath = path + "/Info.plist";
PlistDocument plist = new PlistDocument();
plist.ReadFromString(File.ReadAllText(plistPath));
// Get root
PlistElementDict rootDict = plist.root;
rootDict.SetBoolean("UIRequiresFullScreen",true);
plist.WriteToFile(plistPath);
}
}
#endif
}
In fact OnPostprocessBuild is always called, so you don't have to put anything in post export method field, which is designed for more specific methods.

BundleTransformer.Less inject variables depending on context/request

We would like the use the bundling mechanism of System.Web.Optimization in combination with the Less transformer.
The problem is that the same application/server serves pages for different branded websites. So depending on the 'SiteContext' the same .less files are used but different values should be used by the .less variables. So we want the (re)use the same less files but with different variables depending on the context of the request.
I tried a couple of different theories:
In all 3 cases I setup different bundles depending on the SiteContext.
1 inject an #import directive with the themed variables by using a custom VirtualPathProvider that intercepts the variables.less file.
So I have:
the styling file eg: header.less (imports the variables file)
the variables file: variables.less
a themed variables file: variables-theme.less (injected in variables.less via the VirtualPathProvider)
This is not working because the BundleTransformer cache sees this as the same file and doesn't know about the SiteContext. The cache key is based on the Url of the IAsset and we cannot influence this behavior.
2 Replace the variables.less import by variables-themed.less with an custom transformer that runs before the Less transformer.
Again no luck, same caching issues.
And as a side effect, the extra transformer was not called in debug because the assets are not bundled but called individually by the LessAssetHandler. This could be solved by writing your own AssetHandler that calls all required transformers.
3 create themed Asset names that are resolved by a custom VirtualPathProvider
Eg. Add header-themeX.less to the bundle, this file doesn't exist but you resolve this file to header.less and use method 2 to set the correct variables file import. (replace the import of the variables.less to the themed version).
Once again no luck. I think this could solve the caching issue if it wasn't for the Bundle.Include(string virtualPath) that does a File.Exists(path) internally. It doesn't pass via the CustomVirtualPathProvider.
Am I looking to deep to solve this?
All ideas are welcome, I can imagine that this will become a problem to more and more people as the System.Web.Optimization library gets more popular...
Keep in mind that:
we have a lot of .less/css files
we will have 5 or so themes
we like to keep things working in visual studio (that is why header.less has a ref. to variables.less)
Thanks for any feedback.
Michael!
You use the Microsoft ASP.NET Web Optimization Framework and the Bundle Transformer in multi-tenant environment, so you need to replace some components of the System.Web.Optimization and create own versions of the debugging HTTP-handlers (see «Problem: LESS file imports are added to BundleResponse.Files collection» discussion). As far as I know, Murat Cakir solve all these problems in the SmartStore.NET project.
In the Bundle Transformer there are 2 ways to inject of LESS-variables:
Look a properties GlobalVariables and ModifyVariables of LESS-translator:
using System.Collections.Generic;
using System.Web.Optimization;
using BundleTransformer.Core.Builders;
using BundleTransformer.Core.Orderers;
using BundleTransformer.Core.Transformers;
using BundleTransformer.Core.Translators;
using BundleTransformer.Less.Translators;
public class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
var nullBuilder = new NullBuilder();
var nullOrderer = new NullOrderer();
var lessTranslator = new LessTranslator
{
GlobalVariables = "my-variable='Hurrah!'",
ModifyVariables = "font-family-base='Comic Sans MS';body-bg=lime;font-size-h1=50px"
};
var cssTransformer = new CssTransformer(new List<ITranslator>{ lessTranslator });
var commonStylesBundle = new Bundle("~/Bundles/BootstrapStyles");
commonStylesBundle.Include(
"~/Content/less/bootstrap-3.1.1/bootstrap.less");
commonStylesBundle.Builder = nullBuilder;
commonStylesBundle.Transforms.Add(cssTransformer);
commonStylesBundle.Orderer = nullOrderer;
bundles.Add(commonStylesBundle);
}
}
Create a custom item transformation:
using System.Text;
using System.Web.Optimization;
public sealed class InjectContentItemTransform : IItemTransform
{
private readonly string _beforeContent;
private readonly string _afterContent;
public InjectContentItemTransform(string beforeContent, string afterContent)
{
_beforeContent = beforeContent ?? string.Empty;
_afterContent = afterContent ?? string.Empty;
}
public string Process(string includedVirtualPath, string input)
{
if (_beforeContent.Length == 0 && _afterContent.Length == 0)
{
return input;
}
var contentBuilder = new StringBuilder();
if (_beforeContent.Length > 0)
{
contentBuilder.AppendLine(_beforeContent);
}
contentBuilder.AppendLine(input);
if (_afterContent.Length > 0)
{
contentBuilder.AppendLine(_afterContent);
}
return contentBuilder.ToString();
}
}
And register this transformation as follows:
using System.Web.Optimization;
using BundleTransformer.Core.Orderers;
using BundleTransformer.Core.Bundles;
public class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
var nullOrderer = new NullOrderer();
const string beforeLessCodeToInject = #"#my-variable: 'Hurrah!';";
const string afterLessCodeToInject = #"#font-family-base: 'Comic Sans MS';
#body-bg: lime;
#font-size-h1: 50px;";
var commonStylesBundle = new CustomStyleBundle("~/Bundles/BootstrapStyles");
commonStylesBundle.Include(
"~/Content/less/bootstrap-3.1.1/bootstrap.less",
new InjectContentItemTransform(beforeLessCodeToInject, afterLessCodeToInject));
commonStylesBundle.Orderer = nullOrderer;
bundles.Add(commonStylesBundle);
}
}
Both ways have disadvantage: the injection of LESS-variables does not work in debug mode.

Problems in the global namespace when I try to use my custom server control (in the App_Code directory)

Traditionally I use the regular asp.net website (created using the File > New Website). Recently, I opted to work off of a full fledged project (created using File > New Project > ASP.net Web Application).
I've been using the same custom controls for years without incident. I simply create the new website, place my CustomControls.cs file in the App_Code directory, add one line to the web.config file and I can use all of my custom server controls.
When I try that with my web project I get the following error
Error 225 The
type or namespace name 'DTF' could not
be found in the global namespace (are
you missing an assembly
reference?) D:[Project Location On
Drive]\AgIn02.aspx.designer.cs
My custom control file looks like this
using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using AjaxControlToolkit;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
namespace DTF.Web.UI
{
public class IntOnlyBox : TextBox
{
private RequiredFieldValidator rfv;
private ValidatorCalloutExtender vce;
private AjaxControlToolkit.FilteredTextBoxExtender ftb;
private string strInvalidMessage = "";
private string strValidationGroup = "";
public string ValidationGroup
{
get
{
return strValidationGroup;
}
set
{
strValidationGroup = value;
}
}
public string InvalidMessage
{
get
{
return strInvalidMessage;
}
set
{
strInvalidMessage = value;
}
}
protected override void OnInit(EventArgs e)
{
rfv = new RequiredFieldValidator();
rfv.ControlToValidate = this.ID;
rfv.ErrorMessage = "<span style=\"color:black\"><b>Required Field Missing</b><br />" + this.InvalidMessage + "</span>";
//rfv.ErrorMessage = this.InvalidMessage;
rfv.ID = "rfv" + this.ID;
rfv.Display = ValidatorDisplay.None;
rfv.SetFocusOnError = true;
rfv.EnableClientScript = true;
rfv.ValidationGroup = this.ValidationGroup;
vce = new AjaxControlToolkit.ValidatorCalloutExtender();
vce.ID = "vce" + this.ID;
vce.TargetControlID = "rfv" + this.ID;
vce.Width = 300;
ftb = new FilteredTextBoxExtender();
ftb.ID = "ftb" + this.ID;
ftb.TargetControlID = this.ID;
ftb.FilterType = FilterTypes.Numbers;
Controls.Add(rfv);
Controls.Add(vce);
Controls.Add(ftb);
}
protected override void Render(HtmlTextWriter w)
{
//w.Write(this.InvalidMessage);
base.Render(w);
rfv.RenderControl(w);
vce.RenderControl(w);
ftb.RenderControl(w);
}
}
}
my web.config entry looks like this (in the pages/controls area)
<add tagPrefix="DTF" namespace="DTF.Web.UI" />
I've tried everything I can think of to get this to work and compile. The intellisense for the custom server controls works fine, it simply won't compile.
I also get the error "The base class
includes the field 'blanro', but its
type (DTF.DateBoxFull) is not
compatible with the type of control
(DTF.DateBoxFull)."
Any idea how to fix this, and why it would work in the regular asp.net website but not in a web project?
Thanks Everyone.
Compile your custom classes to a .dll and add a reference to your project.
Well, to answer it simply WAP and Web Site have a different way of compiling the applications and hence you may be seeing this behavior.
When using a WAP, it's best not to put your custom control in App_Code. Instead, simply put that code somewhere else in your project, and you should be able to use the control from your pages without problems.
The App_Code directory in a WebApplication project has no effect. All code files, regardless of the directory in which it resides, are compiled into a single assembly, and that assembly is placed in the bin directory.
Just move all your files out of App_Code and then delete App_Code.

Categories