Xamarin.Forms
Benutzerdefinierte Steuerelemente erstellen
Suche…
Erstellen Sie ein benutzerdefiniertes Steuerelement für Xamarin Forms (keine native Eingabe erforderlich).
Nachfolgend finden Sie ein Beispiel für ein reines benutzerdefiniertes Steuerelement von Xamarin Forms. Hierfür wird kein benutzerdefiniertes Rendering ausgeführt, könnte jedoch leicht in meinem eigenen Code implementiert werden. Ich verwende dieses Steuerelement zusammen mit einem benutzerdefinierten Renderer für Label
und Entry
.
Die individuelle Steuerung ist ein ContentView
mit einem Label
, Entry
, und ein BoxView
innerhalb es an Ort und Stelle gehalten wird , unter Verwendung von 2 StackLayout
s. Wir definieren auch mehrere bindbare Eigenschaften sowie ein TextChanged
Ereignis.
Die benutzerdefinierten bindbaren Eigenschaften funktionieren, indem sie wie folgt definiert werden und die Elemente innerhalb des Steuerelements (in diesem Fall eine Label
und ein Entry
) an die benutzerdefinierten bindbaren Eigenschaften gebunden sind. Einige Eigenschaften der bindbaren Eigenschaften müssen auch ein BindingPropertyChangedDelegate
implementieren, damit die gebundenen Elemente ihre Werte ändern.
public class InputFieldContentView : ContentView {
#region Properties
/// <summary>
/// Attached to the <c>InputFieldContentView</c>'s <c>ExtendedEntryOnTextChanged()</c> event, but returns the <c>sender</c> as <c>InputFieldContentView</c>.
/// </summary>
public event System.EventHandler<TextChangedEventArgs> OnContentViewTextChangedEvent; //In OnContentViewTextChangedEvent() we return our custom InputFieldContentView control as the sender but we could have returned the Entry itself as the sender if we wanted to do that instead.
public static readonly BindableProperty LabelTextProperty = BindableProperty.Create("LabelText", typeof(string), typeof(InputFieldContentView), string.Empty);
public string LabelText {
get { return (string)GetValue(LabelTextProperty); }
set { SetValue(LabelTextProperty, value); }
}
public static readonly BindableProperty LabelColorProperty = BindableProperty.Create("LabelColor", typeof(Color), typeof(InputFieldContentView), Color.Default);
public Color LabelColor {
get { return (Color)GetValue(LabelColorProperty); }
set { SetValue(LabelColorProperty, value); }
}
public static readonly BindableProperty EntryTextProperty = BindableProperty.Create("EntryText", typeof(string), typeof(InputFieldContentView), string.Empty, BindingMode.TwoWay, null, OnEntryTextChanged);
public string EntryText {
get { return (string)GetValue(EntryTextProperty); }
set { SetValue(EntryTextProperty, value); }
}
public static readonly BindableProperty PlaceholderTextProperty = BindableProperty.Create("PlaceholderText", typeof(string), typeof(InputFieldContentView), string.Empty);
public string PlaceholderText {
get { return (string)GetValue(PlaceholderTextProperty); }
set { SetValue(PlaceholderTextProperty, value); }
}
public static readonly BindableProperty UnderlineColorProperty = BindableProperty.Create("UnderlineColor", typeof(Color), typeof(InputFieldContentView), Color.Black, BindingMode.TwoWay, null, UnderlineColorChanged);
public Color UnderlineColor {
get { return (Color)GetValue(UnderlineColorProperty); }
set { SetValue(UnderlineColorProperty, value); }
}
private BoxView _underline;
#endregion
public InputFieldContentView() {
BackgroundColor = Color.Transparent;
HorizontalOptions = LayoutOptions.FillAndExpand;
Label label = new Label {
BindingContext = this,
HorizontalOptions = LayoutOptions.StartAndExpand,
VerticalOptions = LayoutOptions.Center,
TextColor = Color.Black
};
label.SetBinding(Label.TextProperty, (InputFieldContentView view) => view.LabelText, BindingMode.TwoWay);
label.SetBinding(Label.TextColorProperty, (InputFieldContentView view) => view.LabelColor, BindingMode.TwoWay);
Entry entry = new Entry {
BindingContext = this,
HorizontalOptions = LayoutOptions.End,
TextColor = Color.Black,
HorizontalTextAlignment = TextAlignment.End
};
entry.SetBinding(Entry.PlaceholderProperty, (InputFieldContentView view) => view.PlaceholderText, BindingMode.TwoWay);
entry.SetBinding(Entry.TextProperty, (InputFieldContentView view) => view.EntryText, BindingMode.TwoWay);
entry.TextChanged += OnTextChangedEvent;
_underline = new BoxView {
BackgroundColor = Color.Black,
HeightRequest = 1,
HorizontalOptions = LayoutOptions.FillAndExpand
};
Content = new StackLayout {
Spacing = 0,
HorizontalOptions = LayoutOptions.FillAndExpand,
Children = {
new StackLayout {
Padding = new Thickness(5, 0),
Spacing = 0,
HorizontalOptions = LayoutOptions.FillAndExpand,
Orientation = StackOrientation.Horizontal,
Children = { label, entry }
}, _underline
}
};
SizeChanged += (sender, args) => entry.WidthRequest = Width * 0.5 - 10;
}
private static void OnEntryTextChanged(BindableObject bindable, object oldValue, object newValue) {
InputFieldContentView contentView = (InputFieldContentView)bindable;
contentView.EntryText = (string)newValue;
}
private void OnTextChangedEvent(object sender, TextChangedEventArgs args) {
if(OnContentViewTextChangedEvent != null) { OnContentViewTextChangedEvent(this, new TextChangedEventArgs(args.OldTextValue, args.NewTextValue)); } //Here is where we pass in 'this' (which is the InputFieldContentView) instead of 'sender' (which is the Entry control)
}
private static void UnderlineColorChanged(BindableObject bindable, object oldValue, object newValue) {
InputFieldContentView contentView = (InputFieldContentView)bindable;
contentView._underline.BackgroundColor = (Color)newValue;
}
}
Und hier ist ein Bild des Endprodukts unter iOS (das Bild zeigt, wie es aussieht, wenn ein benutzerdefinierter Renderer für Label
und Entry
wird, der zum Entfernen des Rahmens unter iOS und zum Festlegen einer benutzerdefinierten Schriftart für beide Elemente verwendet wird):
Ein Problem, dem ich BoxView.BackgroundColor
, war, BoxView.BackgroundColor
zu ändern, wenn sich UnderlineColor
änderte. Selbst nach dem Binden der BackgroundColor
Eigenschaft von BoxView
wird diese BoxView
nicht geändert, bis ich den UnderlineColorChanged
Delegaten hinzugefügt habe.
Beschriftung mit bindbarer Sammlung von Feldern
Ich habe ein benutzerdefiniertes Label mit Wrapper für die FormattedText
Eigenschaft erstellt:
public class MultiComponentLabel : Label
{
public IList<TextComponent> Components { get; set; }
public MultiComponentLabel()
{
var components = new ObservableCollection<TextComponent>();
components.CollectionChanged += OnComponentsChanged;
Components = components;
}
private void OnComponentsChanged(object sender, NotifyCollectionChangedEventArgs e)
{
BuildText();
}
private void OnComponentPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
BuildText();
}
private void BuildText()
{
var formattedString = new FormattedString();
foreach (var component in Components)
{
formattedString.Spans.Add(new Span { Text = component.Text });
component.PropertyChanged -= OnComponentPropertyChanged;
component.PropertyChanged += OnComponentPropertyChanged;
}
FormattedText = formattedString;
}
}
Ich habe eine Sammlung von benutzerdefinierten TextComponent
hinzugefügt:
public class TextComponent : BindableObject
{
public static readonly BindableProperty TextProperty =
BindableProperty.Create(nameof(Text),
typeof(string),
typeof(TextComponent),
default(string));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
}
Und wenn Sammlung von Textkomponenten Änderungen oder Text
- Eigenschaft separater Komponente Änderungen , die ich wieder aufbauen FormattedText
Eigentum von Basis Label
.
Und wie ich 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.MultiComponentLabelPage">
<controls:MultiComponentLabel Margin="0,20,0,0">
<controls:MultiComponentLabel.Components>
<controls:TextComponent Text="Time"/>
<controls:TextComponent Text=": "/>
<controls:TextComponent Text="{Binding CurrentTime, Source={x:Reference Page}}"/>
</controls:MultiComponentLabel.Components>
</controls:MultiComponentLabel>
</ContentPage>
Codebehind der Seite:
public partial class MultiComponentLabelPage : ContentPage
{
private string _currentTime;
public string CurrentTime
{
get { return _currentTime; }
set
{
_currentTime = value;
OnPropertyChanged();
}
}
public MultiComponentLabelPage()
{
InitializeComponent();
BindingContext = this;
}
protected override void OnAppearing()
{
base.OnAppearing();
Device.StartTimer(TimeSpan.FromSeconds(1), () =>
{
CurrentTime = DateTime.Now.ToString("hh : mm : ss");
return true;
});
}
}
Erstellen eines benutzerdefinierten Eintragssteuerelements mit einer MaxLength-Eigenschaft
Das Xamarin Forms Entry
Steuerelement verfügt nicht über eine MaxLength
Eigenschaft. Um dies zu erreichen, können Sie Entry
wie folgt MaxLength
, indem Sie eine Bindable MaxLength
Eigenschaft hinzufügen. Dann müssen Sie nur das TextChanged
Ereignis bei Entry
abonnieren und die Länge des Text
TextChanged
wenn dieser aufgerufen wird:
class CustomEntry : Entry
{
public CustomEntry()
{
base.TextChanged += Validate;
}
public static readonly BindableProperty MaxLengthProperty = BindableProperty.Create(nameof(MaxLength), typeof(int), typeof(CustomEntry), 0);
public int MaxLength
{
get { return (int)GetValue(MaxLengthProperty); }
set { SetValue(MaxLengthProperty, value); }
}
public void Validate(object sender, TextChangedEventArgs args)
{
var e = sender as Entry;
var val = e?.Text;
if (string.IsNullOrEmpty(val))
return;
if (MaxLength > 0 && val.Length > MaxLength)
val = val.Remove(val.Length - 1);
e.Text = val;
}
}
Verwendung in XAML:
<ContentView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:customControls="clr-namespace:CustomControls;assembly=CustomControls"
x:Class="Views.TestView">
<ContentView.Content>
<customControls:CustomEntry MaxLength="10" />
</ContentView.Content>