Xamarin iOS delegate BaseType usage - xamarin

I'm just wondering why delegate in binding iOS project has to use BaseType(typeof(NSObject)) attribute when it's iOS counterpart does not use NSObject
iOS code:
#protocol TestDelegate
- (void)onSuccess:(NSString*)token;
#end
#interface Utility : NSObject
#property (nullable, weak, getter = getTestDelegate, setter = setTestDelegate:) id<TestDelegate> delegate;
#end
Sharpie code with delegate to event mapping added:
[Protocol, Model]
public interface TestDelegate
{
[Export ("onSuccess:")]
void OnSuccess (string token);
}
[BaseType(typeof(NSObject),
Delegates = new string[] { "WeakDelegate" },
Events = new Type[] { typeof(TestDelegate) })
public interface Utility
{
[Wrap ("WeakDelegate")]
[NullAllowed]
TestDelegate Delegate { [Bind ("getTestDelegate")] get; [Bind ("setTestDelegate:")] set; }
[NullAllowed, Export ("delegate", ArgumentSemantic.Weak)]
NSObject WeakDelegate { [Bind ("getTestDelegate")] get; [Bind ("getTestDelegate:")] set; }
}
BaseType attribute was not generated on TestDelegate by Sharpie because iOS native code was not using <NSObject> in its protocol.
This fails with "The type or namespace name TestDelegate' does not exist in the namespaceTest'. Are you missing an assembly reference? (CS0234) (Test.iOS)".
When I add [BaseType(typeof(NSObject))] on top of the TestDelegate it works like a charm.
The question is why this is needed?

Related

Binding string with Button

I am trying to bind a string to a Button in pure C# (no XAML), but apparently I am doing it wrong, as the result of my code is that the button disappears.
I am defining my property as follows:
public string selectionString { get; set; }
And this is how I am binding the string to the button:
selectionString = "Hello";
selectionButton = new Button
{
TextColor = Color.Black
};
selectionButton.SetBinding(Button.TextProperty, "selectionString");
Children.Add(selectionButton);
I have tried to use BindingMode.TwoWay, but it doesn't work.
Of course, setting the text and removing the binding makes the button appear and work.
My need is just this: the button text should be the selectionString, and if this changes by an external event, so the button's text should change accordingly.
Am I missing something in how the binding works?
Bindings work against public properties on the view's binding context, and respond to INotifyPropertyChanged events firing. Hopefully this demonstrates for you.
public class MyViewModel : INotifyPropertyChanged
{
// Fire RaisePropertyChanged in the setter, I use Fody to weave this in
public string SelectionString {get;set;}
}
public class MyView : Page
{
protected override void OnBindingContextChanged()
{
if (BindingContext is MyViewModel)
{
this.SetBinding(Button.TextProperty, "SelectionString");
}
}
}

Xamarin exception when framework callback passes an interface

I'm trying to bind a framework written in Swift to my Xamarin application. The framework has a delegate with the following method:
onError(error: IError)
IError is a protocol in Swift (4):
#objc public protocol IError {
var message: String { get }
}
It's binding, as generated by sharpie and modified by me, looks like this:
// #protocol IError
[BaseType(typeof(NSObject))] // <- Added by me. Won't compile without it.
[Protocol, Model]
abstract class IError
{
// #required #property (readonly, copy, nonatomic) NSString * _Nonnull message;
[Abstract]
[Export("message")]
string Message { get; }
}
When the callback is being called, I get the following error in my application:
System.MemberAccessException has been thrown
Cannot create an instance of IError because it is an abstract class
Why is my application trying to instantiate IError? isn't the framework supposed to do this? Does it have to do with [BaseType(typeof(NSObject))]?
Finally found the solution here. You need to declare a bare interface with the same name but starting with 'I' in your binding and then it magically creates a wrapper for the interface which you receive instead of the real object.
The binding now looks like this:
//bare interface
interface IIError {}
// #protocol IError
[BaseType(typeof(NSObject))] // <- Added by me. Won't compile without
it.
[Protocol, Model]
abstract class IError
{
// #required #property (readonly, copy, nonatomic) NSString *
_Nonnull message;
[Abstract]
[Export("message")]
string Message { get; }
}
The binding for the method then looks like this:
[Abstract]
[Export("onError:")]
void OnError(IIError error);
EDIT:
It seems to also be documented here. Missed it somehow.

Xamarin binding Events WeakDelegate issue

I have following
iOS code:
#protocol TestDelegate
- (void)onSuccess:(NSString*)token;
#end
#interface Utility : NSObject
#property (nullable, weak, getter = getTestDelegate, setter = setTestDelegate:) id<TestDelegate> delegate;
#end
and Sharpie generated code with delegate to event mapping:
[Protocol, Model]
public interface TestDelegate
{
[Export ("onSuccess:")]
void OnSuccess (string token);
}
[BaseType(typeof(NSObject),
Delegates = new string[] { "WeakDelegate" },
Events = new Type[] { typeof(TestDelegate) })
public interface Utility
{
[Wrap ("WeakDelegate")]
[NullAllowed]
TestDelegate Delegate { [Bind ("getTestDelegate")] get; [Bind ("setTestDelegate:")] set; }
[NullAllowed, Export ("delegate", ArgumentSemantic.Weak)]
NSObject WeakDelegate { [Bind ("getTestDelegate")] get; [Bind ("getTestDelegate:")] set; }
}
When I'm attaching only to the events and not using delegate property then events are not fired.
Correct me if I’m wrong but when using only events then nothing is referring to the internally created _XDelegate so it will be garbage collected right away.
In my case I had to store internal _XDelegate after attaching to the events to make them work.
public class TestClass
{
private readonly Delegate _del;
public TestClass()
{
iOS.SingletonInstance().OnSuccess += HandleOnSuccess;
_del = iOS.SingletonInstance().Delegate; //store Delegate instance to make events work
}
}
The issue was somewhere else - SingletonInstance() is a method instead of property so nothing is referring to the returned object so it's being GCed.

Visual Studio 2010/2012/2013, Class Diagram: how to show interface as base class, not as "lillypop"?

Since the interface is already on the diagram I would like to show inheritance reference explicitly. But I can't find how...
There is a bug in VS 2005 up to 2012 that won't allow it to work.
I have a work arround that might trick it into drawing the inheritance for interfaces.
Say your interface is called IMyInterface. You have to replace it with an abstract class implementing that interface and use it instead of your interface. The code would make use of the conditional compilation and will look like this:
//to generate class diagram, add 'CLSDIAGRAM' to the conditional symbols on the Build tab,
// or add '#define CLSDIAGRAM' at the top of this file
#if CLSDIAGRAM
#warning CLSDIAGRAM is defined and this build should be used only in the context of class diagram generation
//rename your interface by adding _
public interface IMyInterface_
{
int MyProperty { get; }
void MyMethod();
}
//this class will act as an interface in the class diagram ;)
public abstract class IMyInterface : IMyInterface_ // tricks other code into using the class instead
{
//fake implementation
public int MyProperty {
get { throw new NotImplementedException(); }
}
public void MyMethod()
{
throw new NotImplementedException();
}
}
#else
// this is the original interface
public interface IMyInterface {
int MyProperty { get; }
void MyMethod();
}
#endif
That's likely to show it as you wish.
In your case IMyInterface will become IMedicine.

Nested Server Controls

I've got a server control that contains nested server controls,
<uc1:ArticleControl runat="server">
<HeadLine></HeadLine>
<Blurb></Blurb>
<Body></Body>
</uc1:ArticleControl>
Code:
[ToolboxData("<{0}:ArticleControl runat=server></{0}:ArticleControl>")]
[ParseChildren(ChildrenAsProperties = true)]
public class ArticleControl : WebControl
{
[PersistenceMode(PersistenceMode.InnerProperty)]
public HeadLineControl HeadLine
{
get;
set;
}
[PersistenceMode(PersistenceMode.InnerProperty)]
public BlurbControl Blurb
{
get;
set;
}
[PersistenceMode(PersistenceMode.InnerProperty)]
public BodyControl Body
{
get;
set;
}
}
Nested control definition (applies to all nested controls):
public class HeadLineControl : ControlBase
{
public HeadLineControl() : base() { }
public HeadLineControl(Article article) : base(article) { }
Base class definition
public abstract class ControlBase : Control
{
protected Article article;
protected ControlBase() { }
protected ControlBase(Article article)
{
this.article = article;
}
The ArticleControl is responsible for writing for the individual parts of the article specified by the nested controls,
My problem is that when the Articlecontrol is created, instances of the nested server controls are created by the .NET framework using the default constructor defined for the System.Web.Ui.Control class eg:
namespace System.Web.UI
{
public class Control : IComponent, IDisposable, IParserAccessor, IUrlResolutionService, IDataBindingsAccessor, IControlBuilderAccessor, IControlDesignerAccessor, IExpressionsAccessor
{
// Summary:
// Initializes a new instance of the System.Web.UI.Control class.
public Control();
I need to call or override the default behaviour of .Net to call my Control base class constructor in stead of the default .Net defined contructor. So in short, if a new instance of HeadLineControl is created, it needs to created by the ControlBase(Article article) constuctor.
Is this possible and if possible, how do I accomplish this?
I've done this in the meanwhile as a workaround, but there must be a better way?
[PersistenceMode(PersistenceMode.InnerProperty)]
public HeadLineControl HeadLine
{
get { return null; }
set
{
this.Controls.Add(new HeadLineControl(articlePage.Article)();
}
}

Resources