Xamarin.Forms
Benutzerdefinierte Renderer
Suche…
Benutzerdefinierter Renderer für ListView
Mit benutzerdefinierten Renderern können Entwickler das Erscheinungsbild und das Verhalten der Xamarin.Forms-Steuerelemente auf jeder Plattform anpassen. Entwickler könnten Funktionen nativer Steuerelemente verwenden.
Zum Beispiel müssen wir das Blättern in ListView
deaktivieren. Unter iOS ist ListView
auch dann scrollbar, wenn alle Elemente auf dem Bildschirm angezeigt werden und der Benutzer die Liste nicht scrollen kann. Xamarin.Forms.ListView
verwaltet diese Einstellung nicht. In diesem Fall hilft ein Renderer.
Erstens sollten wir ein benutzerdefiniertes Steuerelement in einem PCL-Projekt erstellen, das einige erforderliche bindbare Eigenschaften deklariert:
public class SuperListView : ListView
{
public static readonly BindableProperty IsScrollingEnableProperty =
BindableProperty.Create(nameof(IsScrollingEnable),
typeof(bool),
typeof(SuperListView),
true);
public bool IsScrollingEnable
{
get { return (bool)GetValue(IsScrollingEnableProperty); }
set { SetValue(IsScrollingEnableProperty, value); }
}
}
Im nächsten Schritt wird für jede Plattform ein Renderer erstellt.
iOS:
[assembly: ExportRenderer(typeof(SuperListView), typeof(SuperListViewRenderer))]
namespace SuperForms.iOS.Renderers
{
public class SuperListViewRenderer : ListViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<ListView> e)
{
base.OnElementChanged(e);
var superListView = Element as SuperListView;
if (superListView == null)
return;
Control.ScrollEnabled = superListView.IsScrollingEnable;
}
}
}
Und Android (Android-Liste hat keinen Bildlauf, wenn alle Elemente auf dem Bildschirm angezeigt werden. Daher wird das Bildlauf nicht deaktiviert. Trotzdem können wir native Eigenschaften verwenden):
[assembly: ExportRenderer(typeof(SuperListView), typeof(SuperListViewRenderer))]
namespace SuperForms.Droid.Renderers
{
public class SuperListViewRenderer : ListViewRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.ListView> e)
{
base.OnElementChanged(e);
var superListView = Element as SuperListView;
if (superListView == null)
return;
}
}
}
Element
des Renderers ist mein SuperListView
Steuerelement aus dem PCL-Projekt.
Control
Steuerelementeigenschaft des Renderers ist das native Steuerelement. Android.Widget.ListView
für Android und UIKit.UITableView
für iOS.
Und wie wir es in XAML
:
<ContentPage x:Name="Page"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:controls="clr-namespace:SuperForms.Controls;assembly=SuperForms.Controls"
x:Class="SuperForms.Samples.SuperListViewPage">
<controls:SuperListView ItemsSource="{Binding Items, Source={x:Reference Page}}"
IsScrollingEnable="false"
Margin="20">
<controls:SuperListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Label Text="{Binding .}"/>
</ViewCell>
</DataTemplate>
</controls:SuperListView.ItemTemplate>
</controls:SuperListView>
</ContentPage>
.cs
Datei der Seite:
public partial class SuperListViewPage : ContentPage
{
private ObservableCollection<string> _items;
public ObservableCollection<string> Items
{
get { return _items; }
set
{
_items = value;
OnPropertyChanged();
}
}
public SuperListViewPage()
{
var list = new SuperListView();
InitializeComponent();
var items = new List<string>(10);
for (int i = 1; i <= 10; i++)
{
items.Add($"Item {i}");
}
Items = new ObservableCollection<string>(items);
}
}
Benutzerdefinierter Renderer für BoxView
Mit Hilfe der benutzerdefinierten Renderer-Hilfe können Sie neue Eigenschaften hinzufügen und auf der nativen Plattform anders darstellen, als dies durch gemeinsam genutzten Code möglich ist. In diesem Beispiel werden Radien und Schatten zu einer Boxansicht hinzugefügt.
Erstens sollten wir ein benutzerdefiniertes Steuerelement in einem PCL-Projekt erstellen, das einige erforderliche bindbare Eigenschaften deklariert:
namespace Mobile.Controls
{
public class ExtendedBoxView : BoxView
{
/// <summary>
/// Respresents the background color of the button.
/// </summary>
public static readonly BindableProperty BorderRadiusProperty = BindableProperty.Create<ExtendedBoxView, double>(p => p.BorderRadius, 0);
public double BorderRadius
{
get { return (double)GetValue(BorderRadiusProperty); }
set { SetValue(BorderRadiusProperty, value); }
}
public static readonly BindableProperty StrokeProperty =
BindableProperty.Create<ExtendedBoxView, Color>(p => p.Stroke, Color.Transparent);
public Color Stroke
{
get { return (Color)GetValue(StrokeProperty); }
set { SetValue(StrokeProperty, value); }
}
public static readonly BindableProperty StrokeThicknessProperty =
BindableProperty.Create<ExtendedBoxView, double>(p => p.StrokeThickness, 0);
public double StrokeThickness
{
get { return (double)GetValue(StrokeThicknessProperty); }
set { SetValue(StrokeThicknessProperty, value); }
}
}
}
Im nächsten Schritt wird für jede Plattform ein Renderer erstellt.
iOS:
[assembly: ExportRenderer(typeof(ExtendedBoxView), typeof(ExtendedBoxViewRenderer))]
namespace Mobile.iOS.Renderers
{
public class ExtendedBoxViewRenderer : VisualElementRenderer<BoxView>
{
public ExtendedBoxViewRenderer()
{
}
protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
{
base.OnElementChanged(e);
if (Element == null)
return;
Layer.MasksToBounds = true;
Layer.CornerRadius = (float)((ExtendedBoxView)this.Element).BorderRadius / 2.0f;
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == ExtendedBoxView.BorderRadiusProperty.PropertyName)
{
SetNeedsDisplay();
}
}
public override void Draw(CGRect rect)
{
ExtendedBoxView roundedBoxView = (ExtendedBoxView)this.Element;
using (var context = UIGraphics.GetCurrentContext())
{
context.SetFillColor(roundedBoxView.Color.ToCGColor());
context.SetStrokeColor(roundedBoxView.Stroke.ToCGColor());
context.SetLineWidth((float)roundedBoxView.StrokeThickness);
var rCorner = this.Bounds.Inset((int)roundedBoxView.StrokeThickness / 2, (int)roundedBoxView.StrokeThickness / 2);
nfloat radius = (nfloat)roundedBoxView.BorderRadius;
radius = (nfloat)Math.Max(0, Math.Min(radius, Math.Max(rCorner.Height / 2, rCorner.Width / 2)));
var path = CGPath.FromRoundedRect(rCorner, radius, radius);
context.AddPath(path);
context.DrawPath(CGPathDrawingMode.FillStroke);
}
}
}
}
Sie können wieder anpassen, wie Sie möchten, innerhalb der Zeichenmethode.
Und das gleiche für Android:
[assembly: ExportRenderer(typeof(ExtendedBoxView), typeof(ExtendedBoxViewRenderer))]
namespace Mobile.Droid
{
/// <summary>
///
/// </summary>
public class ExtendedBoxViewRenderer : VisualElementRenderer<BoxView>
{
/// <summary>
///
/// </summary>
public ExtendedBoxViewRenderer()
{
}
/// <summary>
///
/// </summary>
/// <param name="e"></param>
protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
{
base.OnElementChanged(e);
SetWillNotDraw(false);
Invalidate();
}
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == ExtendedBoxView.BorderRadiusProperty.PropertyName)
{
Invalidate();
}
}
/// <summary>
///
/// </summary>
/// <param name="canvas"></param>
public override void Draw(Canvas canvas)
{
var box = Element as ExtendedBoxView;
base.Draw(canvas);
Paint myPaint = new Paint();
myPaint.SetStyle(Paint.Style.Stroke);
myPaint.StrokeWidth = (float)box.StrokeThickness;
myPaint.SetARGB(convertTo255ScaleColor(box.Color.A), convertTo255ScaleColor(box.Color.R), convertTo255ScaleColor(box.Color.G), convertTo255ScaleColor(box.Color.B));
myPaint.SetShadowLayer(20, 0, 5, Android.Graphics.Color.Argb(100, 0, 0, 0));
SetLayerType(Android.Views.LayerType.Software, myPaint);
var number = (float)box.StrokeThickness / 2;
RectF rectF = new RectF(
number, // left
number, // top
canvas.Width - number, // right
canvas.Height - number // bottom
);
var radius = (float)box.BorderRadius;
canvas.DrawRoundRect(rectF, radius, radius, myPaint);
}
/// <summary>
///
/// </summary>
/// <param name="color"></param>
/// <returns></returns>
private int convertTo255ScaleColor(double color)
{
return (int) Math.Ceiling(color * 255);
}
}
}
Die XAML:
Wir beziehen uns zunächst auf unsere Kontrolle mit dem zuvor definierten Namespace.
xmlns:Controls="clr-namespace:Mobile.Controls"
Wir verwenden dann das Control wie folgt und verwenden Eigenschaften, die zu Beginn definiert wurden:
<Controls:ExtendedBoxView
x:Name="search_boxview"
Color="#444"
BorderRadius="5"
HorizontalOptions="CenterAndExpand"
/>
Auf den Renderer von einem nativen Projekt aus zugreifen
var renderer = Platform.GetRenderer(visualElement);
if (renderer == null)
{
renderer = Platform.CreateRenderer(visualElement);
Platform.SetRenderer(visualElement, renderer);
}
DoSomeThingWithRender(render); // now you can do whatever you want with render
Abgerundetes Label mit einem benutzerdefinierten Renderer für Frame (PCL- und iOS-Teile)
Erster Schritt: PCL-Teil
using Xamarin.Forms;
namespace ProjectNamespace
{
public class ExtendedFrame : Frame
{
/// <summary>
/// The corner radius property.
/// </summary>
public static readonly BindableProperty CornerRadiusProperty =
BindableProperty.Create("CornerRadius", typeof(double), typeof(ExtendedFrame), 0.0);
/// <summary>
/// Gets or sets the corner radius.
/// </summary>
public double CornerRadius
{
get { return (double)GetValue(CornerRadiusProperty); }
set { SetValue(CornerRadiusProperty, value); }
}
}
}
Zweiter Schritt: iOS-Teil
using ProjectNamespace;
using ProjectNamespace.iOS;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
[assembly: ExportRenderer(typeof(ExtendedFrame), typeof(ExtendedFrameRenderer))]
namespace ProjectNamespace.iOS
{
public class ExtendedFrameRenderer : FrameRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Frame> e)
{
base.OnElementChanged(e);
if (Element != null)
{
Layer.MasksToBounds = true;
Layer.CornerRadius = (float)(Element as ExtendedFrame).CornerRadius;
}
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == ExtendedFrame.CornerRadiusProperty.PropertyName)
{
Layer.CornerRadius = (float)(Element as ExtendedFrame).CornerRadius;
}
}
}
}
Dritter Schritt: XAML-Code zum Aufrufen eines ExtendedFrame
Wenn Sie es in einem XAML-Teil verwenden möchten, vergessen Sie nicht, Folgendes zu schreiben:
xmlns:controls="clr-namespace:ProjectNamespace;assembly:ProjectNamespace"
nach dem
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
Jetzt können Sie den ExtendedFrame folgendermaßen verwenden:
<controls:ExtendedFrame
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
BackgroundColor="Gray"
CornerRadius="35.0">
<Frame.Content>
<Label
Text="MyText"
TextColor="Blue"/>
</Frame.Content>
</controls:ExtendedFrame>
Abgerundete BoxView mit wählbarer Hintergrundfarbe
Erster Schritt: PCL-Teil
public class RoundedBoxView : BoxView
{
public static readonly BindableProperty CornerRadiusProperty =
BindableProperty.Create("CornerRadius", typeof(double), typeof(RoundedEntry), default(double));
public double CornerRadius
{
get
{
return (double)GetValue(CornerRadiusProperty);
}
set
{
SetValue(CornerRadiusProperty, value);
}
}
public static readonly BindableProperty FillColorProperty =
BindableProperty.Create("FillColor", typeof(string), typeof(RoundedEntry), default(string));
public string FillColor
{
get
{
return (string) GetValue(FillColorProperty);
}
set
{
SetValue(FillColorProperty, value);
}
}
}
Zweiter Schritt: Droid-Teil
[assembly: ExportRenderer(typeof(RoundedBoxView), typeof(RoundedBoxViewRenderer))]
namespace MyNamespace.Droid
{
public class RoundedBoxViewRenderer : VisualElementRenderer<BoxView>
{
protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
{
base.OnElementChanged(e);
SetWillNotDraw(false);
Invalidate();
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
SetWillNotDraw(false);
Invalidate();
}
public override void Draw(Canvas canvas)
{
var box = Element as RoundedBoxView;
var rect = new Rect();
var paint = new Paint
{
Color = Xamarin.Forms.Color.FromHex(box.FillColor).ToAndroid(),
AntiAlias = true,
};
GetDrawingRect(rect);
var radius = (float)(rect.Width() / box.Width * box.CornerRadius);
canvas.DrawRoundRect(new RectF(rect), radius, radius, paint);
}
}
}
Dritter Schritt: iOS-Teil
[assembly: ExportRenderer(typeof(RoundedBoxView), typeof(RoundedBoxViewRenderer))]
namespace MyNamespace.iOS
{
public class RoundedBoxViewRenderer : BoxRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<BoxView> e)
{
base.OnElementChanged(e);
if (Element != null)
{
Layer.CornerRadius = (float)(Element as RoundedBoxView).CornerRadius;
Layer.BackgroundColor = Color.FromHex((Element as RoundedBoxView).FillColor).ToCGColor();
}
}
protected override void OnElementPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (Element != null)
{
Layer.CornerRadius = (float)(Element as RoundedBoxView).CornerRadius;
Layer.BackgroundColor = (Element as RoundedBoxView).FillColor.ToCGColor();
}
}
}
}