如何override/modify Frame 的Content 属性 接受Xamarin.Forms 中的多个View?

How to override/modify the Content property of Frame to accept multiple Views in Xamarin.Forms?

这是我的 C# 模板代码:

public class PopupFrame : Frame
{
    public PopupFrame()
    {
        this.SetDynamicResource(BackgroundColorProperty, "PopUpBackgroundColor");
        this.SetDynamicResource(CornerRadiusProperty, "PopupCornerRadius");
        HasShadow = true;
        HorizontalOptions = LayoutOptions.FillAndExpand;
        Padding = 0;
        VerticalOptions = LayoutOptions.Center;
    }
}

我是这样使用的:

<t:PopupFrame>
   <StackLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
         <t:PopupHeader Text="Copy Deck" />
         <t:PopupEntryHeader Text="New Deck Name" />
                
                More XAML here
   </StackLayout>
</t:PopupFrame>

有没有什么方法可以让我编写 PopupFrame 代码,使 StackLayout 成为其中的一部分并获取内容。

这是我想要编码的内容:

<t:PopupFrame>
   <t:PopupHeader Text="Copy Deck" />
   <t:PopupEntryHeader Text="New Deck Name" />
                
       More XAML here

</t:PopupFrame>

如果我是对的,您可以通过将 PopupFrame class 的 ContentProperty 属性设置为本身就是一个集合的 属性 来实现此目的。这将覆盖 FrameContentProperty,即 Content 以允许您将多个视图设置为内容,而不是只有一个,这是 Frame 的默认值...

所以,如果您觉得这一切都不错,请继续阅读。

方法

您可以为 PopupFrame class 定义一个 ContentProperty,像这样:

[Xamarin.Forms.ContentProperty("Contents")]
class PopupFrame : Frame
{
    StackLayout contentStack { get; } = new StackLayout();

    public IList<View> Contents { get => contentStack.Children; }


    public PopupFrame()
    {
        Content = contentStack;

        HasShadow = true;
        HorizontalOptions = LayoutOptions.FillAndExpand;
        Padding = 0;
        VerticalOptions = LayoutOptions.Center;
    }
}

然后你就可以做你想做的事情了:

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:t="clr-namespace:popupframe"
             x:Class="popupframe.MainPage">

    <StackLayout>
        <t:PopupFrame>
            <t:PopupHeader Text="Test header"/>
            <Label Text="Test content"/>
        </t:PopupFrame>
    </StackLayout>

</ContentPage>

我这边同时显示 PopupHeaderLabel:

最后是关于 ContentProperty 的一些理论

以下内容直接摘自book of Ch. Petzold on Xamarin.Forms

XAML中使用的每个class允许定义一个属性作为内容属性(有时也称为class 的默认值 属性)。对于此内容 属性,不需要 属性 元素标签,并且开始和结束标签内的任何 XML 内容都会自动分配给此 属性。很方便,ContentPage的内容属性是ContentStackLayout的内容属性是Children,[=88=的内容Frame 的 ] 是 Content

这些内容属性已记录在案,但您需要知道在哪里查看。 class 通过使用 ContentPropertyAttribute 指定其内容 属性。如果此属性附加到 class,它会与 class 声明一起出现在在线 Xamarin.Forms API 文档中。这是它在 ContentPage 的文档中的显示方式:

[Xamarin.Forms.ContentProperty("Content")]
public class ContentPage : TemplatedPage

如果大声说出来,听起来有点多余:“Content属性就是ContentPage的内容属性。”

Frame class 的声明类似:

[Xamarin.Forms.ContentProperty("Content")]
public class Frame : ContentView

StackLayout 没有应用 ContentProperty 属性,但 StackLayout 派生自 Layout<View>,并且 Layout<T> 具有 ContentProperty属性:

[Xamarin.Forms.ContentProperty("Children")]
public abstract class Layout<T> : Layout, IViewContainer<T>
where T : View

ContentProperty属性被继承自Layout<T>的class属性,所以Children是[=26=的内容属性 ].

PopupFrame.cs

public class PopupFrame : Frame
{
    StackLayout PopupContent;
    public IList<View> Body
    {
        get => PopupContent.Children;
    }
    public PopupFrame()
    {

        PopupContent = new StackLayout();

        SetDynamicResource(Frame.BackgroundColorProperty, "PopUpBackgroundColor");
        SetDynamicResource(Frame.CornerRadiusProperty, "PopupCornerRadius");
        HasShadow = true;
        HorizontalOptions = LayoutOptions.FillAndExpand;
        Padding = 0;
        VerticalOptions = LayoutOptions.Center;

        Content = PopupContent;
    }

现在您可以使用

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:t="clr-namespace:popupframe"
             x:Class="popupframe.MainPage">

    <StackLayout>
       <t:PopupFrame>
         <t:PopupFrame.Body>
             <t:PopupHeader Text="Test header"/>
             <Label Text="Test content"/>
           </t:PopupFrame.Body>
        </t:PopupFrame>
    </StackLayout>

</ContentPage>