且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

用户控件中的WPF将控件模板的内容设置为依赖项属性的值

更新时间:2022-04-26 03:10:17

这是WPF中的一种常见模式,很多ContentControl派生的内置控件都使用了这种模式(子内容),HeaderedContentControl(2个子内容)或ItemsControl(n个孩子的集合)。通常,Content属性(在运行时将被替换为占位符的东西)应为object类型。在这种情况下,您也可以摆脱更改处理程序而只依赖数据绑定。

This is a common pattern in WPF that is used by a lot of the built in controls deriving from ContentControl (1 piece of child content), HeaderedContentControl (2 pieces of child content), or ItemsControl (a collection of n children). In general, the Content properties (the stuff that will be substituted into placeholders at runtime) should be of type object. You can also get rid of the change handler and just rely on data binding in this situation.

[Category("jonblind")]
public object ContentAreaControl
{
    get { return GetValue(ContentAreaControlProperty); }
    set { SetValue(ContentAreaControlProperty, value); }
}

public static readonly DependencyProperty ContentAreaControlProperty = 
    DependencyProperty.Register("ContentAreaControl", typeof(object), typeof(jonblind),
    new FrameworkPropertyMetadata(null));

使用此新的Content属性,您可以使用ContentPresenter设置绑定,该绑定仅用作传入的内容的占位符。这在从ContentControl派生的自定义控件中进行设置更加容易,在该控件中ContentPresenter可以在ControlTemplate内自动连接到Content。

With this new Content property you can then set up a Binding using a ContentPresenter, which simply acts as a placeholder for your Content that's passed in. This is even easier to set up in custom controls derived from ContentControl where the ContentPresenter can be wired up to the Content automatically inside a ControlTemplate.

<UserControl x:Class="ShapInteractiveClient.View.SampleTests.jonblind"
         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="300" d:DesignWidth="300">

<Grid x:Name="blindGrid" Grid.RowSpan="2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" 
      Opacity="0.82">
    <Grid.Background>
        <LinearGradientBrush EndPoint="0,1" StartPoint="0,0" MappingMode="RelativeToBoundingBox">
            <GradientStop Color="#FFA8CBFE" Offset="1"/>
            <GradientStop Color="Red"/>
            <GradientStop Color="#FFE1EDFE" Offset="0.147"/>
        </LinearGradientBrush>
    </Grid.Background>

    <ContentPresenter Content="{Binding Path=ContentAreaContent, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}" />

</Grid>
</UserControl>

通常,将UIElement实例声明为Resources是一个坏主意(***将其放置在Template Resources中)但是我们可以像对待其他控件一样轻松解决该问题。这样的用法就更像ContentControl(如Button)的样子:

In general it's a bad idea to declare UIElement instances as Resources (prefer placing them inside Template Resources instead) but we can get around that issue easily by treating this like any other control. The usage is then much more like what a ContentControl (like Button) looks like:

<SampleTests:jonblind IsContentVisible="{Binding Path=ShowBlind}">
    <SampleTests:jonblind.ContentAreaControl>
        <StackPanel>
            <TextBox VerticalAlignment="Center" HorizontalAlignment="Center" Text="Loading!!!" />
            <Button Content="hide me!" Command="{Binding Path=alternateblind}" />
        </StackPanel>
    </SampleTests:jonblind.ContentAreaControl>
</SampleTests:jonblind>

作为自定义ContentControl而不是UserControl,您可以获得更多好处,但是一些增加的复杂性和对概念的更好理解通常有助于使其正确运行。开始使用UserControl时,可以轻松完成所需的操作。

You can get even more advantages from doing this as a custom ContentControl instead of a UserControl, but there is some added complexity and a better understanding of the concepts is generally helpful to make it work correctly. While you're starting out sticking with a UserControl can make it simpler to get what you need done.