Call UWP service on Shared project - xamarin

I have a Xamarin app where I implemented a service to do some printing. I create that service on UWP app because it needs some dependencies of it.
UWP Service:
public class PrintUWPService
{
PrintManager printmgr = PrintManager.GetForCurrentView();
PrintDocument PrintDoc;
PrintDocument printDoc;
PrintTask Task;
private Windows.UI.Xaml.Controls.WebView ViewToPrint = new Windows.UI.Xaml.Controls.WebView();
public PrintUWPService()
{
printmgr.PrintTaskRequested += Printmgr_PrintTaskRequested;
}
public async void Print(WebView viewToPrint, string htmlSource)
{
ViewToPrint.NavigateToString(htmlSource);
if (PrintDoc != null)
{
printDoc.GetPreviewPage -= PrintDoc_GetPreviewPage;
printDoc.Paginate -= PrintDoc_Paginate;
printDoc.AddPages -= PrintDoc_AddPages;
}
printDoc = new PrintDocument();
try
{
printDoc.GetPreviewPage += PrintDoc_GetPreviewPage;
printDoc.Paginate += PrintDoc_Paginate;
printDoc.AddPages += PrintDoc_AddPages;
var showprint = await PrintManager.ShowPrintUIAsync();
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
}
PrintDoc = null;
GC.Collect();
}
private void Printmgr_PrintTaskRequested(PrintManager sender, PrintTaskRequestedEventArgs args)
{
var deff = args.Request.GetDeferral();
Task = args.Request.CreatePrintTask("Grocery List", OnPrintTaskSourceRequested);
deff.Complete();
}
async void OnPrintTaskSourceRequested(PrintTaskSourceRequestedArgs args)
{
var def = args.GetDeferral();
await Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
args.SetSource(printDoc.DocumentSource);
});
def.Complete();
}
private void PrintDoc_AddPages(object sender, AddPagesEventArgs e)
{
printDoc.AddPage(ViewToPrint);
printDoc.AddPagesComplete();
}
private void PrintDoc_Paginate(object sender, PaginateEventArgs e)
{
PrintTaskOptions opt = Task.Options;
printDoc.SetPreviewPageCount(1, PreviewPageCountType.Final);
}
private void PrintDoc_GetPreviewPage(object sender, GetPreviewPageEventArgs e)
{
printDoc.SetPreviewPage(e.PageNumber, ViewToPrint);
}
}
}
Then I create interface for it:
public interface IPrintUWPService
{
void Print(WebView viewToPrint, string htmlSource);
}
Now inside a class of my shared project I want to call this service as:
private readonly IPrintUWPService _printService = DependencyService.Get<IPrintUWPService>();
But it does not work. it says:
IPrintUWPService does not exist in the current context
So I try to access to UWP class as:
using MyCompany.ProjectName.App.UWP
But it says that "UWP" namespace does not exist. What am I doing wrong? is not possible call service on UWP project inside shared project? Regards

Call UWP DependencyService on Shared project
As #Jason mentioned in above comment, you need declare the interface in the Xamarin Forms project and implement it in the UWP client project.
And please note, after implementing the interface in each platform project, the platform implementations must be registered with the DependencyService, so that Xamarin.Forms can locate them at runtime. Use DependencyAttribute like the following.
[assembly: Dependency(typeof(UWP Client namesapce))]

Related

Has anyone been able to see a crash attachment with Xamarin Forms and AppCenter.ms?

As far as I know I have followed exactly the instructions:
I have set everything up as suggested. Used my secret key, enabled crashes. Had the set up checked by another developer and see the crash happened in appcenter.ms but still I never see any attached information.
Here's an example:
public class Application
{
// This is the main entry point of the application.
static void Main(string[] args)
{
// if you want to use a different Application Delegate class from "AppDelegate"
// you can specify it here.
try
{
UIApplication.Main(args, null, "AppDelegate");
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
Crashes.TrackError(ex,
new Dictionary<string, string> {
{"Main", "Exception"},
{"Device Model", DeviceInfo.Model },
});
throw;
}
}
}
No matter what, when and how my application crashes I still will not get the attached information.
I am wondering has anyone got the attached data for crashes to work with XF ?
We can use AppCenter only after it has been started which according to official documentation on iOS we do it in AppDelegate class in the method FinishedLaunching. But the point is the class Application in Main.cs file is called before AppDelegate class.
If you want to see the attached info then you can try it for example in a XAML code-behind file by manually throwing an exception. Here is an example for a button's click event:
private void TheButton1_OnClicked(object sender, EventArgs e)
{
try
{
throw new DivideByZeroException("Testing attached info!");
}
catch (Exception exception)
{
Crashes.TrackError(exception,
new Dictionary<string, string> {{"Device Model", "My device model" }});
}
}
The attached info on TrackError() method i.e properties dictionary works on both Android and iOS. To see that info you need to go through this in App Center's panel:
From left panel choose Diagnostics.
From Groups section choose your specific group.
From tabs in top section choose Reports.
Choose your specific device.
The attached info is In Stacktrace tab and in Error properties section.
Just to correct, the additional data you attach with exception in TrackError method are mostly in catch blocks or generated exception in TrackError methods, so it will only displayed with those manually logged(TrackError) exceptions.
Crashes are exceptions that are not handled and logged automatically by appcenter so if you look in crash reports there will not be any attached data available.
Additional data sent with exception as properties can be found in reports section of error on appcenter.
I am sure you have initialized Crash service in OnStart method of App.xaml.cs class with correct app secrets and required platforms(android/ios).
I was able to track the crashes. The only difference is am tracking it from the native projects.
For Android in the MainActivity:
protected override void OnCreate(Bundle savedInstanceState)
{
...
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
TaskScheduler.UnobservedTaskException += TaskSchedulerOnUnobservedTaskException;
AndroidEnvironment.UnhandledExceptionRaiser += AndroidEnvironment_UnhandledExceptionRaiser;
...
}
private void AndroidEnvironment_UnhandledExceptionRaiser(object sender, RaiseThrowableEventArgs e)
{
var newExc = new Exception("UnhandledExceptionRaiser", e.Exception as Exception);
LogUnhandledException(newExc);
}
private static void TaskSchedulerOnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs unobservedTaskExceptionEventArgs)
{
var newExc = new Exception("TaskSchedulerOnUnobservedTaskException", unobservedTaskExceptionEventArgs.Exception);
LogUnhandledException(newExc);
}
private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs unhandledExceptionEventArgs)
{
var newExc = new Exception("CurrentDomainOnUnhandledException", unhandledExceptionEventArgs.ExceptionObject as Exception);
LogUnhandledException(newExc);
}
internal static void LogUnhandledException(Exception exception)
{
try
{
Crashes.TrackError(exception);
...
}
catch (Exception ex)
{
// just suppress any error logging exceptions
}
}
For iOS in the AppDelegate:
public override bool FinishedLaunching(UIApplication app, NSDictionary options)
{
...
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
TaskScheduler.UnobservedTaskException += TaskSchedulerOnUnobservedTaskException;
...
}
private static void TaskSchedulerOnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs unobservedTaskExceptionEventArgs)
{
var newExc = new Exception("TaskSchedulerOnUnobservedTaskException", unobservedTaskExceptionEventArgs.Exception);
LogUnhandledException(newExc);
}
private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs unhandledExceptionEventArgs)
{
var newExc = new Exception("CurrentDomainOnUnhandledException", unhandledExceptionEventArgs.ExceptionObject as Exception);
LogUnhandledException(newExc);
}
internal static void LogUnhandledException(Exception exception)
{
try
{
...
}
catch
{
// just suppress any error logging exceptions
}
}

ObservableCollection made of strings Not updating

Ok, so, I'm trying to link an ObservableCollection from my Android project to my Cross-Platform Project::
I've got this so far...this is in my Cross-platform app
ObservableCollection<String> NewRef = DependencyService.Get<ISlateBluetoothItems>().test().testThing;
NewRef.CollectionChanged += TestThing_CollectionChanged;
listView.ItemsSource = NewRef;
private void TestThing_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
listView.ItemsSource = DependencyService.Get<ISlateBluetoothItems>().test().testThing;
Console.WriteLine("working");
}
The line "working" is never printed even if I make changes to the ObservableCollection on the android portion of my app...
Here's the interface I'm using for the DependencyService:
using System.Collections.ObjectModel;
namespace ThoughtCastRewrite.BluetoothX
{
public interface ISlateBluetoothItems
{
BluetoothItems test();
}
}
Here's the class I use to expose the list:
namespace ThoughtCastRewrite.BluetoothX
{
public class BluetoothItems
{
public ObservableCollection<String> testThing;
public BluetoothItems()
{
testThing = new ObservableCollection<String>();
testThing.Add("wtf?");
}
public void AddThis()
{
testThing.Add("ok");
}
}
}
This is in the Android portion of my app, it implements the ISlateBluetoothItems interface
BluetoothItems bluetoothItems = new BluetoothItems();
then I call
bluetoothItems.AddThis();
but "ok" is not added to my list! I don't get the CollectionChanged event firing off! What's the deal guys? What's the deal?
You should assign your ObservableCollection as a source of your listview only once, not after each change. Changes to the collection will be automaticcly propagated to the listview.

Xamarin Forms WebView.CanGoBack always returns false on UWP

Using the Xamarin Forms WebView control, I'm overriding the OnBackButtonPressed() and finding that the CanGoBack always returns false in UWP.
I don't see this problem in Android.
Is this a XF bug or am I doing something wrong?
Note: I'm running XF v2.3.3.193
EDIT: I upgraded to XF 2.3.4.247 and the problem persists.
I have created a code sample and reproduce your issue when the WebView browse several website. And I have found reason in the Xamarin.Forms source code.
void UpdateCanGoBackForward()
{
((IWebViewController)Element).CanGoBack = Control.CanGoBack;
((IWebViewController)Element).CanGoForward = Control.CanGoForward;
}
The CanGoBack property will be changed when UpdateCanGoBackForward method invoked. And UpdateCanGoBackForward method was called only when the native NavigationCompleted event was invoked. So if some website could not be loaded quickly, the CanGoBack property would not be changed.
You can improve this design by custom WebView. And you could follow the code below.
CustomWebView.cs
Add the new property for CustomWebView.
public class CustomWebView : WebView
{
public bool CCanGoBack { get; set; }
public CustomWebView()
{
}
}
CustomWebViewRenderer.cs
And change the property when the ContentLoading event invoked.
[assembly: ExportRenderer(typeof(CustomWebView), typeof(CustomWebViewRenderer))]
namespace CustomWebViewTest.UWP
{
public class CustomWebViewRenderer : WebViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.ContentLoading += Control_ContentLoading;
}
}
private void Control_ContentLoading(Windows.UI.Xaml.Controls.WebView sender, Windows.UI.Xaml.Controls.WebViewContentLoadingEventArgs args)
{
(Element as CustomWebView).CCanGoBack = Control.CanGoBack;
}
}
}
MainPage.cs
private void backClicked(object sender, EventArgs e)
{
if (Browser.CCanGoBack)
{
Browser.GoBack();
}
}

How to monitor the changes in the Bluetooth Connectivity state changes from Xamarin.Forms

Need to check the bluetooth connection to a remote device exists or got disconnected. Its basically a Forms which mainly targets Android and UWP.
I tried with the Dependency services and made the implementation in Android as below,
_[assembly: Xamarin.Forms.Dependency(typeof(BluetoothListenerActivity))]
namespace demotool.Droid
{
public class BluetoothListenerActivity : Activity,IBluetoothListener
{
public event EventHandler OnDeviceDisconnected;
public static BluetoothListenerActivity mySelf;
//string device;
public void start()
{
mySelf = this;
BluetoothStatusBroadCast mreceiver = new BluetoothStatusBroadCast();
IntentFilter mfilter = new IntentFilter(BluetoothDevice.ActionAclDisconnected);
Forms.Context.RegisterReceiver(mreceiver,mfilter);
}
public void receivedstatuschangd(string devicename,string state)
{
OnDeviceDisconnected(this, new DeviceDisconnectedEventArgs(name: devicename,status: state));
}
}
}_
BroadcastReceiver:
namespace Demo.Droid
{
[BroadcastReceiver]
class BluetoothStatusBroadCast : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
BluetoothDevice device =(BluetoothDevice)intent.GetParcelableExtra(BluetoothDevice.ExtraDevice);
BluetoothListenerActivity.mySelf.receivedstatuschangd(device.Name, intent.Action);
}
}
}
Xamarin Forms Part:
_ protected override void OnStart()
{
IBluetoothListener bluetoothlistener = DependencyService.Get();
bluetoothlistener.start();
bluetoothlistener.OnDeviceDisconnected += Bluetoothlistener_OnDeviceDisconnected;
}
private void Bluetoothlistener_OnDeviceDisconnected(object sender, DeviceDisconnectedEventArgs e)
{
Page page1 = new Page();
page1.DisplayAlert(e.Name+ " " +e.Status, "Alert", "OK");
}_
The Intent Action that I have registered- BluetoothDevice.ActionAclDisconnected, is getting triggered once the Pairing is completed or a connection request is made, which I assume is not the actual Disconnection of the devices
Is there any common plugin which monitors the Bluetooth Connectivity Changes to a remote device. Or could you please tell me the actual Intent Action that I should listen for.
Thanks in Advance !

Assert that a mocked (MOQ thus DynamicProxy) event has no handlers attached

This is quite straight forward(ish) to do is the event is 'real' as in now created by DynamicProxy, but I can't work anything out for a mocked event.
The best way to explain what I'm trying to achieve is with code, please see the comment lines in the test method:
using System;
using Moq;
using NUnit.Framework;
namespace MOQTest
{
[TestFixture]
public class EventsMoqTest
{
[Test]
public void DetachTest()
{
var hasEventMock = new Mock<IHasEvent>();
using (var observer = new Observer(hasEventMock.Object))
{
//Assert that hasEventMock.Object has handler attached
}
//Assert that hasEventMock.Object DOES NOT have handler attached
}
}
public interface IHasEvent
{
event EventHandler AnEvent;
}
public class Observer : IDisposable
{
private readonly IHasEvent _hasEvent;
private readonly EventHandler _hasEventOnAnEvent;
public Observer(IHasEvent hasEvent)
{
_hasEvent = hasEvent;
_hasEventOnAnEvent = _hasEvent_AnEvent;
_hasEvent.AnEvent += _hasEventOnAnEvent;
}
void _hasEvent_AnEvent(object sender, EventArgs e)
{}
public void Dispose()
{
_hasEvent.AnEvent -= _hasEventOnAnEvent;
}
}
}
Unfortunately, you can't. This isn't really a moq issue, but the way the C# event keyword works with delegates. See this SO answer for more information.

Resources