Xamarin.Forms
사용자 지정 컨트롤 만들기
수색…
Xamarin Forms 사용자 지정 입력 컨트롤 만들기 (네이티브 필요 없음)
아래는 순수한 Xamarin Forms 사용자 정의 컨트롤의 예입니다. 이 경우 사용자 지정 렌더링은 수행되지 않지만 쉽게 구현할 수 있습니다. 사실 내 자신의 코드에서이 Label
과 Entry
모두에 대해 사용자 지정 렌더러와 동일한 컨트롤을 사용합니다.
사용자 정의 컨트롤은 Label
, Entry
및 BoxView
가 포함 된 ContentView
로서 2 StackLayout
사용하여 제자리에 고정되어 있습니다. TextChanged
이벤트뿐 아니라 여러 바인딩 가능 속성도 정의합니다.
사용자 정의 Y 인드 가능 등록 정보는 아래에 정의 된대로 정의되며 (이 경우 Label
W Entry
) 제어 내의 요소가 사용자 정의 Y 인드 가능 등록 정보에 Y 인드 됨으로써 작동합니다. 바인딩 가능한 속성에 대한 일부는 바운드 된 요소가 값을 변경하도록 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
속성을 바인딩 한 후에도 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
속성이 다시 작성됩니다.
그리고 내가 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 Forms Entry
컨트롤에는 MaxLength
속성이 없습니다. 이 작업을 수행하려면 Bindable MaxLength
속성을 추가하여 Entry
를 아래와 같이 확장 할 수 있습니다. 그런 다음 Entry
에서 TextChanged
이벤트를 구독하고 Text
가 호출 될 때 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>