user6217862
user6217862

Reputation:

How to access to a native control within a DependencyService?

I have multiple platform-based dependency services that act as the interface (code not UI) to binding projects that in turn "can" produce seques to modal view controllers and for that they need which view that attached to.

After I implement the dependency interface:

public interface IProcessAndRespond
{
    void gpuCalc (Byte[] data, object view);
}

In the platform code, I need to pass a Android Context/Widget/View, an iOS UIView and/or UIViewController to various platform specfic routines.

public class DateTimeDialog_iOS : IDateTimeDialog
{
    public DateTimeDialog_iOS ()
    {
    }
    void gpuCalc (Byte[] data, object view)
    {
       // lookup a native control by name? id? 
    }
}

How can I reference a native control within my gpuCalc method?

Upvotes: 2

Views: 1136

Answers (1)

SushiHangover
SushiHangover

Reputation: 74209

Can you access a native control in Xamarin.Forms?

Yes and No ;-)

The only spot that a native control is truly exposed and mostly safe to change is within its Xamarin.Forms renderer class.

But, we do it all the time in straight platform-dependent based code, I an not saying this is best-practice but it works and gets the job done when you can not control the 3rd-party code that you are binding to.

Example: On iOS you would convert a IntPtr of the native object via:

Runtime.GetNSObject (button.NativeHandle) as UIButton;

BEWARE: Do not hold a reference the those IntPtr-derived objects, treat it as transient! Other you will be chasing phantom crashes

Note: If I had my way, I would wrap these 3rd-party services within a custom control and then write a custom renderer for it but the amount of work ($) prevents that most of the time.

So how can you do it:

In your Xamarin.Forms based project, subclass a control, these are very fast subclasses and we did all the Forms controls in less then an hour.

Button Subclass:

public class NButton : Button
{
    public IntPtr NativeHandle;

    public NButton ()
    {
    }
}

In each of your platform dependent projects, subclass a native render:

Button Render Subclass:


iOS:


public class NButtonRenderer : ButtonRenderer
{
    public NButtonRenderer ()
    {
    }

    protected override void OnElementChanged (ElementChangedEventArgs<Button> e)
    {
        base.OnElementChanged (e);

        if (Control != null) {
            (e.NewElement as NButton).NativeHandle = Control.Handle;
        }

        if (e.OldElement != null) {
            (e.NewElement as NButton).NativeHandle = IntPtr.Zero;
        }
    }

}

Android:


public class NButtonRenderer : ButtonRenderer
{
    public NButtonRenderer ()
    {
    }

    protected override void OnElementChanged (ElementChangedEventArgs<NButton> e)
    {
        base.OnElementChanged (e);

        if (Control != null) {
            (e.NewElement as NButton).NativeHandle = Control.Handle;
        }

        if (e.OldElement != null) {
            (e.NewElement as NButton).NativeHandle = IntPtr.Zero;
        }
    }
}

Note: make sure you register it:


[assembly: ExportRenderer (typeof(NButton), typeof(NButtonRenderer))]


Create a Dependency service for each of your platforms

Ref: https://developer.xamarin.com/guides/xamarin-forms/dependency-service/

Example: IDateTimeDialog Interface

public interface IDateTimeDialog
{
    Task<DateTime> ShowDateTimeDialog (DateTime dateTime, NButton button);
}

Now you have access to the native control:

Within your platform specific, implement the interface method you defined:

    async public Task<DateTime> ShowDateTimeDialog (DateTime dateTime, NButton button)
    {

        // Do not hold a reference the following object, treat it as transient!
        UIButton foo = Runtime.GetNSObject (button.NativeHandle) as UIButton;
        ~~~~~
        return dateTime;
    }

Upvotes: 2

Related Questions