We are using mvvmcross for Xamarin android native but unable to use webview to render html page, please help if anyone tried ...regular xamarin android can do but since we use mvvmcross then that regular won't work
We tried using mvvmcross XAMRIN also added plugin https://nuget.info/packages/MvvmCross.Plugin.WebBrowser/8.0.2 but nothing works
I Did it from View because below LoadDataWithBaseURL() function is not supported/working in ViewModel
i have added below code in View.
WebView.LoadDataWithBaseURL(null, Html, "text/html", "utf-8", null);
In design
<WebView android:id="#+id/ReceiptWebView" android:layout_width="match_parent" android:layout_height="match_parent"/>
It will load HTMl with Table.
This should get you started. I've tested with MvvmCross 8.0.2. This code is slightly modified from Google
You could also probably just use the MvvmCross Web Browser plugin by first saving your HTML to a file then using a "file://..." URI.
using Android.Content;
using Android.Runtime;
using Android.Util;
using Android.Webkit;
using System;
namespace MyApp.Droid.Ui.Controls
{
[Register("myapp.droid.ui.controls.MvxWebView")]
public class MvxWebView : WebView
{
private string _text;
[Preserve(Conditional = true)]
public MvxWebView(Context context, IAttributeSet attrs) : base(context, attrs)
{
}
[Preserve(Conditional = true)]
public string Text
{
get
{
return _text;
}
set
{
if (string.IsNullOrEmpty(value))
{
return;
}
_text = value;
LoadData(_text, "text/html", "utf-8");
UpdatedHtmlContent();
}
}
public event EventHandler HtmlContentChanged;
private void UpdatedHtmlContent()
{
HtmlContentChanged?.Invoke(this, EventArgs.Empty);
}
}
}
Then in your view:
<myapp.droid.ui.controls.MvxWebView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:MvxBind='Text MyHtml' />
...and in your ViewModel:
public string MyHtml
{
get => _myHtml;
set => SetProperty(ref _myHtml, value);
}
MyHtml = "<html><p>Hello, World!</p></html>";
Not necessarily a solution to fix the non-working WebView; but if indeed this is an outstanding compatibility issue, maybe looking into MvvmsCross' WebBrowser plugin might be a suitable workaround fir your application.
Related
I am not sure of the terminology for what I'm looking to do, so sorry in advance!
I've found a FilePicker plugin for Xamarin.Forms (https://github.com/Studyxnet/FilePicker-Plugin-for-Xamarin-and-Windows) that implements device-specific functionality for selecting files via the CrossFilePicker class.
The way to use leverage this functionality would be something like
CrossFilePicker.Current.OpenFile("Filename.txt");
The most important part of this for me is that CrossFilePicker.Current is static and can be accessible from anywhere in the shared layer of my Xamarin.Forms app.
I need to implement a class with the same characteristics. I want to leverage device Accessibility functionality (i.e. determining if a screen reader is enabled) and I need to be able to do so with a static class.
My eventual plan is to then wrap this static class so that I can use it for unit tests too.
I don't want to import device libraries into my shared project.
TLDR: I need a static class that implements device-specific functionality.
Any help would be greatly appreciated! Thank you :)
EDIT:
Here are the files I have currently implemented in my project
IAccessibilityService Located in the shared .NET project
namespace Bitspace.Services
{
public interface IAccessibilityService
{
public bool IsScreenReaderEnabled();
public void Announcement(string message);
public void NavigationAnnouncement(string message);
}
}
DeviceAccessibility.cs Located in the shared .NET project
using System;
namespace Bitspace.Services
{
public class DeviceAccessibility
{
private static Lazy<IAccessibilityService> Implementation = new Lazy<IAccessibilityService>(() => CreateAccessibilityService(), System.Threading.LazyThreadSafetyMode.PublicationOnly);
public static IAccessibilityService Current
{
get
{
var curr = Implementation.Value;
if (curr == null)
{
throw new Exception();
}
return curr;
}
}
private static IAccessibilityService CreateAccessibilityService()
{
return new DeviceAccessibilityImplementation();
}
}
}
DeviceAccessibilityImplementation.cs Located in the Android project
using Android.Runtime;
namespace Bitspace.Services
{
[Preserve (AllMembers = true)]
public class DeviceAccessibilityImplementation : IAccessibilityService
{
public bool IsScreenReaderEnabled()
{
return true;
}
public void Announcement(string message)
{
}
public void NavigationAnnouncement(string message)
{
}
}
}
If I try to build the project, I get an error on the return new DeviceAccessibilityImplementation(); line in DeviceAccessibility.cs that says DeviceAccessibility.cs(25, 24): [CS0246] The type or namespace name 'DeviceAccessibilityImplementation' could not be found (are you missing a using directive or an assembly reference?)
However, CTRL Clicking on that line takes me to the DeviceAccessibilityImplementation.cs
I have a contentpage in c# along with a xaml file.
Page1.xaml.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace CompanyName.Pages
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Page1 : ContentPage
{
public Page1()
{
InitializeComponent();
}
}
}
Page1.xaml:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CompanyName.Pages.Page1">
<ContentPage.Content>
<StackLayout>
<Label Text="Welcome to Xamarin.Forms!"
VerticalOptions="CenterAndExpand"
HorizontalOptions="CenterAndExpand"
x:Name="test"
/>
<Button Text="Hello, World!" />
</StackLayout>
</ContentPage.Content>
</ContentPage>
This is a fairly large project so to isolate anything that could be causing issues, I set my MainPage to Page1.
App.xaml.cs:
using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
using System.Threading.Tasks;
using System.Linq;
using CompanyName.DataBases;
using CompanyName.Pages;
using CompanyName.Models;
namespace CompanyName
{
public partial class App : Application
{
public static String OnDatabasesLoaded { get; } = "DatabasesLoaded";
public static Boolean IsDatabasesLoaded { get; private set; }
public App()
{
InitializeComponent();
// The below line is my normal entry point
// MainPage = new NavigationPage(new MainPage()) { BarBackgroundColor = Color.FromHex("#2196f3"),BarTextColor=Color.White};
MainPage = new NavigationPage(new Page1()); // I added this to see if Page1 would even work
}
protected async override void OnStart()
{
// Create the tabels
/*
ColorDatabase colorDatabase = await ColorDatabase.Instance();
QuiltDatabase quiltDatabase = await QuiltDatabase.Instance();
TextDatabase textDatabase = await TextDatabase.Instance();
*/
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
}
My problem is that my xaml file is not rendered. On both a physical device and the emulator, a blank screen appears (excluding the navigation bar at the top). Both the xaml and code behind are set as Embedded Resources. I am using Visual Studio 2022 with Xamarin.Forms 5.0.0.2478. I made a new project and I was able to create a new page and the layout displayed fine.
I haven't been able to see if this error happens on ios. All my testing has been on Android.
Thanks
"Both the xaml and code behind are set as Embedded Resources."
Why? code and xaml are not resource types. VS has to "compile" those files.
Let VS correctly insert them into your project:
Copy those files someplace safe outside the project.
Delete them from project.
Rt-click on project / Add New Item / Content Page, name "Page1".
Then open your originals in a text editor, so you can copy the text, paste them into the files created by VS.
I am currently busy with a Xamarin mobile application (only android now) and I am trying to show PDF files. I found a free plugin here: https://github.com/xamarin/docs-archive/tree/master/Recipes/xamarin-forms/Controls/display-pdf but I run into an issue.
When I go to the view I see the PDF controls, but not the PDF itself. (shows a gray screen). Here is a similar issue: display local PDF file in webview control - Displays Blank Pdf File but nothing is working.
I put the PDF file in the correct place with the correct options, see here:
And here you can see the view in the emulator:
I also tried the run the given demo project, but I get a lot of errors :(
Some help would be really appreciated!
CustomView:
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
namespace BizzTimeTest.ViewModels
{
public class CustomPDFWebView : WebView
{
public static readonly BindableProperty UriProperty = BindableProperty.Create(propertyName: "Uri",
returnType: typeof(string),
declaringType: typeof(CustomPDFWebView),
defaultValue: default(string));
public string Uri
{
get { return (string)GetValue(UriProperty); }
set { SetValue(UriProperty, value); }
}
}
}
Custom renderer:
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using BizzTimeTest.Droid;
using BizzTimeTest.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
[assembly: ExportRenderer(typeof(CustomPDFWebView), typeof(CustomWebViewRenderer))]
namespace BizzTimeTest.Droid
{
[Obsolete]
public class CustomWebViewRenderer : WebViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
if (e.NewElement != null)
{
var customWebView = Element as CustomPDFWebView;
Control.Settings.AllowUniversalAccessFromFileURLs = true;
Control.LoadUrl(string.Format("file:///android_asset/pdfjs/web/viewer.html?file={0}", string.Format("file:///android_asset/Content/{0}", "test_pdf_file.pdf")));
}
}
}
}
xaml
<local:CustomPDFWebView x:Name="custom_pdf_viewer" IsVisible="false"
Uri="test_pdf_file.pdf" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" />
I am getting the error
Exception junit.framework.AssertionFailedError: Please have your activity extend XWalkActivity for shared mode
in android crash reports. I have not been able to get this crash on testing. I have only used XWalkView in my game and not XWalkActivity. The implementation is as shown below.
XWalkView mXWalkView = new XWalkView(context);
MyJavaScriptInterface javaInterface = new MyJavaScriptInterface();
mXWalkView.addJavascriptInterface(javaInterface, "interOps");
mXWalkView.load("javascript:( function () { function createURL(event) { window.interOps.Callback(event.data); }window.addEventListener('message', createURL , false); } ) ()",null);
mXWalkView.load(URL, null);
mXWalkView.setResourceClient(new XWalkResourceClient(mXWalkView) {
#Override
public void onLoadFinished(XWalkView view, String url)
{
super.onLoadFinished(view, url);
}
});
Crash log points to the line
XWalkView mXWalkView = new XWalkView(context);
And I have integrated crosswalk in embedded mode and not shared mode; which is why this crash log is confusing. Can anyone help me figure out why this error is being thrown.
I think the error is from load javascript, i tried to reform the javascript abit:
function messageCallback() { window.addEventListener('message', function(event){window.interOps.Callback(event.data);} , false);}messageCallback();
dunno it it helps, but couldn't hurt to try.
It's possible that you're passing the wrong Context, if you're in the activity class try passing this otherwise try getBaseContext() or getApplicationContext() and see if you get the same error.
I do not know whether it is relevant or not. I had same error, i changed supported architectures to v7a and my application runs without errors.
Try it like this;
mXwalkView = new XWalkView(this, null);
Constructor for Crosswalk runtime.
Public Constructors
XWalkView(Context context, AttributeSet attrs)
Constructor for inflating via XML.
XWalkView(Context context, Activity activity)
I'm very new to MVVMCross & Xamarin, so it's very possible I'm missing something simple, but I have an Mvx.MvxGridView layout bound to a simple list of objects.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Mvx.MvxGridView
android:numColumns="5"
android:verticalSpacing="15dp"
android:horizontalSpacing="15dp"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
local:MvxBind="ItemsSource Bikes"
local:MvxItemTemplate="#layout/bikeassignmentview_bikeelement" />
</LinearLayout>
The view is pretty simple:
namespace Keiser.MPM.Screen.Droid.Views
{
using Android.App;
[Activity(Theme = "#style/Theme.FullScreen")]
public class BikeAssignmentView : BaseView
{
protected override void OnCreate(Android.OS.Bundle bundle)
{
base.OnCreate(bundle);
this.SetContentView(Resource.Layout.BikeAssignmentView_Page);
}
}
}
Same with the view model:
namespace Keiser.MPM.Screen.Core.ViewModels
{
using System.Collections.Generic;
using System.Windows.Input;
using Cirrious.CrossCore;
using Cirrious.MvvmCross.ViewModels;
using Keiser.MPM.Screen.Core.Models.Bikes;
public class BikeAssignmentViewModel : BaseViewModel
{
private IBikeManagerService _bikeManagerService;
private List<Bike> _bikes;
public List<Bike> Bikes { get { return _bikes; } }
public BikeAssignmentViewModel(IBikeManagerService bikeManagerService)
{
_bikeManagerService = bikeManagerService;
_bikes = _bikeManagerService.Bikes;
}
}
}
The service where the Bikes list is actually originating is nested all the way down in a service class:
namespace Keiser.MPM.Screen.Core.Models.Bikes
{
using System;
using System.Linq;
using System.Threading;
using System.Collections.Generic;
using Cirrious.CrossCore;
using Cirrious.CrossCore.Core;
using Cirrious.MvvmCross.ViewModels;
using Cirrious.MvvmCross.Plugins.Messenger;
using Core.Models.Settings;
public class BikeManagerService : MvxNotifyPropertyChanged, IBikeManagerService
{
public object BikesLocker = new object();
private List<Bike> _bikes = new List<Bike>();
public List<Bike> Bikes
{
get { return _bikes; }
set { _bikes = value; RaisePropertyChanged(() => Bikes); }
}
// --- Other boring code...
}
}
Here's the issue. The grid view won't populate dynamically at all if the list is empty when the view is loaded. If I enter the page with the list populated, it will load correctly, and the grids will be added for new objects added to the list, but it will not remove disposed grids from the list until I click on the screen a bit. The objects continue to update correctly until the object is disposed. Then the fields of the object stop working, but they don't disappear. Also, if the list ever goes back to empty, the view won't ever update again.
Am I missing something? Should I be invalidating the view or something? Any help or resources would be greatly appreciated!
[======================= Solution =======================]
The final solution was to convert the list to an observable collection:
Interface:
namespace Keiser.MPM.Screen.Core.Models.Helpers
{
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
public interface IObservableCollection<T>
: IList<T>
, INotifyPropertyChanged
, INotifyCollectionChanged
{
}
}
Class:
namespace Keiser.MPM.Screen.Core.Models.Helpers
{
using System.Collections.Generic;
using System.Collections.ObjectModel;
public class SimpleObservableCollection<T>
: ObservableCollection<T>
, IObservableCollection<T>
{
public SimpleObservableCollection(List<T> source) : base(source) { }
protected override void OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
base.OnCollectionChanged(e);
}
}
}
All changes made to the Collection had to be done on the main UI thread, which began to degrade performance (I'm guessing from the continual context switching?). I ended up scrapping the Observable list and implementing an IEnumerable class which fires a message on changes and subscribing to the message in the view:
namespace Keiser.MPM.Screen.Droid.Views
{
using Android.App;
using Android.Widget;
using Cirrious.MvvmCross.Plugins.Messenger;
[Activity(Theme = "#style/Theme.FullScreen")]
public class BikeAssignmentView : BaseView
{
protected MvxSubscriptionToken _BikeListToken;
protected override void OnCreate(Android.OS.Bundle bundle)
{
base.OnCreate(bundle);
this.SetContentView(Resource.Layout.BikeAssignmentView_Page);
var gridView = FindViewById<GridView>(Resource.Id.gridview);
_BikeListToken = Cirrious.CrossCore.Mvx.Resolve<IMvxMessenger>().SubscribeOnMainThread<Core.Models.Bikes.BikesChangedMessage>(message =>
{
((BaseAdapter)gridView.Adapter).NotifyDataSetChanged();
});
}
}
}
Normal Mvvm and Data-Binding works using INotifyPropertyChanged
This means that when your Grid in the UI binds its ItemsSource to Bikes on the BikeAssignmentViewModel then it hooks into the PropertyChanged event on BikeAssignmentViewModel
Since you are firing RaisePropertyChanged from your Service and not from your ViewModel then the Grid never sees this change notification.
To work around this:
you could find a way to raise the RaisePropertyChange call from the ViewModel rather than the Service
you could find a way to bind the Grid to the Service rather as well as to the ViewModel (e.g. bind ItemsSource Service.Books)
If you're new to Data-Binding then it may also be worth reading up more about Data-Binding and for lists also learning about ObservableCollection and INotifyCollectionChanged