How is INativeElementView supposed to be implemented? - c#

I was trying to implement ListViewCachingStrategy=RecycleElement in a Xamarin.Forms xaml page and everything displays correctly until I try to scroll. Then I get an exception that says that INativeElementView needs to be implemented. I designed the views in each native platform's ui designer and have a custom renderer for each. This crash does not happen if I use RetainElement instead of RecycleElement. Any ideas or suggestions?
Edit: My view that I created in the designer is called FeeViewCell and if I understand it right that is where INativeElementView is supposed to be implemented (in FeeViewCell.cs). My problem is now that casting to an element returns a System.InvalidCastException saying that the specified cast is not valid. Here is my code implementing INativeElementView:
public Element Element
{
get
{
return this.ToView() as Element;
}
}
I also have this as a question on the Xamarin forum here.

The INativeNativeView interface needs to be implemented on the custom cell class. In my example in iOS I in the custom renderer provides an item that comes from the pcl in my case a FeeCell.
This is part of my custom renderer
public class FeeCellRenderer : ViewCellRenderer
{
public override UIKit.UITableViewCell GetCell(Cell item, UIKit.UITableViewCell reusableCell, UIKit.UITableView tv)
{
var x = (FeeCell)item;
//Do whatever you need to here.
}
}
This is part of my custom ui class:
public partial class FeeViewCell : UITableViewCell, INativeElementView
{
public static readonly NSString Key = new NSString(nameof(FeeViewCell));
Element _element;
public Element Element
{
get { return _element; }
set { _element = value; }
}
}
It also works to do this:
public Element Element
{
get { return new FeeCell(); }
}

Related

'The left-hand side of an assignment must be a variable, property or indexer' error with custom handler in .NET MAUI

I am currently migrating a Xamarin.Forms app to .NET MAUI, and I am noticing that to make custom controls it has changed to creating a custom handler. I have followed the documentation values and an article in the .NET MAUI Wiki on GitHub but I have come across when making the MapText that when assigning the value that I declare in my interface it throws me the error CS0131 "The left-hand side of an assignment must be a variable, property or indexer" and this prevents me from continuing.
For the moment I have continued using the classic way to create the custom controls, but since I have already indicated deprecated I have decided to adopt this new way, but the error mentioned above prevents me, in this case not only with a custom Entry, but also with a custom Label.
I would be very grateful for your support
This is my CustomEntryHandler:
public partial class CustomEntryHandler : ViewHandler<ICustomEntry, EditText>
{
private static PropertyMapper<ICustomEntry, CustomEntryHandler> CustomEntryMapper = new(ViewMapper)
{
[nameof(ICustomEntry.Text)] = MapText,
};
public CustomEntryHandler(CommandMapper commandMapper = null) : base(CustomEntryMapper, commandMapper)
{
}
protected override EditText CreatePlatformView()
{
return new EditText(Context);
}
private static void MapText(EntryHandler handler, ICustomEntry entry)
{
handler.PlatformView?.Text = entry.Text;// This line is the error CS0131
}
}
And this is my interface and Custom Control Class:
public interface ICustomEntry : IView
{
public string Text { get; }
public Color TextColor { get; }
}
public class CustomEntry : View, ICustomEntry
{
public string Text { get; set; }
public Color TextColor { get; set; }
}
You can change your code:
if(handler.PlatformView != null)
handler.PlatformView.Text = entry.Text;
The null propogation operator returns a value. Since, its on the left hand side and not a value, it cant be use this way.
Someone also opened an issue:
https://github.com/dotnet/csharplang/issues/2883

Designer Collection Editor, how to know current index?

My control has a child control collection of TabPage type. I want to create a new child instance assigning a name according to its index. To do this, I need to know the current state of the collection. But how? CreateInstance only gives the item type, not the collection reference.
public class MyEditor : CollectionEditor
{
public MyEditor(Type type) : base(type)
{
}
protected override Type[] CreateNewItemTypes()
{
return new[]
{
typeof(TabPage)
};
}
protected override object CreateInstance(Type itemType)
{
return new TabPage("Page 1"); //<-- How to know current index?
}
}
With the lack of examples, I resorted to decompiling the .NET Framework and seeing how Microsoft did it. Microsoft's TabPageCollectionEditor was inside "System.Design.dll".
You can access the control which has the collection like this:
var control = Context?.Instance as MyTabControl;
Then it was a trivial thing to get current index:
int currentIndex = control.TabPages.Count;

Custom control / UIView in Xamarin.iOS with design-time properties

I am creating a user interface for an iOS app and I am looking for the correct way to create a reusable custom control. I got it generally working when running the app, but at design time setting my "exported" properties has no visible effect in the designer. I think I am doing something fundamentally wrong, so perhaps someone could give me guidance
What I am doing:
I have created a subclass of UIControl.
In the constructor I call an Initialize method.
In the Initialize method, I add several subviews and constraints to layout them within my control
Here is some hollowed out code that shows the above:
[Register("RangedValueSelector"), DesignTimeVisible(true)]
public sealed class RangedValueSelector : UIControl
{
public RangedValueSelector(IntPtr p)
: base(p)
{
Initialize();
}
public RangedValueSelector()
{
Initialize();
}
public int HorizontalButtonSpacing
{
get { return _horizontalButtonSpacing; }
set
{
_horizontalButtonSpacing = value;
}
}
[Export("LabelBoxVerticalInset"), Browsable(true)]
public int LabelBoxVerticalInset
{
get
{
return _labelBoxVerticalInset;
}
set
{
_labelBoxVerticalInset = value;
}
}
private void Initialize()
{
//Code that creates and add Subviews
//Code that creates and add the required constraints, some of which should depend on the design time properties
}
}
So the control works perfectly fine if I set the exported properties via the designer, however they do not have an immediate effect in the designer itself.
What is the suggested way of having design-time settable properties that change the constraint values? I would like to avoid having to recreate all the subviews each time someone in the code or in the designer sets a property.
You are missing constructor with RectangleF which is used by designer.
public RangedValueSelector(RectangleF bounds):base(bounds){}
The rest seems to be correct.

How do you retrieve User Defined Runtime Attributes in Xamarin?

On my XCode storyboard I have a custom image view (called CustomImageButton). There is a section about User Defined Runtime Attributes:
I've seen a couple of examples about how to retrieve these values in Objective-C but can't work out how to convert to Xamarin C#. The code I'm using is:
class CustomImageButton : UIImageView
{
public CustomImageButton(IntPtr handle) : base(handle)
{
NSObject objType = ValueForKey(new NSString("bgType"));
if (objType == null)
// Do something here
}
public override NSObject ValueForUndefinedKey(NSString key)
{
return null;
}
}
However, it always goes into the undefined key part and returns null. How can I retrieve the string 'Transparent' for the key 'bgType'?
In your Custom (C#-based) UI class, add a property for your custom User Defined Runtime Attribute.
[Connect]
private NSString Stack {
get {
return (NSString)GetNativeField ("Stack");
}
set {
SetNativeField ("Stack", value);
}
}
This will in turn auto-generate the property in your ObjC custom class header file:
#property (nonatomic, retain) IBOutlet NSString *Stack;
Defined your attribute value:
When the xib or storyboard is loaded, this property gives the custom attribute a place to be stored for future reference.
Now you can access this value via ValueForKey:
partial void OnBottonUpInside (CustomButton sender) {
NSString value = (NSString)sender.ValueForKey(new NSString("Stack"));
Console.WriteLine(value);
}
Output Example:
2016-02-03 08:27:51.666 keyvalue[46492:2538387] OverFlow
Of course, I could have defined the property as public and just access it that way:
partial void OnBottonUpInside (CustomButton sender)
{
Console.WriteLine (sender.Stack);
}
Note: Normally in ObjC you would just define this as a strong property, but Xamarin does not define it that way, but the result is the "same":
#property (strong) NSString *Stack;
#SushuHangover's response is correct and still works. However, if you want to avoid the deprecated GetNativeField() & SetNativeField() methods, then this would be the change that has worked for me:
[Connect]
private NSObject Stack
{
get
{
return ValueForKey((NSString)"Stack");
}
set
{
SetValueForKey(value, (NSString)"Stack");
}
}

Item Collection option for a User Control

As you can see in the pic below, for a ListView Control you can add Items using the Properties pane.
How do I enable this kind of stuff for my UserControl?
I'm not getting anything when I search Google, but I'm probably not using the correct terms.
Does anybody know?
Thanks
You need to create a class that defines the object type that the collection ids composed of. A listView has ListViewItem objects. A TabControl has TabPage objects. Your control has objects which are defined by you. Let's call it MyItemType.
You also need a wraper class for the collection. A simple implementation is shown below.
public class MyItemTypeCollection : CollectionBase
{
public MyItemType this[int Index]
{
get
{
return (MyItemType)List[Index];
}
}
public bool Contains(MyItemType itemType)
{
return List.Contains(itemType);
}
public int Add(MyItemType itemType)
{
return List.Add(itemType);
}
public void Remove(MyItemType itemType)
{
List.Remove(itemType);
}
public void Insert(int index, MyItemType itemType)
{
List.Insert(index, itemType);
}
public int IndexOf(MyItemType itemType)
{
return List.IndexOf(itemType);
}
}
Finally you need to add a member variable for the collection to your user control and decorate it properly:
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public MyItemTypeCollection MyItemTypes
{
get { return _myItemTypeCollection; }
}
and you now have a simple interface that allows you to browse and edit the collection. Leaves a lot to be desired still but to do more you will have to learn about custom designers which can be difficult to understand and implement.

Categories

Resources