Native Ansichten in C#

Beispiel herunterladen Das Beispiel herunterladen

Native Ansichten von iOS, Android und UWP können direkt von Xamarin.Forms Seiten referenziert werden, die mit C# erstellt wurden. In diesem Artikel wird veranschaulicht, wie Sie einem Xamarin.Forms mit C# erstellten Layout native Ansichten hinzufügen und das Layout benutzerdefinierter Ansichten überschreiben, um die Verwendung der Mess-API zu korrigieren.

Übersicht

Jedes Xamarin.Forms Steuerelement, das festgelegt werden kann Content oder über eine Children Sammlung verfügt, kann plattformspezifische Ansichten hinzufügen. Beispielsweise kann der -Eigenschaft oder der ContentView.ContentStackLayout.Children Auflistung ein iOS-Element UILabel direkt hinzugefügt werden. Beachten Sie jedoch, dass diese Funktionalität die Verwendung von #if defines in Xamarin.Forms Shared Project-Lösungen erfordert und nicht in Xamarin.Forms .NET Standard-Bibliothekslösungen verfügbar ist.

Die folgenden Screenshots veranschaulichen plattformspezifische Ansichten, die einem Xamarin.FormsStackLayouthinzugefügt wurden:

StackLayout mit Platform-Specific Ansichten

Die Möglichkeit, einem Xamarin.Forms Layout plattformspezifische Ansichten hinzuzufügen, wird durch zwei Erweiterungsmethoden auf jeder Plattform ermöglicht:

  • Add – fügt der Children Auflistung eines Layouts eine plattformspezifische Ansicht hinzu.
  • ToView – übernimmt eine plattformspezifische Ansicht und umschließt sie als eine Xamarin.FormsView , die Content als Eigenschaft eines Steuerelements festgelegt werden kann.

Wenn Sie diese Methoden in einem freigegebenen Xamarin.Forms Projekt verwenden, müssen Sie den entsprechenden plattformspezifischen Xamarin.Forms Namespace importieren:

  • iOS: Xamarin.Forms.Platform.iOS
  • Android: Xamarin.Forms.Platform.Android
  • Universelle Windows-Plattform (UWP) : Xamarin.Forms.Platform.UWP

Hinzufügen Platform-Specific Ansichten auf jeder Plattform

In den folgenden Abschnitten wird veranschaulicht, wie sie einem Xamarin.Forms Layout auf jeder Plattform plattformspezifische Ansichten hinzufügen.

iOS

Im folgenden Codebeispiel wird veranschaulicht, wie ein UILabel und ein StackLayoutContentViewhinzugefügt wird:

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

Im Beispiel wird davon ausgegangen, dass die stackLayout Instanzen und contentView zuvor in XAML oder C# erstellt wurden.

Android

Im folgenden Codebeispiel wird veranschaulicht, wie ein TextView und ein StackLayoutContentViewhinzugefügt wird:

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

Im Beispiel wird davon ausgegangen, dass die stackLayout Instanzen und contentView zuvor in XAML oder C# erstellt wurden.

Universelle Windows-Plattform

Im folgenden Codebeispiel wird veranschaulicht, wie ein TextBlock und ein StackLayoutContentViewhinzugefügt wird:

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

Im Beispiel wird davon ausgegangen, dass die stackLayout Instanzen und contentView zuvor in XAML oder C# erstellt wurden.

Überschreiben von Plattformmessungen für benutzerdefinierte Ansichten

Benutzerdefinierte Ansichten auf jeder Plattform implementieren die Messung häufig nur ordnungsgemäß für das Layoutszenario, für das sie entworfen wurden. Beispielsweise kann eine benutzerdefinierte Ansicht so konzipiert worden sein, dass sie nur die Hälfte der verfügbaren Breite des Geräts belegt. Nach der Freigabe für andere Benutzer ist die benutzerdefinierte Ansicht jedoch möglicherweise erforderlich, um die volle verfügbare Breite des Geräts zu belegen. Daher kann es erforderlich sein, eine Implementierung für die Messung benutzerdefinierter Ansichten zu überschreiben, wenn sie in einem Xamarin.Forms Layout wiederverwendet wird. Aus diesem Grund stellen die Add Erweiterungsmethoden und ToView Außerkraftsetzungen bereit, mit denen Messdelegaten angegeben werden können, die das Layout der benutzerdefinierten Ansicht überschreiben können, wenn es einem Xamarin.Forms Layout hinzugefügt wird.

In den folgenden Abschnitten wird veranschaulicht, wie Das Layout benutzerdefinierter Ansichten überschrieben wird, um die Verwendung der Mess-API zu korrigieren.

iOS

Das folgende Codebeispiel zeigt die CustomControl Klasse, die von UILabelerbt:

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);
  }
}

Wie im folgenden Codebeispiel veranschaulicht, wird eine StackLayoutinstance dieser Ansicht hinzugefügt:

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);

Da die CustomControl.SizeThatFits Überschreibung jedoch immer eine Höhe von 150 zurückgibt, wird die Ansicht mit leerer Fläche darüber und darunter angezeigt, wie im folgenden Screenshot gezeigt:

iOS CustomControl mit fehlerhafter GrößeThatFits-Implementierung

Eine Lösung für dieses Problem besteht darin, eine GetDesiredSizeDelegate Implementierung bereitzustellen, wie im folgenden Codebeispiel veranschaulicht:

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));
}

Diese Methode verwendet die von der CustomControl.SizeThatFits -Methode bereitgestellte Breite, ersetzt jedoch die Höhe von 150 durch eine Höhe von 70. Wenn der CustomControl instance hinzugefügt StackLayoutwird, kann die FixSize -Methode als die angegeben werden, um die GetDesiredSizeDelegate fehlerhafte Messung zu beheben, die von der CustomControl -Klasse bereitgestellt wird:

stackLayout.Children.Add (customControl, FixSize);

Dies führt dazu, dass die benutzerdefinierte Ansicht ordnungsgemäß angezeigt wird, ohne leerer Platz darüber und darunter, wie im folgenden Screenshot gezeigt:

iOS CustomControl mit GetDesiredSize Override

Android

Das folgende Codebeispiel zeigt die CustomControl Klasse, die von TextViewerbt:

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);
  }
}

Wie im folgenden Codebeispiel veranschaulicht, wird eine StackLayoutinstance dieser Ansicht hinzugefügt:

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);

Da die CustomControl.OnMeasure Überschreibung jedoch immer die Hälfte der angeforderten Breite zurückgibt, wird die Ansicht nur mit der Hälfte der verfügbaren Breite des Geräts angezeigt, wie im folgenden Screenshot gezeigt:

Android CustomControl mit bad OnMeasure-Implementierung

Eine Lösung für dieses Problem besteht darin, eine GetDesiredSizeDelegate Implementierung bereitzustellen, wie im folgenden Codebeispiel veranschaulicht:

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));
}

Diese Methode verwendet die von der CustomControl.OnMeasure -Methode bereitgestellte Breite, multipliziert sie jedoch mit zwei. Wenn der CustomControl instance hinzugefügt StackLayoutwird, kann die FixSize -Methode als die angegeben werden, um die GetDesiredSizeDelegate fehlerhafte Messung zu beheben, die von der CustomControl -Klasse bereitgestellt wird:

stackLayout.Children.Add (customControl, FixSize);

Dies führt dazu, dass die benutzerdefinierte Ansicht ordnungsgemäß angezeigt wird und die Breite des Geräts belegt, wie im folgenden Screenshot gezeigt:

Android CustomControl mit benutzerdefiniertem GetDesiredSize Delegate

Universelle Windows-Plattform

Das folgende Codebeispiel zeigt die CustomControl Klasse, die von Panelerbt:

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);
  }
}

Wie im folgenden Codebeispiel veranschaulicht, wird eine StackLayoutinstance dieser Ansicht hinzugefügt:

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

Da die CustomControl.ArrangeOverride Überschreibung jedoch immer die Hälfte der angeforderten Breite zurückgibt, wird die Ansicht auf die Hälfte der verfügbaren Breite des Geräts gekappt, wie im folgenden Screenshot gezeigt:

UWP CustomControl mit Bad ArrangeOverride-Implementierung

Eine Lösung für dieses Problem besteht in der Bereitstellung einer ArrangeOverrideDelegate Implementierung beim Hinzufügen der Ansicht zu StackLayout, wie im folgenden Codebeispiel veranschaulicht:

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;
});

Diese Methode verwendet die von der CustomControl.ArrangeOverride -Methode bereitgestellte Breite, multipliziert sie jedoch mit zwei. Dies führt dazu, dass die benutzerdefinierte Ansicht ordnungsgemäß angezeigt wird und die Breite des Geräts belegt, wie im folgenden Screenshot gezeigt:

UWP CustomControl mit ArrangeOverride Delegate

Zusammenfassung

In diesem Artikel wurde erläutert, wie Sie einem Xamarin.Forms mit C# erstellten Layout native Ansichten hinzufügen und das Layout benutzerdefinierter Ansichten überschreiben, um die Verwendung der Mess-API zu korrigieren.