Local WebView in Xamarin.Forms

Hi,
What is the simplest way to locally call an HTML in a WebView running on Page of a CarouselPage in Xamarin.Forms?

Thanks!

Best Answer

Answers

  • DiegoEstevezDiegoEstevez USMember
    edited July 2016

    Does anyone know? Im not interested in the CSS nor scripts of it.

  • JohnHardmanJohnHardman GBUniversity ✭✭✭✭✭

    @DiegoEstevez - do you mean loading a local HTML file into a WebView? And does it need to work across Android, iOS and Windows?

    For platforms where opening a URL starting "file://" works, all that is required is:

                _webView = new WebView
                {
                    Source = new UrlWebViewSource
                    {
                        Url = _url
                    },
                    HorizontalOptions = LayoutOptions.FillAndExpand,
                    VerticalOptions = LayoutOptions.FillAndExpand
                };
    

    However, for platforms where opening a URL starting "file://" does not work, streaming it as follows does work:

                Stream stream = assembly.GetManifestResourceStream(fileName);
                StreamReader reader = new StreamReader(stream);
                _htmlString = reader.ReadToEnd();
    
                HtmlWebViewSource html = new HtmlWebViewSource();
                html.Html = _htmlString;
    
                _webView = new WebView
                {
                    Source = html,
                    HorizontalOptions = LayoutOptions.FillAndExpand,
                    VerticalOptions = LayoutOptions.FillAndExpand
                };
    
  • DiegoEstevezDiegoEstevez USMember

    @JohnHardman said:
    @DiegoEstevez - do you mean loading a local HTML file into a WebView? And does it need to work across Android, iOS and Windows?

    For platforms where opening a URL starting "file://" works, all that is required is:

                _webView = new WebView
                {
                    Source = new UrlWebViewSource
                    {
                        Url = _url
                    },
                    HorizontalOptions = LayoutOptions.FillAndExpand,
                    VerticalOptions = LayoutOptions.FillAndExpand
                };
    

    However, for platforms where opening a URL starting "file://" does not work, streaming it as follows does work:

                Stream stream = assembly.GetManifestResourceStream(fileName);
                StreamReader reader = new StreamReader(stream);
                _htmlString = reader.ReadToEnd();
    
                HtmlWebViewSource html = new HtmlWebViewSource();
                html.Html = _htmlString;
    
                _webView = new WebView
                {
                    Source = html,
                    HorizontalOptions = LayoutOptions.FillAndExpand,
                    VerticalOptions = LayoutOptions.FillAndExpand
                };
    

    @JohnHardman Im trying this on iOS and Im getting 'Stream', 'StreamReader', 'assembly' nor '_htmlString' not found. Do I have to import something? Im trying in this on the C# of the XAML im working on.
    Thank you.

  • DiegoEstevezDiegoEstevez USMember
    edited July 2016

    Update:

    I imported 'System.IO;' and Im not getting any Stream errors anymore, though I have imported 'System.Reflection' and changed "assembly" to "Assembly", but im getting this error: "An object reference is required for the non-static method 'Assembly.GetManifestResourceStream(stream)'".

  • JohnHardmanJohnHardman GBUniversity ✭✭✭✭✭

    @DiegoEstevez - the usings I have in that module are:

    using System;
    using System.IO;
    using System.Reflection;
    using System.Threading;
    using System.Threading.Tasks;
    using Xamarin.Forms;
    
  • DiegoEstevezDiegoEstevez USMember

    @JohnHardman said:
    @DiegoEstevez - the usings I have in that module are:

    using System;
    using System.IO;
    using System.Reflection;
    using System.Threading;
    using System.Threading.Tasks;
    using Xamarin.Forms;
    

    Imported them all but Im still getting: "An object reference is required for the non-static method 'Assembly.GetManifestResourceStream(stream)'".

  • JohnHardmanJohnHardman GBUniversity ✭✭✭✭✭

    @DiegoEstevez - you can get the assembly as follows:

                var assembly = typeof(A_class_that_appears_in_the_assembly_that_the_local_html_file_is_in).GetTypeInfo().Assembly;
    
  • DiegoEstevezDiegoEstevez USMember
    edited July 2016

    @JohnHardman said:
    @DiegoEstevez - you can get the assembly as follows:

                var assembly = typeof(A_class_that_appears_in_the_assembly_that_the_local_html_file_is_in).GetTypeInfo().Assembly;
    

    And how do I find the A_class_that_appears_in_the_assembly_that_the_local_html_file_is_in? I tried putting the name of the page where the WebView is but im getting the stream null.

    Thank you for your patience.

  • JohnHardmanJohnHardman GBUniversity ✭✭✭✭✭

    @DiegoEstevez -

    I have my HTML files as EmbeddedResources with CopyAlways set, in a project that I will call Project1 ( it's not really called that :-) )

    In Project1, I also have many classes. We can use any of these. So, if one of the classes in Project1 is MyClass1 and that appears in the namespace MyNamespace1, I can simply use:

    var assembly = typeof(MyNamespace1.MyClass1).GetTypeInfo().Assembly;

    What this does, is to get the Assembly in which the class is declared, so if your HTML files are EmbeddedResources with CopyAlways set, in that same assembly, everything should be ok.

  • DiegoEstevezDiegoEstevez USMember
    edited July 2016

    @JohnHardman said:
    @DiegoEstevez -

    I have my HTML files as EmbeddedResources with CopyAlways set, in a project that I will call Project1 ( it's not really called that :-) )

    In Project1, I also have many classes. We can use any of these. So, if one of the classes in Project1 is MyClass1 and that appears in the namespace MyNamespace1, I can simply use:

    var assembly = typeof(MyNamespace1.MyClass1).GetTypeInfo().Assembly;

    What this does, is to get the Assembly in which the class is declared, so if your HTML files are EmbeddedResources with CopyAlways set, in that same assembly, everything should be ok.

    Before I was using the class name of the page in where the WebView was, now I created a file in the cross-platform directory with a custon namespace. I put the namespace.classname in the typeof and I still get the stream null.
    I have the html inside the resource folder in the iOS directory as EmbeddedResources with CopyAlways set.

    Thank you for your time.

  • JohnHardmanJohnHardman GBUniversity ✭✭✭✭✭

    @DiegoEstevez - Ok, it's the filename that's the problem now, not the assembly.

    What is the value of filename being used in the call to GetManifestResourceStream?

    If your HTML file is "filename.html", you need to add a prefix to this.

    My project name, namespace and generated assembly all have the same name, so looking quickly at my code I cannot recall which of these is used as the prefix. I think it's the generated assembly name (from the project properties). If my memory serves me correctly, then once the prefix is added, the value passed to GetManifestResourceStream would be "MyAssemblyName.filename.html"

    Also, I have my HTML files in the root of the project, rather than in a resource folder. I've been meaning to change that for a while, but haven't got around to it yet.

  • DiegoEstevezDiegoEstevez USMember
    edited July 2016

    @JohnHardman said:
    @DiegoEstevez - Ok, it's the filename that's the problem now, not the assembly.

    What is the value of filename being used in the call to GetManifestResourceStream?

    If your HTML file is "filename.html", you need to add a prefix to this.

    My project name, namespace and generated assembly all have the same name, so looking quickly at my code I cannot recall which of these is used as the prefix. I think it's the generated assembly name (from the project properties). If my memory serves me correctly, then once the prefix is added, the value passed to GetManifestResourceStream would be "MyAssemblyName.filename.html"

    Also, I have my HTML files in the root of the project, rather than in a resource folder. I've been meaning to change that for a while, but haven't got around to it yet.

    Still getting the same error. I moved the html to the cross-platform directory and added the prefix you told me.

    This is my code:

    var assembly = typeof(testNamespace.testC).GetTypeInfo().Assembly; Stream stream = assembly.GetManifestResourceStream("testC.testHTML.html"); StreamReader reader = new StreamReader(stream); var _htmlString = reader.ReadToEnd(); HtmlWebViewSource html = new HtmlWebViewSource(); html.Html = _htmlString;

  • JohnHardmanJohnHardman GBUniversity ✭✭✭✭✭

    @DiegoEstevez - The prefix on the filename is not the class name - I think it's the assembly name. So, if your assembly name is the same as the namespace, the filename will be testNamespace.testHTML.html

  • DiegoEstevezDiegoEstevez USMember

    @JohnHardman said:
    @DiegoEstevez - The prefix on the filename is not the class name - I think it's the assembly name. So, if your assembly name is the same as the namespace, the filename will be testNamespace.testHTML.html

    Changed it to testNamespace and still getting the stream null error :s

  • JohnHardmanJohnHardman GBUniversity ✭✭✭✭✭

    @DiegoEstevez - It just occurred to me that you said you are trying this on iOS. In that case, you don't need to use the streaming option, the first method I mentioned above (using UrlWebViewSource) works on iOS.

  • DiegoEstevezDiegoEstevez USMember

    @JohnHardman said:
    @DiegoEstevez - It just occurred to me that you said you are trying this on iOS. In that case, you don't need to use the streaming option, the first method I mentioned above (using UrlWebViewSource) works on iOS.

    Alright so I tried urlWebViewSource and Im getting no errors though the HTML is blank.

    Browser.Source = new UrlWebViewSource { Url = "file://testHTML.html" };

  • JohnHardmanJohnHardman GBUniversity ✭✭✭✭✭
    edited July 2016

    @DiegoEstevez - When using UrlWebViewSource the filename needs to be massaged into the correct form for the platform. On iOS, the URL is created as follows:

            string url = Path.Combine(
                NSBundle.MainBundle.BundlePath,
                filename);
    

    I use a dependency service for this platform-specific stuff.

    I don't know if you've seen the documentation about this. It can be found at
    https://developer.xamarin.com/guides/xamarin-forms/working-with/webview/

  • JohnHardmanJohnHardman GBUniversity ✭✭✭✭✭

    @DiegoEstevez -

    When using the URLWebViewSource on iOS, I have the HTML file as a BundleResource.
    When using the URLWebViewSource on Android, I have the HTML file as an AndroidAsset.

    For Windows platforms, it is an Embedded Resource, but using HtmlWebViewSource

  • DiegoEstevezDiegoEstevez USMember

    @JohnHardman said:
    @DiegoEstevez -

    When using the URLWebViewSource on iOS, I have the HTML file as a BundleResource.
    When using the URLWebViewSource on Android, I have the HTML file as an AndroidAsset.

    For Windows platforms, it is an Embedded Resource, but using HtmlWebViewSource

    And how do I call NSBundle in the cross-platform project?

  • JohnHardmanJohnHardman GBUniversity ✭✭✭✭✭

    @DiegoEstevez - If you can hard-code the HTML string that makes life easier :-)

    Re. using NSBundle, that's would go in the iOS-specific dependency service. If also building for Android you would need an equivalent, which for me is simply:

            string url = Path.Combine(
                "file:///android_asset/",
                fileName);
    

    For Windows platforms, I use the streaming method described previously.

  • DiegoEstevezDiegoEstevez USMember

    @JohnHardman said:
    @DiegoEstevez - If you can hard-code the HTML string that makes life easier :-)

    Re. using NSBundle, that's would go in the iOS-specific dependency service. If also building for Android you would need an equivalent, which for me is simply:

            string url = Path.Combine(
                "file:///android_asset/",
                fileName);
    

    For Windows platforms, I use the streaming method described previously.

    A> @JohnHardman said:

    @DiegoEstevez - If you can hard-code the HTML string that makes life easier :-)

    Re. using NSBundle, that's would go in the iOS-specific dependency service. If also building for Android you would need an equivalent, which for me is simply:

            string url = Path.Combine(
                "file:///android_asset/",
                fileName);
    

    For Windows platforms, I use the streaming method described previously.

    Awesome! Thank you so much for your patience and they effort you put in helping me @JohnHardman :)

  • 15mgm1515mgm15 USMember ✭✭✭

    @DiegoEstevez Thanks for your answers it worked like a charm!

  • EeedgarEeedgar USMember

    @DiegoEstevez and all. I'm coming to this a little late ...

    I note the brief - keep things simple - so would suggest to ensure that as much code as possible goes into the the PCL rather than each individual platform's project, you use one of the cross platform file projects, like PCLStorage for example (I am not assoc with this project). That way you don't have to worry about the streams etc yourself and you can have the content of the file as follows and then include that in the HTMLWebViewSource as noted by others. It also encapsulates everything as async so you don't kill the UX:

    IFolder rootFolder = FileSystem.Current.LocalStorage;
    IFile theFile= await rootFolder.GetFileAsync(theFileName);
    string fileText = await itemPage.ReadAllTextAsync();

    Or if you are a one line maniac:

    string fileText = await (await FileSystem.Current.LocalStorage.GetFileAsync(webDocVersionID.ToString())).ReadAllTextAsync();

  • sharmilasharmila USMember

    As Defining Through HtmlWebViewSource it is working fine.But Onclick Event not firing from Html page in Windows UWP.Can Anybody help through this?

Sign In or Register to comment.