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.
Related
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.
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 want to load a list of installed applications on an Android phone.
I am trying to build a Xamarin Forms application leveraging the MVVM framework.
I am using Visual Studio 2019 Pro and Xamarin Forms
I keep finding articles talking about Android.App.Application.Context.PackageManager.GetInstalledApplications however I have not been able to figure out how to get get a reference to it.
You can implement this using dependency service. I will give in detail so others who are new to Xamarin also might understand.
First we will create our model. You can name this as InApp.cs in Shared directory.
public class InApp
{
public string AppName { get; set; }
public string PackageName { get; set; }
public InApp(string appName, string packageName)
{
AppName = appName;
PackageName = packageName;
}
}
Now we can create our dependency service in Android Folder. Name this as AndroidService.cs.
public class AndroidService : IAndroidService
{
public List<InApp> GetIntalledApps()
{
List<InApp> inApps = new List<InApp>();
IList<ApplicationInfo> apps = Android.App.Application.Context.PackageManager.GetInstalledApplications(PackageInfoFlags.MatchAll);
for (int i = 0; i < apps.Count; i++)
{
inApps.Add(new InApp(apps[i].LoadLabel(Android.App.Application.Context.PackageManager), apps[i].PackageName));
}
return inApps;
}
}
The above code will get the installed apps, then creates a List of the model which we've created above and returns it.
At run time, Xamarin should know where to look for the dependency service, so for that we should add this above the name space of AndroidService class we've created above.
[assembly: Xamarin.Forms.Dependency(typeof(AndroidService))]
IAndroidService is the interface which will access the Android folder AndroidService class at runtime. We will create this class in Shared directory. We can name this as IAndroidService.cs.
public interface IAndroidService
{
List<InApp> GetIntalledApps();
}
Now we have completed our dependency service implementation. Next part is to create a ListView and add the returned installed apps list from our Android service.
Since we are doing this in MVVM, we will create a view model now.
Create InstalledAppViewModel.cs in Shared directory.
public class InstalledAppViewModel: INotifyPropertyChanged
{
private ObservableCollection<InApp> installedApps;
public ObservableCollection<InApp> InstalledApps
{
get { return installedApps; }
set
{
installedApps = value;
}
}
public InstalledAppViewModel()
{
List<InApp> listOfInstalledApps = DependencyService.Get<IAndroidService>().GetIntalledApps();
InstalledApps = new ObservableCollection<InApp>(listOfInstalledApps);
}
public event PropertyChangedEventHandler PropertyChanged;
}
Above, we have done the dependency injection and added the returned values from the GetInstalledApps method to our Observable list.
Now in your MainPage.Xaml.Cs bind the view model.
BindingContext = new InstalledAppViewModel();
Add the list view in your MainPage.Xaml.
<ListView ItemsSource="{Binding InstalledApps}">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout Orientation="Vertical">
<StackLayout Orientation="Horizontal" VerticalOptions="CenterAndExpand">
<Label Text="{Binding AppName}" FontSize="Medium" />
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
In ItemSource of the list view attribute, you are binding the Observable list which we have created in the View Model class.
I am having a weird issue, Xamarin Forms App works fine when I setup Content page as a startup page. If I set TabbedPage as a startup and same ContentPage as a Children of a TabbedPage then it doesn't display/data-bind ContentPage. No errors. What am I missing any idea? Here is my TabbedPage view model.
using MvvmCross.Core.ViewModels;
using System.Windows.Input;
namespace Company.Mobile.ViewModels
{
public class TabbedMainViewModel
: MvxViewModel
{
}
}
XAML:
<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:forms="using:Xamarin.Forms"
xmlns:local="clr-namespace:company.Mobile.Pages;assembly=company.Mobile"
x:Class="company.Mobile.Pages.TabbedMainPage"
Title="Title">
<TabbedPage.Children>
<local:HomePage/>
<local:MainPage/>
<local:ResourcesPage/>
<local:ContactPage/>
</TabbedPage.Children>
</TabbedPage>
After a lot of trial and error and help from the community, here is what worked.
Set BindingContext to the ContentPage code-behind C#, something like below:
public partial class HomePage : ContentPage
{
public HomePage()
{
InitializeComponent();
var svc = Mvx.Resolve<IMobileService>();
BindingContext = new HomeViewModel(svc);
}
}
Get your data in HomeViewModel constructor something like below:
public class HomeViewModel : MvxViewModel
{
private readonly IMobileService service;
public HomeViewModel(IMobileService service)
{
this.service = service;
//Content = service.GetContent; //Get your data
}
}
I would say that you can do that easier by adding this inline property for an each your tabbed page in XAML, e.g. for the home page it should be BindingContext="{Binding HomePageViewModel}"
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