簡體   English   中英

在充當容器的WPF控件中,如何放置內容?

[英]In a WPF control that acts as a container, how do I place the content?

我正在編寫一個WPF控件,它的意思是一個容器,就像BorderScrollViewer是容器一樣。 它被稱為EllipsisButtonControl ,它應該在其內容的右側放置一個省略號按鈕。 這是我打算如何使用它的一個例子:

<local:EllipsisButtonControl>
    <TextBlock Text="Testing" />
</local:EllipsisButtonControl>

這是EllipsisButtonControl的XAML:

<ContentControl
    x:Class="WpfApplication1.EllipsisButtonControl"
    x:Name="ContentControl"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    mc:Ignorable="d"
    d:DesignHeight="30" d:DesignWidth="300">

    <Grid>

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="Auto" />
        </Grid.ColumnDefinitions>

        <ContentPresenter Grid.Column="0" Content="{Binding ElementName=ContentControl, Path=Content}" />

        <Button Grid.Column="1" Command="{Binding  ElementName=ContentControl, Path=Command}" Margin="3,0" Width="30" Height="24" MaxHeight="24" VerticalAlignment="Stretch" Content="..." />

    </Grid>

</ContentControl>

以下是代碼背后的代碼:

using System.Windows;
using System.Windows.Input;

namespace WpfApplication1
{
    public partial class EllipsisButtonControl
    {
        public EllipsisButtonControl()
        {
            InitializeComponent();
        }

        public static string GetCommand(DependencyObject obj)
        {
            return (string)obj.GetValue(CommandProperty);
        }

        public static void SetCommand(DependencyObject obj, string value)
        {
            obj.SetValue(CommandProperty, value);
        }

        public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached(
                name: "Command",
                propertyType: typeof(ICommand),
                ownerType: typeof(EllipsisButtonControl),
                defaultMetadata: new UIPropertyMetadata());
    }
}

這不起作用。 它使用System.Runtime.Remoting.RemotingException崩潰Designer。

我相信對EllipsisButtonControl XAML的ContentPresenter的綁定是錯誤的,但我不知道如何使它正確。 使該行引用控件內容的適當語法是什么? (例如,用法示例中定義的TextBlock

編輯:

poke在下面提供了一個全面的答案(包括工作代碼),但為了其他可能分享我最初誤解的人的利益,讓我總結一下這里的關鍵概念:容器控件本身不能“放置內容”。 它通過定義修改調用XAML呈現內容的方式的模板來實現預期效果。 解決方案的其余部分來自該前提。

嘗試替換此行:

<ContentPresenter Grid.Column="0" Content="{Binding ElementName=ContentControl, Path=Content}" />

有了這個

<ContentPresenter Grid.Column="0" Content={Binding Content} />

在現有的代碼,你使這種ContentPresenter顯示的生成內容EllipsesButtonControl ,其中包括ContentPresenter必須渲染生成的內容ElipsesButtonControl其中包括ContentPresenter .....無限遞歸。

您的EllipsisButtonControl的XAML已將其內容設置為頂級網格。 您可能想要的是創建一個ControlTemplate ,例如:

<ContentControl x:Class="WpfApplication1.EllipsisButtonControl"
                x:Name="ContentControl"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
                xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
                mc:Ignorable="d" d:DesignHeight="30" d:DesignWidth="300">
    <ContentControl.Template>
        <ControlTemplate>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="Auto" />
                </Grid.ColumnDefinitions>

                <ContentPresenter Grid.Column="0"
                    Content="{Binding ElementName=ContentControl, Path=Content}"/>

                <Button Grid.Column="1"
                    Command="{Binding ElementName=ContentControl, Path=Command}"
                    Margin="3,0" Width="30" Height="24" MaxHeight="24"
                    VerticalAlignment="Stretch" Content="..." />
            </Grid>
        </ControlTemplate>
    </ContentControl.Template>
</ContentControl>
<local:EllipsisButtonControl>
    <TextBlock Text="Testing" />
</local:EllipsisButtonControl>

這確實設置了用戶控件的Content 但是用戶控件的XAML中的以下內容也是如此:

<ContentControl …>
    <Grid>
        …
    </Grid>
</ContentControl>

調用XAML在這里具有優先權,因此無論你在用戶控件的XAML中做什么,實際上都會被忽略。

這里的解決方案是設置用戶控件的模板 模板(在本例中為控件的控件模板)確定控件本身的呈現方式。 用戶控件的最簡單模板(以及它的默認模板)就是在那里使用ContentPresenter ,但是當然,你想要添加一些東西,所以我們必須覆蓋模板。 這通常看起來像這樣:

<ContentControl …>
    <!-- We are setting the `Template` property -->
    <ContentControl.Template>
        <!-- The template value is of type `ControlTemplate` and we should
             also set the target type properly so binding paths can be resolved -->
        <ControlTemplate>

            <!-- This is where your control code actually goes -->

        </ControlTemplate>
    </ContentControl.Template>
</ContentControl>

現在,這是您完成此工作所需的框架。 但是,一旦進入控件模板,就需要使用正確的綁定類型。 由於我們正在編寫模板並希望綁定到父控件的屬性,因此我們需要將父控件指定為綁定中的相對源。 但最簡單的方法是使用TemplateBinding標記擴展。 使用它, ContentPresenter可以放在上面的ControlTemplate

<ContentPresenter Content="{TemplateBinding Content}" />

這應該是您需要的所有內容,以使內容演示者工作。

但是,現在您使用控件模板,當然您也需要調整其他綁定。 特別是綁定到自定義依賴項屬性Command 這通常看起來與綁定到Content的模板相同,但由於我們的控件模板的目標是ContentControl類型,而ContentControl沒有自定義屬性,因此我們需要在此處顯式引用您的自定義依賴項屬性:

<Button Command="{TemplateBinding local:EllipsisButtonControl.Command}" … />

一旦我們擁有了,所有綁定都應該正常工作。 (如果你現在想知道:是的,綁定總是以類型的靜態依賴屬性為目標)


因此,總而言之,您的自定義內容控件應如下所示:

<ContentControl
        x:Class="WpfApplication1.EllipsisButtonControl"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:local="clr-namespace:WpfApplication1"
        d:DesignHeight="30" d:DesignWidth="300" mc:Ignorable="d">
    <ContentControl.Template>
        <ControlTemplate TargetType="ContentControl">

            <Grid>
                <ContentPresenter Grid.Column="0"
                        Content="{TemplateBinding Content}" />

                <Button Grid.Column="1" Content="…"
                        Command="{TemplateBinding local:EllipsisButtonControl.Command}" />
            </Grid>

        </ControlTemplate>
    </ContentControl.Template>
</ContentControl>

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM