다음을 통해 공유


XAML의 네이티브 뷰

iOS, Android 및 유니버설 Windows 플랫폼 기본 보기는 XAML 파일에서 Xamarin.Forms 직접 참조할 수 있습니다. 속성 및 이벤트 처리기는 네이티브 뷰에서 설정할 수 있으며 뷰와 Xamarin.Forms 상호 작용할 수 있습니다. 이 문서에서는 XAML 파일에서 Xamarin.Forms 네이티브 뷰를 사용하는 방법을 보여 줍니다.

네이티브 뷰를 XAML 파일에 포함하려면 다음을 Xamarin.Forms 수행합니다.

  1. 네이티브 뷰를 xmlns 포함하는 네임스페이스에 대한 네임스페이스 선언을 XAML 파일에 추가합니다.
  2. XAML 파일에서 네이티브 뷰의 인스턴스를 만듭니다.

Important

네이티브 뷰를 사용하는 모든 XAML 페이지에 대해 컴파일된 XAML을 사용하지 않도록 설정해야 합니다. 이 작업은 XAML 페이지의 코드 숨김 클래스를 특성으로 [XamlCompilation(XamlCompilationOptions.Skip)] 데코레이팅하여 수행할 수 있습니다. XAML 컴파일에 대한 자세한 내용은 에서 XAML 컴파일을 Xamarin.Forms참조하세요.

코드 숨김 파일에서 네이티브 뷰를 참조하려면 SAP(공유 자산 프로젝트)를 사용하고 조건부 컴파일 지시문으로 플랫폼별 코드를 래핑해야 합니다. 자세한 내용은 코드의 네이티브 뷰를 참조하세요.

네이티브 뷰 사용

다음 코드 예제에서는 각 플랫폼에 대한 네이티브 뷰를 다음 Xamarin.FormsContentPage으로 사용하는 방법을 보여 줍니다.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"
        xmlns:androidWidget="clr-namespace:Android.Widget;assembly=Mono.Android;targetPlatform=Android"
        xmlns:androidLocal="clr-namespace:SimpleColorPicker.Droid;assembly=SimpleColorPicker.Droid;targetPlatform=Android"
        xmlns:win="clr-namespace:Windows.UI.Xaml.Controls;assembly=Windows, Version=255.255.255.255,
            Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
        x:Class="NativeViews.NativeViewDemo">
    <StackLayout Margin="20">
        <ios:UILabel Text="Hello World" TextColor="{x:Static ios:UIColor.Red}" View.HorizontalOptions="Start" />
        <androidWidget:TextView Text="Hello World" x:Arguments="{x:Static androidLocal:MainActivity.Instance}" />
        <win:TextBlock Text="Hello World" />
    </StackLayout>
</ContentPage>

네이티브 뷰 네임스페이스 및 clr-namespace assembly 네임스페이스를 지정할 뿐만 아니라 해당 네임스페이 targetPlatform 스도 지정해야 합니다. 이 설정은 , , UWPAndroid( Windows 해당UWP), macOS, GTKTizen또는 WPF.로 설정iOS되어야 합니다. 런타임 시 XAML 파서는 애플리케이션이 실행 중인 플랫폼과 일치하지 않는 XML 네임스페이스 targetPlatform 접두사를 무시합니다.

각 네임스페이스 선언을 사용하여 지정된 네임스페이스의 클래스 또는 구조를 참조할 수 있습니다. 예를 들어 네임스페이스 선언을 ios 사용하여 iOS UIKit 네임스페이스의 클래스 또는 구조를 참조할 수 있습니다. 네이티브 뷰의 속성은 XAML을 통해 설정할 수 있지만 속성 및 개체 형식은 일치해야 합니다. 예를 들어 UILabel.TextColor 속성은 태그 확장 및 네임스페이스를 사용하도록 x:Static 설정 UIColor.Red 됩니다ios.

바인딩 가능한 속성 및 연결된 바인딩 가능 속성은 구문을 사용하여 Class.BindableProperty="value" 네이티브 뷰에서 설정할 수도 있습니다. 각 네이티브 뷰는 클래스에서 파생되는 플랫폼별 NativeViewWrapper 인스턴스에 Xamarin.Forms.View 래핑됩니다. 네이티브 뷰에서 바인딩 가능한 속성 또는 연결된 바인딩 가능 속성을 설정하면 속성 값이 래퍼로 전송됩니다. 예를 들어 네이티브 뷰를 설정 View.HorizontalOptions="Center" 하여 가운데 맞춤 가로 레이아웃을 지정할 수 있습니다.

참고 항목

스타일은 개체에서 지원 BindableProperty 되는 속성만 대상으로 지정할 수 있으므로 네이티브 뷰에서 스타일을 사용할 수 없습니다.

Android 위젯 생성자는 일반적으로 Android Context 개체를 인수로 요구하며 클래스의 정적 속성을 MainActivity 통해 사용할 수 있습니다. 따라서 XAML Context 에서 Android 위젯을 만들 때 태그 확장이 있는 특성을 x:Static 사용하여 x:Arguments 일반적으로 개체를 위젯의 생성자에 전달해야 합니다. 자세한 내용은 네이티브 뷰에 인수 전달을 참조 하세요.

참고 항목

.NET Standard 라이브러리 프로젝트 또는 SAP(공유 자산 프로젝트)에서는 네이티브 뷰 x:Name 의 이름을 지정할 수 없습니다. 이렇게 하면 네이티브 형식의 변수가 생성되므로 컴파일 오류가 발생합니다. 그러나 SAP를 사용하는 경우 네이티브 뷰를 인스턴스에 ContentView 래핑하고 코드 숨김 파일에서 검색할 수 있습니다. 자세한 내용은 코드에서 네이티브 보기를 참조하세요.

네이티브 바인딩

데이터 바인딩은 UI를 해당 데이터 원본과 동기화하는 데 사용되며 애플리케이션이 Xamarin.Forms 데이터를 표시하고 상호 작용하는 방법을 간소화합니다. 원본 개체가 인터페이스를 구현 INotifyPropertyChanged 하는 경우 원본 개체의 변경 내용은 바인딩 프레임워크에 의해 대상 개체에 자동으로 푸시되고 대상 개체의 변경 내용은 필요에 따라 원본 개체에 푸시될 수 있습니다.

네이티브 뷰의 속성은 데이터 바인딩을 사용할 수도 있습니다. 다음 코드 예제에서는 네이티브 뷰의 속성을 사용하여 데이터 바인딩을 보여 줍니다.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"
        xmlns:androidWidget="clr-namespace:Android.Widget;assembly=Mono.Android;targetPlatform=Android"
        xmlns:androidLocal="clr-namespace:SimpleColorPicker.Droid;assembly=SimpleColorPicker.Droid;targetPlatform=Android"
        xmlns:win="clr-namespace:Windows.UI.Xaml.Controls;assembly=Windows, Version=255.255.255.255,
            Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
        xmlns:local="clr-namespace:NativeSwitch"
        x:Class="NativeSwitch.NativeSwitchPage">
    <StackLayout Margin="20">
        <Label Text="Native Views Demo" FontAttributes="Bold" HorizontalOptions="Center" />
        <Entry Placeholder="This Entry is bound to the native switch" IsEnabled="{Binding IsSwitchOn}" />
        <ios:UISwitch On="{Binding Path=IsSwitchOn, Mode=TwoWay, UpdateSourceEventName=ValueChanged}"
            OnTintColor="{x:Static ios:UIColor.Red}"
            ThumbTintColor="{x:Static ios:UIColor.Blue}" />
        <androidWidget:Switch x:Arguments="{x:Static androidLocal:MainActivity.Instance}"
            Checked="{Binding Path=IsSwitchOn, Mode=TwoWay, UpdateSourceEventName=CheckedChange}"
            Text="Enable Entry?" />
        <win:ToggleSwitch Header="Enable Entry?"
            OffContent="No"
            OnContent="Yes"
            IsOn="{Binding IsSwitchOn, Mode=TwoWay, UpdateSourceEventName=Toggled}" />
    </StackLayout>
</ContentPage>

페이지에는 속성이 IsEnabled 속성에 Entry 바인딩되는 항목이 NativeSwitchPageViewModel.IsSwitchOn 포함되어 있습니다. BindingContext 페이지의 코드 숨김 파일에서 클래스의 NativeSwitchPageViewModel 새 인스턴스로 설정되며 ViewModel 클래스는 인터페이스를 INotifyPropertyChanged 구현합니다.

페이지에는 각 플랫폼에 대한 네이티브 스위치도 포함되어 있습니다. 각 네이티브 스위치는 바인딩을 TwoWay 사용하여 속성 값을 업데이트합니다 NativeSwitchPageViewModel.IsSwitchOn . 따라서 스위치가 꺼지면 Entry 해당 스위치가 비활성화되고 스위치가 켜지면 해당 스위치가 Entry 활성화됩니다. 다음 스크린샷은 각 플랫폼에서 이 기능을 보여 줍니다.

네이티브 스위치 사용 안 함네이티브 스위치 사용

네이티브 속성이 iOS에서 KVO(키-값 관찰)를 구현 INotifyPropertyChanged하거나 지원하거나 UWP에 있는 경우 양방향 바인딩이 자동으로 지원됩니다 DependencyProperty . 그러나 많은 네이티브 뷰는 속성 변경 알림을 지원하지 않습니다. 이러한 뷰의 경우 속성 값을 바인딩 식의 일부로 지정할 UpdateSourceEventName 수 있습니다. 이 속성은 대상 속성이 변경될 때 신호를 표시하는 네이티브 뷰의 이벤트 이름으로 설정해야 합니다. 그런 다음 네이티브 스위치의 값이 변경 Binding 되면 사용자가 스위치 값을 변경하고 속성 값이 업데이트된다는 알림이 클래스에 NativeSwitchPageViewModel.IsSwitchOn 표시됩니다.

네이티브 뷰에 인수 전달

생성자 인수는 태그 확장이 있는 특성을 사용하여 네이 x:Arguments 티브 뷰에 x:Static 전달할 수 있습니다. 또한 네이티브 뷰 팩터리 메서드(public static 메서드를 정의하는 클래스 또는 구조체와 동일한 형식의 개체 또는 값을 반환하는 메서드)는 특성을 사용하여 메서드의 이름과 특성을 사용하는 x:FactoryMethod x:Arguments 인수를 지정하여 호출할 수 있습니다.

다음 코드 예제에서는 두 가지 기술을 모두 보여 줍니다.

<ContentPage ...
        xmlns:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"
        xmlns:androidWidget="clr-namespace:Android.Widget;assembly=Mono.Android;targetPlatform=Android"
        xmlns:androidGraphics="clr-namespace:Android.Graphics;assembly=Mono.Android;targetPlatform=Android"
        xmlns:androidLocal="clr-namespace:SimpleColorPicker.Droid;assembly=SimpleColorPicker.Droid;targetPlatform=Android"
        xmlns:winControls="clr-namespace:Windows.UI.Xaml.Controls;assembly=Windows, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
        xmlns:winMedia="clr-namespace:Windows.UI.Xaml.Media;assembly=Windows, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
        xmlns:winText="clr-namespace:Windows.UI.Text;assembly=Windows, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
        xmlns:winui="clr-namespace:Windows.UI;assembly=Windows, Version=255.255.255.255, Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows">
        ...
        <ios:UILabel Text="Simple Native Color Picker" View.HorizontalOptions="Center">
            <ios:UILabel.Font>
                <ios:UIFont x:FactoryMethod="FromName">
                    <x:Arguments>
                        <x:String>Papyrus</x:String>
                        <x:Single>24</x:Single>
                    </x:Arguments>
                </ios:UIFont>
            </ios:UILabel.Font>
        </ios:UILabel>
        <androidWidget:TextView x:Arguments="{x:Static androidLocal:MainActivity.Instance}"
                    Text="Simple Native Color Picker"
                    TextSize="24"
                    View.HorizontalOptions="Center">
            <androidWidget:TextView.Typeface>
                <androidGraphics:Typeface x:FactoryMethod="Create">
                    <x:Arguments>
                        <x:String>cursive</x:String>
                        <androidGraphics:TypefaceStyle>Normal</androidGraphics:TypefaceStyle>
                    </x:Arguments>
                </androidGraphics:Typeface>
            </androidWidget:TextView.Typeface>
        </androidWidget:TextView>
        <winControls:TextBlock Text="Simple Native Color Picker"
                    FontSize="20"
                    FontStyle="{x:Static winText:FontStyle.Italic}"
                    View.HorizontalOptions="Center">
            <winControls:TextBlock.FontFamily>
                <winMedia:FontFamily>
                    <x:Arguments>
                        <x:String>Georgia</x:String>
                    </x:Arguments>
                </winMedia:FontFamily>
            </winControls:TextBlock.FontFamily>
        </winControls:TextBlock>
        ...
</ContentPage>

UIFont.FromName 팩터리 메서드는 iOS에서 UILabel.Font 속성을 새 UIFont 속성으로 설정하는 데 사용됩니다. 이름과 크기는 UIFont 특성의 자식인 메서드 인수에 x:Arguments 의해 지정됩니다.

Typeface.Create 팩터리 메서드는 Android에서 TextView.Typeface 속성을 새 Typeface 속성으로 설정하는 데 사용됩니다. 패밀리 이름과 스타일은 Typeface 특성의 자식인 메서드 인수에 x:Arguments 의해 지정됩니다.

FontFamily 생성자는 속성을 유니버설 Windows 플랫폼(UWP)의 새 FontFamily 속성으로 설정하는 TextBlock.FontFamily 데 사용됩니다. 이름은 FontFamily 특성의 자식인 메서드 인수에 x:Arguments 의해 지정됩니다.

참고 항목

인수는 생성자 또는 팩터리 메서드에 필요한 형식과 일치해야 합니다.

다음 스크린샷은 팩터리 메서드 및 생성자 인수를 지정하여 다른 네이티브 뷰에서 글꼴을 설정한 결과를 보여 줍니다.

네이티브 보기에서 글꼴 설정

XAML에서 인수를 전달하는 방법에 대한 자세한 내용은 XAML에서 인수 전달을 참조하세요.

코드에서 네이티브 뷰 참조

네이티브 뷰의 이름을 특성으로 x:Name 지정할 수는 없지만 네이티브 뷰가 특성 값을 지정 x:Name 하는 자식인 경우 공유 액세스 프로젝트의 코드 숨김 파일에서 XAML 파일에 선언된 네이티브 뷰 인스턴스를 ContentView 검색할 수 있습니다. 그런 다음 코드 숨김 파일의 조건부 컴파일 지시문 내에서 다음을 수행해야 합니다.

  1. ContentView.Content 속성 값을 검색하고 플랫폼별 NativeViewWrapper 형식으로 캐스팅합니다.
  2. 속성을 검색하고 네이 NativeViewWrapper.NativeElement 티브 뷰 형식으로 캐스팅합니다.

그런 다음 네이티브 뷰에서 네이티브 API를 호출하여 원하는 작업을 수행할 수 있습니다. 또한 이 방법은 여러 플랫폼에 대한 여러 XAML 네이티브 뷰가 동일한 ContentView자식이 될 수 있다는 이점을 제공합니다. 다음 코드 예제에서는 이 기술을 보여 줍니다.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"
        xmlns:androidWidget="clr-namespace:Android.Widget;assembly=Mono.Android;targetPlatform=Android"
        xmlns:androidLocal="clr-namespace:SimpleColorPicker.Droid;assembly=SimpleColorPicker.Droid;targetPlatform=Android"
        xmlns:winControls="clr-namespace:Windows.UI.Xaml.Controls;assembly=Windows, Version=255.255.255.255,
            Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
        xmlns:local="clr-namespace:NativeViewInsideContentView"
        x:Class="NativeViewInsideContentView.NativeViewInsideContentViewPage">
    <StackLayout Margin="20">
        <ContentView x:Name="contentViewTextParent" HorizontalOptions="Center" VerticalOptions="CenterAndExpand">
            <ios:UILabel Text="Text in a UILabel" TextColor="{x:Static ios:UIColor.Red}" />
            <androidWidget:TextView x:Arguments="{x:Static androidLocal:MainActivity.Instance}"
                Text="Text in a TextView" />
              <winControls:TextBlock Text="Text in a TextBlock" />
        </ContentView>
        <ContentView x:Name="contentViewButtonParent" HorizontalOptions="Center" VerticalOptions="EndAndExpand">
            <ios:UIButton TouchUpInside="OnButtonTap" View.HorizontalOptions="Center" View.VerticalOptions="Center" />
            <androidWidget:Button x:Arguments="{x:Static androidLocal:MainActivity.Instance}"
                Text="Scale and Rotate Text"
                Click="OnButtonTap" />
            <winControls:Button Content="Scale and Rotate Text" />
        </ContentView>
    </StackLayout>
</ContentPage>

위의 예제에서 각 플랫폼의 네이티브 뷰는 컨트롤의 ContentView 자식이며 x:Name , 특성 값은 코드 숨김에서 검색 ContentView 하는 데 사용됩니다.

public partial class NativeViewInsideContentViewPage : ContentPage
{
    public NativeViewInsideContentViewPage()
    {
        InitializeComponent();

#if __IOS__
        var wrapper = (Xamarin.Forms.Platform.iOS.NativeViewWrapper)contentViewButtonParent.Content;
        var button = (UIKit.UIButton)wrapper.NativeView;
        button.SetTitle("Scale and Rotate Text", UIKit.UIControlState.Normal);
        button.SetTitleColor(UIKit.UIColor.Black, UIKit.UIControlState.Normal);
#endif
#if __ANDROID__
        var wrapper = (Xamarin.Forms.Platform.Android.NativeViewWrapper)contentViewTextParent.Content;
        var textView = (Android.Widget.TextView)wrapper.NativeView;
        textView.SetTextColor(Android.Graphics.Color.Red);
#endif
#if WINDOWS_UWP
        var textWrapper = (Xamarin.Forms.Platform.UWP.NativeViewWrapper)contentViewTextParent.Content;
        var textBlock = (Windows.UI.Xaml.Controls.TextBlock)textWrapper.NativeElement;
        textBlock.Foreground = new Windows.UI.Xaml.Media.SolidColorBrush(Windows.UI.Colors.Red);
        var buttonWrapper = (Xamarin.Forms.Platform.UWP.NativeViewWrapper)contentViewButtonParent.Content;
        var button = (Windows.UI.Xaml.Controls.Button)buttonWrapper.NativeElement;
        button.Click += (sender, args) => OnButtonTap(sender, EventArgs.Empty);
#endif
    }

    async void OnButtonTap(object sender, EventArgs e)
    {
        contentViewButtonParent.Content.IsEnabled = false;
        contentViewTextParent.Content.ScaleTo(2, 2000);
        await contentViewTextParent.Content.RotateTo(360, 2000);
        contentViewTextParent.Content.ScaleTo(1, 2000);
        await contentViewTextParent.Content.RelRotateTo(360, 2000);
        contentViewButtonParent.Content.IsEnabled = true;
    }
}

ContentView.Content 핑된 네이티브 뷰를 플랫폼별 NativeViewWrapper 인스턴스로 검색하기 위해 속성에 액세스합니다. 그런 다음 네 NativeViewWrapper.NativeElement 이티브 뷰를 네이티브 형식으로 검색하기 위해 속성에 액세스합니다. 그런 다음 네이티브 뷰의 API를 호출하여 원하는 작업을 수행합니다.

각 네이티브 단추는 터치 이벤트에 대한 응답으로 대리자를 사용하므로 iOS 및 Android 네이티브 단추는 동일한 OnButtonTap 이벤트 처리기를 공유합니다 EventHandler . 그러나 UWP(유니버설 Windows 플랫폼)는 별도의 RoutedEventHandler값을 사용하며, 이 예제에서는 이벤트 처리기를 사용합니다OnButtonTap. 따라서 네이티브 단추를 클릭하면 OnButtonTap 이벤트 처리기가 실행되어 명명contentViewTextParent된 컨트롤에 ContentView 포함된 네이티브 컨트롤의 크기를 조정하고 회전합니다. 다음 스크린샷은 각 플랫폼에서 발생하는 작업을 보여 줍니다.

네이티브 컨트롤이 포함된 ContentView

하위 클래스 네이티브 뷰

대부분의 iOS 및 Android 네이티브 뷰는 속성이 아닌 메서드를 사용하여 컨트롤을 설정하기 때문에 XAML에서 인스턴스화하기에 적합하지 않습니다. 이 문제의 해결 방법은 속성을 사용하여 컨트롤을 설정하는 더 XAML 친화적인 API를 정의하고 플랫폼 독립적 이벤트를 사용하는 래퍼에서 네이티브 뷰를 서브클래스하는 것입니다. 래핑된 네이티브 뷰를 SAP(공유 자산 프로젝트)에 배치하고 조건부 컴파일 지시문으로 묶거나 플랫폼별 프로젝트에 배치하고 .NET Standard 라이브러리 프로젝트의 XAML에서 참조할 수 있습니다.

다음 코드 예제에서는 서브클래스 네이티브 뷰를 사용하는 페이지를 보여 Xamarin.Forms 줍니다.

<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
        xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
        xmlns:ios="clr-namespace:UIKit;assembly=Xamarin.iOS;targetPlatform=iOS"
        xmlns:iosLocal="clr-namespace:SubclassedNativeControls.iOS;assembly=SubclassedNativeControls.iOS;targetPlatform=iOS"
        xmlns:android="clr-namespace:Android.Widget;assembly=Mono.Android;targetPlatform=Android"
        xmlns:androidLocal="clr-namespace:SimpleColorPicker.Droid;assembly=SimpleColorPicker.Droid;targetPlatform=Android"
        xmlns:androidLocal="clr-namespace:SubclassedNativeControls.Droid;assembly=SubclassedNativeControls.Droid;targetPlatform=Android"
        xmlns:winControls="clr-namespace:Windows.UI.Xaml.Controls;assembly=Windows, Version=255.255.255.255,
            Culture=neutral, PublicKeyToken=null, ContentType=WindowsRuntime;targetPlatform=Windows"
        xmlns:local="clr-namespace:SubclassedNativeControls"
        x:Class="SubclassedNativeControls.SubclassedNativeControlsPage">
    <StackLayout Margin="20">
        <Label Text="Subclassed Native Views Demo" FontAttributes="Bold" HorizontalOptions="Center" />
        <StackLayout Orientation="Horizontal">
          <Label Text="You have chosen:" />
          <Label Text="{Binding SelectedFruit}" />      
        </StackLayout>
        <iosLocal:MyUIPickerView ItemsSource="{Binding Fruits}"
            SelectedItem="{Binding SelectedFruit, Mode=TwoWay, UpdateSourceEventName=SelectedItemChanged}" />
        <androidLocal:MySpinner x:Arguments="{x:Static androidLocal:MainActivity.Instance}"
            ItemsSource="{Binding Fruits}"
            SelectedObject="{Binding SelectedFruit, Mode=TwoWay, UpdateSourceEventName=ItemSelected}" />
        <winControls:ComboBox ItemsSource="{Binding Fruits}"
            SelectedItem="{Binding SelectedFruit, Mode=TwoWay, UpdateSourceEventName=SelectionChanged}" />
    </StackLayout>
</ContentPage>

페이지에는 Label 네이티브 컨트롤에서 사용자가 선택한 과일을 표시하는 과일이 포함되어 있습니다. Label 속성에 바인딩됩니다SubclassedNativeControlsPageViewModel.SelectedFruit. BindingContext 페이지의 코드 숨김 파일에서 클래스의 SubclassedNativeControlsPageViewModel 새 인스턴스로 설정되며 ViewModel 클래스는 인터페이스를 INotifyPropertyChanged 구현합니다.

페이지에는 각 플랫폼에 대한 네이티브 선택기 보기도 포함되어 있습니다. 각 네이티브 뷰는 해당 ItemSource 속성을 SubclassedNativeControlsPageViewModel.Fruits 컬렉션에 바인딩하여 과일 컬렉션을 표시합니다. 이렇게 하면 다음 스크린샷과 같이 사용자가 과일을 선택할 수 있습니다.

서브클래스 네이티브 뷰

iOS 및 Android에서 네이티브 선택기는 메서드를 사용하여 컨트롤을 설치합니다. 따라서 이러한 선택기는 XAML 친화적으로 만들기 위해 속성을 노출하기 위해 서브클래스되어야 합니다. UWP(유니버설 Windows 플랫폼)ComboBox에서는 이미 XAML에 친숙하므로 서브클래싱이 필요하지 않습니다.

iOS

iOS 구현은 뷰를 UIPickerView 서브클래싱하고 XAML에서 쉽게 사용할 수 있는 속성 및 이벤트를 노출합니다.

public class MyUIPickerView : UIPickerView
{
    public event EventHandler<EventArgs> SelectedItemChanged;

    public MyUIPickerView()
    {
        var model = new PickerModel();
        model.ItemChanged += (sender, e) =>
        {
            if (SelectedItemChanged != null)
            {
                SelectedItemChanged.Invoke(this, e);
            }
        };
        Model = model;
    }

    public IList<string> ItemsSource
    {
        get
        {
            var pickerModel = Model as PickerModel;
            return (pickerModel != null) ? pickerModel.Items : null;
        }
        set
        {
            var model = Model as PickerModel;
            if (model != null)
            {
                model.Items = value;
            }
        }
    }

    public string SelectedItem
    {
        get { return (Model as PickerModel).SelectedItem; }
        set { }
    }
}

클래스는 MyUIPickerView 이벤트와 SelectedItem 속성을 노출 ItemsSource 합니다SelectedItemChanged. A UIPickerView 에는 속성 및 이벤트에서 액세스 MyUIPickerView 하는 기본 UIPickerViewModel 데이터 모델이 필요합니다. UIPickerViewModel 데이터 모델은 클래스에서 PickerModel 제공합니다.

class PickerModel : UIPickerViewModel
{
    int selectedIndex = 0;
    public event EventHandler<EventArgs> ItemChanged;
    public IList<string> Items { get; set; }

    public string SelectedItem
    {
        get
        {
            return Items != null && selectedIndex >= 0 && selectedIndex < Items.Count ? Items[selectedIndex] : null;
        }
    }

    public override nint GetRowsInComponent(UIPickerView pickerView, nint component)
    {
        return Items != null ? Items.Count : 0;
    }

    public override string GetTitle(UIPickerView pickerView, nint row, nint component)
    {
        return Items != null && Items.Count > row ? Items[(int)row] : null;
    }

    public override nint GetComponentCount(UIPickerView pickerView)
    {
        return 1;
    }

    public override void Selected(UIPickerView pickerView, nint row, nint component)
    {
        selectedIndex = (int)row;
        if (ItemChanged != null)
        {
            ItemChanged.Invoke(this, new EventArgs());
        }
    }
}

클래스는 PickerModel 속성을 통해 클래스에 MyUIPickerView 대한 기본 스토리지를 Items 제공합니다. 변경 내용 Selected 에서 MyUIPickerView 선택한 항목이 실행될 때마다 선택한 인덱스를 업데이트하고 이벤트를 발생시키는 메서드가 ItemChanged 실행됩니다. 이렇게 하면 속성이 SelectedItem 항상 사용자가 선택한 마지막 항목을 반환합니다. 또한 클래스는 PickerModel 인스턴스를 설정하는 데 사용되는 메서드를 재정의합니다 MyUIPickerView .

Android

Android 구현은 뷰를 Spinner 서브클래싱하고 XAML에서 쉽게 사용할 수 있는 속성 및 이벤트를 노출합니다.

class MySpinner : Spinner
{
    ArrayAdapter adapter;
    IList<string> items;

    public IList<string> ItemsSource
    {
        get { return items; }
        set
        {
            if (items != value)
            {
                items = value;
                adapter.Clear();

                foreach (string str in items)
                {
                    adapter.Add(str);
                }
            }
        }
    }

    public string SelectedObject
    {
        get { return (string)GetItemAtPosition(SelectedItemPosition); }
        set
        {
            if (items != null)
            {
                int index = items.IndexOf(value);
                if (index != -1)
                {
                    SetSelection(index);
                }
            }
        }
    }

    public MySpinner(Context context) : base(context)
    {
        ItemSelected += OnBindableSpinnerItemSelected;

        adapter = new ArrayAdapter(context, Android.Resource.Layout.SimpleSpinnerItem);
        adapter.SetDropDownViewResource(Android.Resource.Layout.SimpleSpinnerDropDownItem);
        Adapter = adapter;
    }

    void OnBindableSpinnerItemSelected(object sender, ItemSelectedEventArgs args)
    {
        SelectedObject = (string)GetItemAtPosition(args.Position);
    }
}

클래스는 MySpinner 이벤트와 SelectedObject 속성을 노출 ItemsSource 합니다ItemSelected. 클래스에서 MySpinner 표시하는 항목은 뷰와 연결된 항목에서 Adapter 제공되고 항목은 속성이 처음 설정된 경우 ItemsSourceAdapter 채워집니다. 클래스에서 선택한 항목이 MySpinner 변경 OnBindableSpinnerItemSelected 될 때마다 이벤트 처리기가 속성을 업데이트합니다 SelectedObject .