引用:https://jamilgeor.com/binding-a-xamarin-forms-webview-to-reactiveui-view-model-using-custom-type-converters/

Introduction

In this article, we will expand on our demo app, by displaying article content inside of a WebView after the user is navigated to the ArticleView.

To make things more interesting, because we are pulling in content from this blog, we will be using HTMLAgilityPack to strip out any unnecessary HTML that we don't want to be displayed inside of our app.

We'll also be utilising a ReactiveUI custom type converter to bind our WebView to raw HTML content.

At a high level our ArticleViewModel is going to do the following:

  1. Download HTML from URL specified in the Article object that we are storing in our ViewModel
  2. Cache the HTML content
  3. Parse HTML to strip out unwanted tags, and fix other related issues
  4. Map content to WebView

Setup

Install the HTMLAgilityPack NuGet package into all three projects i.e. Droid, iOS, and PCL projects. We'll be using this to clean up the HTML that we download from this blog.
https://www.nuget.org/packages/HtmlAgilityPack

Parsing HTML

A WebView can take two different data sources, the first being a website URL, and the second being a raw HTML string. In our case we want to use the second method, this is because we'd like to modify the HTML from the articles before we display the article in our View.

I won't go into the parsing code detail, but at a high level, you can see what is happening in the code snippet below.

public interface IHtmlParserService
{
string Parse(string content, string baseUrl);
} public class HtmlParserService : IHtmlParserService
{
public string Parse(string content, string baseUrl)
{
var document = new HtmlDocument(); document.LoadHtml(content); ReplaceRelativeUrls(document, baseUrl); RemoveRedundantElements(document); return document.DocumentNode.OuterHtml;
}
...
}

The full source code for this class can be found here.

ViewModel

We'll now setup our ViewModel. The first thing we need to do is create a new property that the View will bind to called Content. This property will simply store the parsed HTML content that we want to display in our View.

string _content;
public string Content {
get => _content;
set => this.RaiseAndSetIfChanged(ref _content, value);
}

We now need to implement our methods for fetching our content from the blog, and also the method that we'll use to map the parsed HTML content to our Content property.

IObservable<string> LoadArticleContent()
{
return BlobCache
.LocalMachine
.GetOrFetchObject<string>
(CacheKey,
async () =>
await _articleService.Get(Article.Url), CacheExpiry);
} void MapContentImpl(string content)
{
Content = _htmlParserService.Parse(content, Configuration.BlogBaseUrl);
}

We now want to call our new LoadArticleContent method from inside of our constructor to initialize the Content property that the View will be bound to.

public ArticleViewModel(IScreen hostScreen, Article article)
{
... LoadArticleContent()
.ObserveOn(RxApp.MainThreadScheduler)
.Catch<string, Exception>((error) => {
this.Log().ErrorException($"Error loading article {Article.Id}", error);
return Observable.Return("<html><body>Error loading article.</body></html>");
})
.Subscribe(MapContentImpl);
}

The above code simply does the following:

  1. Call LoadArticleContent method
  2. Ensure that the following code runs on the main UI thread
  3. Catch any exceptions, and return an error message
  4. Call the MapContentImpl method and pass the result of LoadArticleContent or the Error message

Custom Type Converters

Now that our ViewModel is all setup, we just need to bind the Source property of our WebView to our Content property on our ViewModel.

Because the Source property of a WebView uses a type that doesn't have a default binding converter provided by ReactiveUI, we'll need to create our own.

To do this we just need to create a class that implements the IBindingTypeConverter interface.

public class HtmlBindingTypeConverter : IBindingTypeConverter, IEnableLogger
{
public int GetAffinityForObjects(Type fromType, Type toType)
{
return fromType == typeof(string) && toType == typeof(WebViewSource) ? 100 : 0;
} public bool TryConvert(object from, Type toType, object conversionHint, out object result)
{
try
{
result = new HtmlWebViewSource { Html = (string)from };
return true;
}
catch (Exception error)
{
this.Log().ErrorException("Problem converting to HtmlWebViewSource", error);
result = null;
return false;
}
}
}

The GetAffinityForObjects method simply validates that a the from and to properties can be converted by our type converter. If it can't then we should return 0 otherwise, we should return any number greater than 0. If there are two converters that can handle the types that are passed to it, then the number returned by this method will determine which one is selected.

The TryConvert method does the actual conversion from one type to another.

You can read more about type converters here:
https://reactiveui.net/docs/handbook/data-binding/type-converters

Now that we have created our type converter, we need to register it with the Splat dependency resolver. We do this in our AppBootstrapper class.

public class AppBootstrapper : ReactiveObject, IScreen
{
...
private void RegisterParts(IMutableDependencyResolver dependencyResolver)
{
...
dependencyResolver.RegisterConstant(new HtmlBindingTypeConverter(), typeof(IBindingTypeConverter));
}
...
}

Binding the View

All we need to do now is setup the binding in our view.

public partial class ArticlePage : ContentPage, IViewFor<ArticleViewModel>
{
readonly CompositeDisposable _bindingsDisposable = new CompositeDisposable(); ...
protected override void OnAppearing()
{
base.OnAppearing(); this.OneWayBind(ViewModel, vm => vm.Content, v => v.ArticleContent.Source).DisposeWith(_bindingsDisposable);
}
...
}

Summary

In this article, we've created a custom type converter for binding raw HTML content to a Xamarin.Forms WebView.

Full source code for this article can be found here:
https://github.com/jamilgeor/FormsTutor/tree/master/Lesson09

References

https://www.nuget.org/packages/HtmlAgilityPack
https://reactiveui.net/docs/handbook/data-binding/type-converters

Binding a Xamarin.Forms WebView to ReactiveUI View Model using Custom Type Converters的更多相关文章

  1. Xamarin.Forms——WebView技术研究

    在Xamarin中有一些Forms原生不太好实现的内容可以考虑使用HTML.Javascript.CSS那一套前端技术来实现,使用WebView来承载显示本地或网络上的HTML文件.不像OpenUri ...

  2. Xamarin.Forms WebView

    目前本地或网络的网页内容和文件加载 WebView是在您的应用程序显示Web和HTML内容的视图.不像OpenUri,这需要用户在Web浏览器的设备上,WebView中显示您的应用程序内的HTML内容 ...

  3. Xamarin Forms中WebView的自适应高度

    在Xamarin.Forms中,WebView如果嵌套在StackLayout和RelativeLayout中必须要设置HeightRequest和WidthRequest属性才会进行渲染.可是在实际 ...

  4. 在 Xamarin.Forms 实现密码输入EntryCell

    在 Xamarin.Forms 中,我们通常使用 TableView 来构建输入表单.Xamarin 为我们提供了 EntryCell 用于输入文本,但是其并不支持密码输入,即密码掩码.这里要对 En ...

  5. Xamarin.Forms 自定义控件(呈现器和效果)

    Xamarin.Forms 使用目标平台的本机控件呈现用户界面,从而让 Xamarin.Forms 应用程序为每个平台保留了相应的界面外观.凭借效果,无需进行自定义呈现器实现,即可自定义每个平台上的本 ...

  6. 搞懂Xamarin.Forms布局,看这篇应该就够了吧

    Xamarin.Forms 布局介绍 什么是布局?可以简单的理解为,我们通过将布局元素有效的组织起来,让屏幕变成我们想要的样子! 我们通过画图的方式来描述一下Xamarin.Forms的布局. 小节锚 ...

  7. Xamarin.Forms介绍

    On May 28, 2014, Xamarin introduced Xamarin.Forms, which allows you to write user-interface code tha ...

  8. Xamarin.Forms中为WebView指定数据来源Source

    Xamarin.Forms中为WebView指定数据来源Source   网页视图WebView用来显示HTML和网页形式内容.使用这种方式,可以借助网页形式进行界面设计,并利于更新和维护.WebVi ...

  9. Xamarin.Forms 开发资源集合(复制)

    复制:https://www.cnblogs.com/mschen/p/10199997.html 收集整理了下 Xamarin.Forms 的学习参考资料,分享给大家,稍后会不断补充: UI样式 S ...

随机推荐

  1. CF1063F String Journey DP、SAM、线段树

    传送门 为了方便把串反过来,条件变为\(t_i\)是\(t_{i+1}\)的真子串,答案显然不变. 一件重要的事情是必定存在一种最优解,字符串序列\(\{t\}\)满足\(|t_i| = i\). 考 ...

  2. Android 系统架构 和 各个版本代号介绍

    一.Android 系统架构: 1. linux内核层Android 基于Linux内核,为Android设备的各种硬件提供底层驱动 比如: 显示驱动.音频.照相机.蓝牙.Wi-Fi驱动,电源管理等 ...

  3. 创建包含CRUD操作的Web API接口5:实现Delete方法

    本节是前面四节的延续,在前面几节中我们创建了Web API并添加了必要的基础设施,实现了Get.Post.和Put方法.本节中,我们将介绍如何在Web API中实现Delete方法. 在RESTful ...

  4. 【爬坑笔记】c# 如何通过EF Core读写sql server的类似double型字段

    =============================================== 2019/8/31_第1次修改                       ccb_warlock == ...

  5. Scala 系列(三)—— 流程控制语句

    一.条件表达式if Scala 中的 if/else 语法结构与 Java 中的一样,唯一不同的是,Scala 中的 if 表达式是有返回值的. object ScalaApp extends App ...

  6. texlive2019安装

    TeX Live 是 TUG (TeX User Group) 发布并维护的的 TeX 系统,可以称得上是TeX的官方系统,官网为:https://www.tug.org/texlive/ 1.通过最 ...

  7. C# 8.0 中开启默认接口实现

    原文:C# 8.0 中开启默认接口实现 当你升级到 C# 8.0 和 .NET Core 3.0 之后,你就可以开始使用默认接口实现的功能了. 从现在开始,你可以在接口里面添加一些默认实现的成员,避免 ...

  8. java之mybatis之查询及分页

    1.mybatis中查询方式有3种 //查询单个值 @Test public void testFindOne()throws IOException{ SqlSession session = My ...

  9. 用HTML、CSS、JS制作圆形进度条(无动画效果)

    逻辑 1.首先有一个圆:蓝色的纯净的圆,效果: 2.再来两个半圆,左边一个,右边一个将此蓝色的圆盖住,效果: 此时将右半圆旋转60°,就会漏出底圆,效果:   然后我们再用一个比底圆小的圆去覆盖这个大 ...

  10. 华为 mate30 安装谷歌助手

    最近入手了 华为 mate30 pro, 作为一个8年的老果粉,在使用2天 mate30p 之后,给了耳目一新的感觉,不得不说这款手机真的很强大,各种优点我也不多说了,可以看网上各种专业的测评 但是手 ...