8

已经有一些很好的例子来说明如何通过以下方式创建“自定义控件” -

我想创建一个“复合自定义控件或用户控件”,其中包含在 XAML 中定义的多个元素(在共享代码中),然后使用渲染器进行自定义(比如调整每个平台的样式)。

请问有人有这样做的例子吗?一个带有可绑定标签和输入框的视图的简单示例应该足以显示主要原理。

这是我到目前为止所拥有的 -

定义了一个 ContentView 来表示我们的用户控件布局和内容。

<ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="News.Forms.VisualNewsContentView">
    <ContentView.Content>
        <StackLayout>
            <Label x:Name="MyLabel" Text="Label"></Label>
            <Entry x:Name="MyEntry" Text="Entry"></Entry>
        </StackLayout>
    </ContentView.Content>
</ContentView>

使用代码隐藏 -

public partial class VisualNewsContentView : ContentView
{
    public VisualNewsContentView ()
    {
        InitializeComponent ();
    }

    // Not sure if I need this to access Entry ...
    public Entry GetEntry ()
    {
        return MyEntry;
    }
}

为该 ContentView 添加一个 Android 自定义渲染器,我如何访问和自定义 ContentView 的本机部分/控件?

[assembly:ExportRenderer (typeof(VisualNewsContentView), typeof(VisualNewsRenderer))]

namespace News.Forms.Android
{
    public class VisualNewsRenderer: ViewRenderer
    {
        public VisualNewsRenderer () { }

        protected override void OnModelChanged (VisualElement oldModel, VisualElement newModel)
        {
            base.OnModelChanged (oldModel, newModel);

            if (newModel != null) {
                VisualNewsContentView newsContentView = newModel as VisualNewsContentView;

                // i.e. How could I get hold of EditText etc so I could natively customise its appearance? When you use a built in renderer like EntryRenderer you can use Control to access native control.
                Console.WriteLine (newsContentView.GetLabel ().Text);
                EditText ed = (EditText)newsContentView.GetEntry ().???
            }
        }
    }
}

只是无法将这些部分组合在一起工作,ContentView 似乎在页面上渲染得很好,但无法弄清楚如何在 viewrenderer 中访问其 Child Native 控件。

也很高兴展示如何将 Binding 用于 Label 和 Entry Text 值。

我不想为用户控件的每个标签/条目等定义自定义渲染器。

4

2 回答 2

2

这是你的意思吗?

访问 Xamarin.Forms 控件的一些属性:

    public partial class VisualNewsContentView : ContentView
    {
        public VisualNewsContentView()
        {
            InitializeComponent();
        }

        public Label Label
        {
            get
            {
                return MyLabel;
            }
            set
            {
                MyLabel = value;
            }
        }

        public Entry Entry
        {
            get
            {
                return MyEntry;
            }
            set
            {
                MyEntry = value;
            }
        }
    }

渲染器内部的一些魔术来自定义页面上的控件:

[assembly:ExportRenderer (typeof(VisualNewsContentView), typeof(VisualNewsRenderer))]

namespace News.Forms.Android
{
    public class VisualNewsRenderer: ViewRenderer
    {
        public VisualNewsRenderer () { }

        protected override void OnModelChanged (VisualElement oldModel, VisualElement newModel)
        {
            base.OnModelChanged (oldModel, newModel);

            if (newModel != null) {
                VisualNewsContentView newsContentView = newModel as VisualNewsContentView;
                newsContentView.Label.Text = "It´s some kind of..";
                newsContentView.Entry.Text = "MAGIC!";
                newsContentView.Entry.BackgroundColor = Color.Blue;
                newsContentView.Entry.RotationX = 180;
                newsContentView.Entry.Focus();

            }
        }
    }
}

编辑:

我不知道是否可以将您的控件从 XAML 页面映射到本机控件。您可以在渲染器中添加要本地自定义的控件。

    [assembly:ExportRenderer (typeof(VisualNewsContentView), typeof(VisualNewsRenderer))]

namespace News.Forms.Android
{
    public class VisualNewsRenderer: NativeRenderer
    {
        public VisualNewsRenderer () { }

        protected override void OnModelChanged (VisualElement oldModel, VisualElement newModel)
        {
            base.OnModelChanged (oldModel, newModel);

            if (newModel != null) {
                LinearLayout layout = new LinearLayout (Application.Context);
                layout.Orientation = Orientation.Vertical;

                TextView tv = new TextView (Application.Context);
                tv.Ellipsize = TextUtils.TruncateAt.Middle;
                tv.Text = "It´s some kind of..";

                EditText et = new EditText (Application.Context);
                et.SetTextColor (Graphics.Color.Chocolate);
                et.Text = "MAGIC!";

                layout.AddView (tv);
                layout.AddView (et);

                SetNativeControl (layout);
            }
        }
    }
}

但是像这样你不会使用你的 ContentView .. 对不起,我没有比这更好的了..

于 2014-07-08T13:22:34.997 回答
1

我自定义复合用户控件的解决方案是为复合用户控件中使用的每个控件制作一个自定义控件。例如,这个控件:

<ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="News.Forms.VisualNewsContentView">
    <ContentView.Content>
        <StackLayout>
            <Label x:Name="MyLabel" Text="Label"></Label>
            <Entry x:Name="MyEntry" Text="Entry"></Entry>
        </StackLayout>
    </ContentView.Content>
</ContentView>

我会做这样的事情:

<ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
xmlns:CustomControls="clr-namespace:App.CustomControls;assembly=App"  x:Class="News.Forms.VisualNewsContentView">
        <ContentView.Content>
            <CustomControls:StackLayout>
                <CustomControls:Label x:Name="MyLabel" Text="Label"></CustomControls:Label>
                <CustomControls:Entry x:Name="MyEntry" Text="Entry"></CustomControls:Entry>
            </CustomControls:StackLayout>
        </ContentView.Content>
    </ContentView>

CustomControls:StackLayout 的示例类是:

(在 StackLayout.cs 中)

using Xamarin.Forms;

namespace App.CustomControls
{
    public class StackLayout : Xamarin.Forms.StackLayout
    {
    }
}

(在 Android 项目的 StackLayoutRenderer.cs 中)

[assembly: ExportRenderer(typeof(App.CustomControls.StackLayout), typeof(App.Droid.CustomRenderers.StackLayoutRenderer))]
namespace App.Droid.CustomRenderers.MapView
{
    public class StackLayoutRenderer : ViewRenderer<StackLayout, Android.Widget.LinearLayout>
    {
        protected override void OnElementChanged(ElementChangedEventArgs<StackLayout> e)
        {
            base.OnElementChanged(e);                               
        }           
    }
}
于 2017-11-27T14:11:40.340 回答