WPF – Tìm hiểu về ContentPresenter

WPF - ContentPresenter - BasicLà thành phần được dùng để hiển thị nội dung của các ContentControl, ContentPresenter được sử dụng chủ yếu khi thiết kế các Control Template. Mặc dù cách sử dụng rất đơn giản, nhưng bạn cũng cần nắm rõ hơn để tránh những rắc rối khi thiết kế template với thành phần này.

Với ContentControl

Một số thuật ngữ bạn cần biết:

–      Template Parent: control được áp dụng template.

–      Content Property: property được dùng để chứa nội dung của control. Property này được xác định thông qua [ContentPropertyAttribute]. Đối với ContentControl thì property này tên là Content (ví dụ Button.Content, Label.Content,…).

Khi thiết kế một control template, ContentPresenter được dùng để xác định vị trí mà nội dung của control sẽ hiển thị. Tuy nhiên bạn cần giúp nó biết được đâu là content property.

Ví dụ với đoạn mã XAML sau, bạn không thể thấy được nội dung của Button (chuỗi “Hello”) sau khi áp dụng template cho nó:

...
<Window.Resources>
    <ControlTemplate x:Key="ButtonTemplate">
        <Grid>
            <Ellipse Fill="{TemplateBinding Property=Background}"/>
            <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
        </Grid>
    </ControlTemplate>
</Window.Resources>

<Grid>
    <Button Template="{StaticResource ButtonTemplate}" Height="50" Width="100">Hello</Button>
</Grid>
...

Để khắc phục điều này, bạn chỉ cần xác định TargetType cho ControlTemplate trên. Bạn có thể gán là TargetType=”ContentControl” hoặc  TargetType=”Button” để trình phân tích biết được đâu là content property.

Một cách khác theo lối thủ công là binding trực tiếp vào property ControlPresenter.Content:

<ControlTemplate x:Key="ButtonTemplate" TargetType="ContentControl">
<!--or-->
<ContentPresenter Content="{TemplateBinding Property=ContentControl.Content}">

Mặc dù đơn giản nhưng có thể gây ra rắc rối nếu như bạn không nắm rõ:

WPF - ContentPresenter - Basic

Property ContentPresenter.ContentSource

Là một lớp con của ContentControl, các lớp kế thừa từ HeaderedContentControl như GroupBox, Expander,… cũng có content property là Content. Tuy nhiên, khi thiết kế template cho các control này, bạn cũng cần phải dùng ContentPresenter để xác định vị trí cho header của chúng.

Thực hiện điều này rất đơn giản, bạn chỉ cần gán giá trị cho property ContentPresenter.ContentSource đến property cần chứa nội dung. Ví dụ tôi tạo một template cho GroupBox sử dụng hai ContentPresenter cho phần Header và Content:

...
<Window.Resources>
    <LinearGradientBrush x:Key="MyGradientBrush" StartPoint="0,0" EndPoint="0,1" >
        <GradientStop Color="LightGoldenrodYellow" Offset="0"/>
        <GradientStop Color="Goldenrod" Offset="0.7"/>
    </LinearGradientBrush>

    <ControlTemplate x:Key="HeaderedControlTemplate" TargetType="HeaderedContentControl">
        <DockPanel>
            <!--Header-->
            <Grid DockPanel.Dock="Top" Height="25">
                <Border Background="{StaticResource MyGradientBrush}" BorderBrush="Black" BorderThickness="1,1,1,0" CornerRadius="8,8,0,0">
                    <ContentPresenter ContentSource="Header" Margin="5" VerticalAlignment="Center"/>
                </Border>
            </Grid>
            <!--Content-->
            <Border BorderBrush="Black" BorderThickness="1">
                <Grid>
                    <Rectangle Fill="LightBlue"/>
                    <ContentPresenter ContentSource="Content" Margin="5"/>
                </Grid>
            </Border>
        </DockPanel>
    </ControlTemplate>
</Window.Resources>

<Grid>
    <GroupBox  Margin="5" Template="{StaticResource HeaderedControlTemplate}" Header="GroupBox's Header" Content="Hello"></GroupBox>
</Grid>
...

Cho ta một giao diện GroupBox mới:

WPF - ContentPresenter - Custom GroupBox

Có cần phải là ContentControl?

Bạn có thể đọc được từ MSDN câu sau về ContentPresenter: “Displays the content of a ContentControl” và nghĩ rằng thành phần này chỉ được áp dụng cho các ContentControl. Tuy nhiên thực sự thì bạn vẫn có thể sử dụng ContentPresenter cho các loại control khác, ví dụ như ItemsControl.

Trong ví dụ sau, tôi sử dụng ContentPresenter để thiết kế template cho TabControl, một lớp kế thừa gián tiếp từ ItemsControl. Mặc dù có nhiều TabItem, nhưng tại một thời điểm, TabControl chỉ hiển thị nội dung của một trong số chúng, thông qua property SelectedContent.

...
<Window.Resources>
    <LinearGradientBrush x:Key="MyGradientBrush" StartPoint="0,0" EndPoint="0,1" >
        <GradientStop Color="LightGoldenrodYellow" Offset="0"/>
        <GradientStop Color="Wheat" Offset="0.7"/>
    </LinearGradientBrush>

    <ControlTemplate x:Key="TabControlTemplate" TargetType="TabControl">
        <Border BorderBrush="Black" BorderThickness="1">
            <DockPanel Background="{StaticResource MyGradientBrush}">
                <TabPanel Background="Gray" DockPanel.Dock="Top" IsItemsHost="True"/>
                <Grid>
                    <ContentPresenter Margin="5" ContentSource="SelectedContent"/>
                </Grid>
            </DockPanel>
        </Border>
    </ControlTemplate>
</Window.Resources>

<Grid>
    <TabControl Margin="5" Template="{StaticResource TabControlTemplate}">
        <TabItem Header="TabItem 1" Content="Hello"/>
        <TabItem Header="TabItem 2"/>
    </TabControl>
</Grid>
...

Một giao diện TabControl đơn giản:

WPF - ContentPresenter - Custom TabControl

https://yinyangit.wordpress.com

Bài liên quan

WPF – Control Template

Advertisements

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất / Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất / Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất / Thay đổi )

Google+ photo

Bạn đang bình luận bằng tài khoản Google+ Đăng xuất / Thay đổi )

Connecting to %s