Xamarin.Forms
Renderizzatori personalizzati
Ricerca…
Renderer personalizzato per ListView
I Renderizzatori personalizzati consentono agli sviluppatori di personalizzare l'aspetto e il comportamento dei controlli Xamarin.Forms su ciascuna piattaforma. Gli sviluppatori potrebbero utilizzare le funzionalità dei controlli nativi.
Ad esempio, abbiamo bisogno di disabilitare scorrere in ListView
. Su iOS ListView
è scorrevole anche se tutti gli elementi sono posizionati sullo schermo e l'utente non dovrebbe essere in grado di scorrere l'elenco. Xamarin.Forms.ListView
non gestisce tale impostazione. In questo caso, un renderer sta venendo in aiuto.
In primo luogo, dovremmo creare un controllo personalizzato nel progetto PCL, che dichiarerà alcune proprietà bindable richieste:
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); }
}
}
Il prossimo passo sarà la creazione di un renderer per ogni piattaforma.
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;
}
}
}
E Android (l'elenco di Android non ha scroll se tutti gli elementi sono posizionati sullo schermo, quindi non disabiliteremo lo scrolling, ma siamo comunque in grado di utilizzare le proprietà native):
[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
proprietà Element
del renderer è il controllo SuperListView
dal progetto PCL.
Control
proprietà di Control
del renderer è il controllo nativo. Android.Widget.ListView
per Android e UIKit.UITableView
per iOS.
E come lo useremo 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>
file .cs
della pagina:
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);
}
}
Renderizzatore personalizzato per BoxView
Aiuto Renderer personalizzato per consentire di aggiungere nuove proprietà e renderle in modo diverso nella piattaforma nativa che non può essere altrimenti attraverso il codice condiviso. In questo esempio aggiungeremo raggio e ombra a un boxview.
In primo luogo, dovremmo creare un controllo personalizzato nel progetto PCL, che dichiarerà alcune proprietà bindable richieste:
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); }
}
}
}
Il prossimo passo sarà la creazione di un renderer per ogni piattaforma.
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);
}
}
}
}
Anche in questo caso è possibile personalizzare comunque come si desidera all'interno del metodo di disegno.
E lo stesso per 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);
}
}
}
Lo XAML:
Per prima cosa facciamo riferimento al nostro controllo con lo spazio dei nomi definito in precedenza.
xmlns:Controls="clr-namespace:Mobile.Controls"
Quindi usiamo il controllo come segue e usiamo le proprietà definite all'inizio:
<Controls:ExtendedBoxView
x:Name="search_boxview"
Color="#444"
BorderRadius="5"
HorizontalOptions="CenterAndExpand"
/>
Accesso al renderer da un progetto nativo
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
Etichetta arrotondata con un renderizzatore personalizzato per Frame (parti PCL e iOS)
Primo passo: parte PCL
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); }
}
}
}
Secondo passo: parte iOS
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;
}
}
}
}
Terzo passo: codice XAML per chiamare un ExtendedFrame
Se vuoi usarlo in una parte XAML, non dimenticare di scrivere questo:
xmlns:controls="clr-namespace:ProjectNamespace;assembly:ProjectNamespace"
dopo
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
Ora puoi usare ExtendedFrame in questo modo:
<controls:ExtendedFrame
VerticalOptions="FillAndExpand"
HorizontalOptions="FillAndExpand"
BackgroundColor="Gray"
CornerRadius="35.0">
<Frame.Content>
<Label
Text="MyText"
TextColor="Blue"/>
</Frame.Content>
</controls:ExtendedFrame>
BoxView arrotondato con colore di sfondo selezionabile
Primo passo: parte PCL
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);
}
}
}
Secondo passo: parte Droid
[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);
}
}
}
Terzo passo: parte iOS
[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();
}
}
}
}