Xamarin.Forms
カスタムコントロールの作成
サーチ…
Xamarin Formsカスタム入力コントロールを作成する(ネイティブは必要ありません)
以下は、純粋なXamarinフォームカスタムコントロールの例です。これに対してカスタムレンダリングは実行されていませんが、実際には自分のコードで簡単に実装することができます。 Label
とEntry
両方に対してカスタムレンダラーと同じコントロールを使用します。
カスタムコントロールは、 ContentView
とLabel
、 Entry
、およびBoxView
2使用して所定の位置に保持され、その中に、 StackLayout
秒。また、複数のバインド可能なプロパティとTextChanged
イベントを定義します。
カスタムのバインド可能なプロパティは、以下のように定義され、コントロール内の要素(この場合はLabel
とEntry
)がカスタムのバインド可能なプロパティにバインドされることによって機能します。 BindingPropertyChangedDelegate
を実装する必要があります。
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;
}
}
そして、ここにiOSの最終製品の写真があります(画像には、iOSでボーダーを削除して両方の要素のカスタムフォントを指定するために使用されているLabel
とEntry
カスタムレンダラを使用したときの様子が表示されます)。
UnderlineColor
変更されたときにBoxView.BackgroundColor
が変更されるというBoxView.BackgroundColor
しました。 BoxView
のBackgroundColor
プロパティをバインドした後でも、 UnderlineColorChanged
デリゲートを追加するまでは変更されません。
バインド可能なスパンのコレクションでラベル付けする
FormattedText
プロパティの周りにラッパーを持つカスタムラベルを作成しました:
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;
}
}
カスタムTextComponent
のコレクションを追加しました:
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); }
}
}
テキストコンポーネントのコレクションが変更されたり、別のコンポーネントのText
プロパティが変更されたりすると、ベースLabel
FormattedText
プロパティが再構築FormattedText
ます。
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>
ページのコードビハインド:
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;
});
}
}
MaxLengthプロパティを使用してカスタムEntryコントロールを作成する
XamarinフォームEntry
コントロールには、 MaxLength
プロパティはありません。これを実現するには、Bindable MaxLength
プロパティを追加して、以下のようにEntry
を拡張できます。次に、 Entry
のTextChanged
イベントを購読し、これが呼び出されたときにText
の長さを検証するだけです。
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;
}
}
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>