Visualizzazioni native in C#

Download Sample Scaricare l'esempio

È possibile fare riferimento direttamente alle visualizzazioni native da iOS, Android e UWP dalle Xamarin.Forms pagine create con C#. Questo articolo illustra come aggiungere visualizzazioni native a un Xamarin.Forms layout creato con C# e come eseguire l'override del layout delle visualizzazioni personalizzate per correggere l'utilizzo dell'API di misurazione.

Panoramica

Qualsiasi Xamarin.Forms controllo che consente di Content impostare o che dispone di una Children raccolta può aggiungere visualizzazioni specifiche della piattaforma. Ad esempio, un iOS UILabel può essere aggiunto direttamente alla ContentView.Content proprietà o alla StackLayout.Children raccolta. Si noti tuttavia che questa funzionalità richiede l'uso di #if definisce nelle Xamarin.Forms soluzioni di progetto condiviso e non è disponibile dalle Xamarin.Forms soluzioni di libreria .NET Standard.

Gli screenshot seguenti illustrano le visualizzazioni specifiche della piattaforma aggiunte a un oggetto Xamarin.FormsStackLayout:

StackLayout Containing Platform-Specific Views

La possibilità di aggiungere visualizzazioni specifiche della piattaforma a un Xamarin.Forms layout è abilitata da due metodi di estensione in ogni piattaforma:

  • Add : aggiunge una visualizzazione specifica della Children piattaforma alla raccolta di un layout.
  • ToView : accetta una visualizzazione specifica della piattaforma e la esegue il wrapping come oggetto Xamarin.FormsView che può essere impostato come Content proprietà di un controllo.

L'uso di questi metodi in un Xamarin.Forms progetto condiviso richiede l'importazione dello spazio dei nomi specifico Xamarin.Forms della piattaforma appropriato:

  • iOS : Xamarin.Forms. Platform.iOS
  • Android : Xamarin.Forms. Platform.Android
  • piattaforma UWP (Universal Windows Platform) (UWP) - Xamarin.Forms. Platform.UWP

Aggiunta di viste specifiche della piattaforma in ogni piattaforma

Le sezioni seguenti illustrano come aggiungere visualizzazioni specifiche della piattaforma a un Xamarin.Forms layout in ogni piattaforma.

iOS

Nell'esempio di codice seguente viene illustrato come aggiungere un UILabel oggetto a StackLayout e a ContentView:

var uiLabel = new UILabel {
  MinimumFontSize = 14f,
  Lines = 0,
  LineBreakMode = UILineBreakMode.WordWrap,
  Text = originalText,
};
stackLayout.Children.Add (uiLabel);
contentView.Content = uiLabel.ToView();

L'esempio presuppone che le stackLayout istanze e contentView siano state create in precedenza in XAML o C#.

Android

Nell'esempio di codice seguente viene illustrato come aggiungere un TextView oggetto a StackLayout e a ContentView:

var textView = new TextView (MainActivity.Instance) { Text = originalText, TextSize = 14 };
stackLayout.Children.Add (textView);
contentView.Content = textView.ToView();

L'esempio presuppone che le stackLayout istanze e contentView siano state create in precedenza in XAML o C#.

Piattaforma UWP (Universal Windows Platform)

Nell'esempio di codice seguente viene illustrato come aggiungere un TextBlock oggetto a StackLayout e a ContentView:

var textBlock = new TextBlock
{
    Text = originalText,
    FontSize = 14,
    FontFamily = new FontFamily("HelveticaNeue"),
    TextWrapping = TextWrapping.Wrap
};
stackLayout.Children.Add(textBlock);
contentView.Content = textBlock.ToView();

L'esempio presuppone che le stackLayout istanze e contentView siano state create in precedenza in XAML o C#.

Override delle misurazioni della piattaforma per le visualizzazioni personalizzate

Le visualizzazioni personalizzate in ogni piattaforma spesso implementano correttamente solo la misurazione per lo scenario di layout per il quale sono stati progettati. Ad esempio, una visualizzazione personalizzata può essere stata progettata per occupare solo la metà della larghezza disponibile del dispositivo. Tuttavia, dopo essere stato condiviso con altri utenti, la visualizzazione personalizzata potrebbe essere necessaria per occupare la larghezza completa disponibile del dispositivo. Pertanto, può essere necessario eseguire l'override di un'implementazione di misurazione delle visualizzazioni personalizzate quando viene riutilizzato in un Xamarin.Forms layout. Per questo motivo, i Add metodi di estensione e ToView forniscono sostituzioni che consentono di specificare delegati di misurazione, che possono eseguire l'override del layout di visualizzazione personalizzato quando viene aggiunto a un Xamarin.Forms layout.

Le sezioni seguenti illustrano come eseguire l'override del layout delle visualizzazioni personalizzate per correggere l'utilizzo dell'API di misurazione.

iOS

Nell'esempio di codice seguente viene illustrata la CustomControl classe , che eredita da UILabel:

public class CustomControl : UILabel
{
  public override string Text {
    get { return base.Text; }
    set { base.Text = value.ToUpper (); }
  }

  public override CGSize SizeThatFits (CGSize size)
  {
    return new CGSize (size.Width, 150);
  }
}

Un'istanza di questa vista viene aggiunta a un StackLayoutoggetto , come illustrato nell'esempio di codice seguente:

var customControl = new CustomControl {
  MinimumFontSize = 14,
  Lines = 0,
  LineBreakMode = UILineBreakMode.WordWrap,
  Text = "This control has incorrect sizing - there's empty space above and below it."
};
stackLayout.Children.Add (customControl);

Tuttavia, poiché l'override CustomControl.SizeThatFits restituisce sempre un'altezza pari a 150, la visualizzazione verrà visualizzata con spazio vuoto sopra e sotto di esso, come illustrato nello screenshot seguente:

iOS CustomControl with Bad SizeThatFits Implementation

Una soluzione a questo problema consiste nel fornire un'implementazione GetDesiredSizeDelegate , come illustrato nell'esempio di codice seguente:

SizeRequest? FixSize (NativeViewWrapperRenderer renderer, double width, double height)
{
  var uiView = renderer.Control;

  if (uiView == null) {
    return null;
  }

  var constraint = new CGSize (width, height);

  // Let the CustomControl determine its size (which will be wrong)
  var badRect = uiView.SizeThatFits (constraint);

  // Use the width and substitute the height
  return new SizeRequest (new Size (badRect.Width, 70));
}

Questo metodo usa la larghezza fornita dal CustomControl.SizeThatFits metodo , ma sostituisce l'altezza di 150 per un'altezza pari a 70. Quando l'istanza CustomControl viene aggiunta a StackLayout, il FixSize metodo può essere specificato come per GetDesiredSizeDelegate correggere la misurazione non valida fornita dalla CustomControl classe :

stackLayout.Children.Add (customControl, FixSize);

Ciò comporta la visualizzazione personalizzata visualizzata correttamente, senza spazio vuoto sopra e sotto di esso, come illustrato nello screenshot seguente:

iOS CustomControl with GetDesiredSize Override

Android

Nell'esempio di codice seguente viene illustrata la CustomControl classe , che eredita da TextView:

public class CustomControl : TextView
{
  public CustomControl (Context context) : base (context)
  {
  }

  protected override void OnMeasure (int widthMeasureSpec, int heightMeasureSpec)
  {
    int width = MeasureSpec.GetSize (widthMeasureSpec);

    // Force the width to half of what's been requested.
    // This is deliberately wrong to demonstrate providing an override to fix it with.
    int widthSpec = MeasureSpec.MakeMeasureSpec (width / 2, MeasureSpec.GetMode (widthMeasureSpec));

    base.OnMeasure (widthSpec, heightMeasureSpec);
  }
}

Un'istanza di questa vista viene aggiunta a un StackLayoutoggetto , come illustrato nell'esempio di codice seguente:

var customControl = new CustomControl (MainActivity.Instance) {
  Text = "This control has incorrect sizing - it doesn't occupy the available width of the device.",
  TextSize = 14
};
stackLayout.Children.Add (customControl);

Tuttavia, poiché l'override CustomControl.OnMeasure restituisce sempre metà della larghezza richiesta, la visualizzazione verrà visualizzata occupando solo la metà della larghezza disponibile del dispositivo, come illustrato nello screenshot seguente:

Android CustomControl with Bad OnMeasure Implementation

Una soluzione a questo problema consiste nel fornire un'implementazione GetDesiredSizeDelegate , come illustrato nell'esempio di codice seguente:

SizeRequest? FixSize (NativeViewWrapperRenderer renderer, int widthConstraint, int heightConstraint)
{
  var nativeView = renderer.Control;

  if ((widthConstraint == 0 && heightConstraint == 0) || nativeView == null) {
    return null;
  }

  int width = Android.Views.View.MeasureSpec.GetSize (widthConstraint);
  int widthSpec = Android.Views.View.MeasureSpec.MakeMeasureSpec (
    width * 2, Android.Views.View.MeasureSpec.GetMode (widthConstraint));
  nativeView.Measure (widthSpec, heightConstraint);
  return new SizeRequest (new Size (nativeView.MeasuredWidth, nativeView.MeasuredHeight));
}

Questo metodo usa la larghezza fornita dal CustomControl.OnMeasure metodo , ma la moltiplica per due. Quando l'istanza CustomControl viene aggiunta a StackLayout, il FixSize metodo può essere specificato come per GetDesiredSizeDelegate correggere la misurazione non valida fornita dalla CustomControl classe :

stackLayout.Children.Add (customControl, FixSize);

Ciò comporta la visualizzazione personalizzata visualizzata correttamente, occupando la larghezza del dispositivo, come illustrato nello screenshot seguente:

Android CustomControl with Custom GetDesiredSize Delegate

Piattaforma UWP (Universal Windows Platform)

Nell'esempio di codice seguente viene illustrata la CustomControl classe , che eredita da Panel:

public class CustomControl : Panel
{
  public static readonly DependencyProperty TextProperty =
    DependencyProperty.Register(
      "Text", typeof(string), typeof(CustomControl), new PropertyMetadata(default(string), OnTextPropertyChanged));

  public string Text
  {
    get { return (string)GetValue(TextProperty); }
    set { SetValue(TextProperty, value.ToUpper()); }
  }

  readonly TextBlock textBlock;

  public CustomControl()
  {
    textBlock = new TextBlock
    {
      MinHeight = 0,
      MaxHeight = double.PositiveInfinity,
      MinWidth = 0,
      MaxWidth = double.PositiveInfinity,
      FontSize = 14,
      TextWrapping = TextWrapping.Wrap,
      VerticalAlignment = VerticalAlignment.Center
    };

    Children.Add(textBlock);
  }

  static void OnTextPropertyChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs args)
  {
    ((CustomControl)dependencyObject).textBlock.Text = (string)args.NewValue;
  }

  protected override Size ArrangeOverride(Size finalSize)
  {
      // This is deliberately wrong to demonstrate providing an override to fix it with.
      textBlock.Arrange(new Rect(0, 0, finalSize.Width/2, finalSize.Height));
      return finalSize;
  }

  protected override Size MeasureOverride(Size availableSize)
  {
      textBlock.Measure(availableSize);
      return new Size(textBlock.DesiredSize.Width, textBlock.DesiredSize.Height);
  }
}

Un'istanza di questa vista viene aggiunta a un StackLayoutoggetto , come illustrato nell'esempio di codice seguente:

var brokenControl = new CustomControl {
  Text = "This control has incorrect sizing - it doesn't occupy the available width of the device."
};
stackLayout.Children.Add(brokenControl);

Tuttavia, poiché l'override CustomControl.ArrangeOverride restituisce sempre metà della larghezza richiesta, la visualizzazione verrà ritagliata a metà della larghezza disponibile del dispositivo, come illustrato nello screenshot seguente:

UWP CustomControl with Bad ArrangeOverride Implementation

Una soluzione a questo problema consiste nel fornire un'implementazione ArrangeOverrideDelegate quando si aggiunge la visualizzazione a StackLayout, come illustrato nell'esempio di codice seguente:

stackLayout.Children.Add(fixedControl, arrangeOverrideDelegate: (renderer, finalSize) =>
{
    if (finalSize.Width <= 0 || double.IsInfinity(finalSize.Width))
    {
        return null;
    }
    var frameworkElement = renderer.Control;
    frameworkElement.Arrange(new Rect(0, 0, finalSize.Width * 2, finalSize.Height));
    return finalSize;
});

Questo metodo usa la larghezza fornita dal CustomControl.ArrangeOverride metodo , ma la moltiplica per due. Ciò comporta la visualizzazione personalizzata visualizzata correttamente, occupando la larghezza del dispositivo, come illustrato nello screenshot seguente:

UWP CustomControl with ArrangeOverride Delegate

Riepilogo

Questo articolo ha illustrato come aggiungere visualizzazioni native a un Xamarin.Forms layout creato con C# e come eseguire l'override del layout delle visualizzazioni personalizzate per correggere l'utilizzo dell'API di misurazione.