2017-04-26 60 views
3

我正在寫一個WPF控件,意思是以同樣的方式將一個容器BorderScrollViewer作爲容器。它被稱爲EllipsisButtonControl,它應該在其內容的右側放置一個省略號按鈕。下面是我打算如何爲它使用的例子:在充當容器的WPF控件中,如何放置內容?

<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使設計者崩潰。

我相信X30L的ContentPresenterContentPresenter上的綁定是錯誤的,但我不知道如何使它正確。使該行參考控件內容的適當語法是什麼? (例如,在使用示例中定義的TextBlock

編輯:

捅提供以下(包括工作代碼)一個全面的答案,但對於其他的誰可能分享我最初誤解的好處,讓我總結這裏的關鍵概念是:容器控制本身不能「放置內容」。它通過定義一個模板來達到預期的效果,該模板修改了調用XAML呈現內容的方式。解決方案的其餘部分來自該前提。

+0

爲什麼命令是附屬屬性,而不是常規的依賴屬性? – Clemens

+0

嗯......因爲我正在複製的例子就是這樣......提示你的問題,我研究了附加屬性,並瞭解到我應該在第23行使用DependencyProperty.Register(),而不是DependencyProperty.RegisterAttached() 。 謝謝。 – Lork

+0

*「容器控件本身不能」放置內容「。」* - 它可以,但只有一個內容,因此設置外部內容會替換內部內容。你想要的是內容放置在內的「框架」(=模板)。 – poke

回答

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

這並設置你的用戶控件的Content。但這樣做在用戶控件的XAML如下:

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

主叫XAML有precendence在這裏,所以無論你是用戶控制的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但由於我們的控制模板靶向類型ContentControlContentControl沒有你的自定義屬性,我們需要在這裏明確地引用您的自定義依賴項屬性:

<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> 
+0

這種方法需要不同的思考方式,但我開始理解它。但它引發了兩個問題:1)爲了使代碼正常工作,我必須將ContentControl更改爲UserControl;爲什麼不ContentControl工作? 2)我可以使用模板綁定來支持按鈕的Command,而不是將其編碼爲依賴屬性? – Lork

+0

隨着我的理解的增長,我意識到你已經回答了問題2:不,我不能放棄Command的依賴屬性代碼,因爲ContentControl沒有Command屬性(並且在切換到UserControl之後,它不會)。 – Lork

+0

WPF一般要求您重新思考組件如何工作。一般來說,控件只是附帶一些模板的代碼(另請參閱「不見的組件」);在很多情況下,您想要覆蓋默認模板。回答你的問題:1)這也適用於一個'ContentControl';你可以看到[這個Gist](https://gist.github.com/poke/bd9bcb6d1f1bf50b988ea8909edb99af)作爲一個工作示例。 2)你可以綁定到控件模板中的其他東西,但是在這裏你想暴露你的'EllipsisButtonControl'屬性,所以你實際上需要一個依賴屬性。 – poke

1

嘗試更換這行:

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

有了這個

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

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

+0

安德魯,解決了設計者崩潰問題,但仍有一些問題。當我嘗試使用示例時,我只看到TextBlock,就好像它沒有包含在EllipsisButtonControl中一樣。 – Lork

1

您的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>