WPF custom control and direct content support

2019-06-06 06:50发布

问题:

I am fairly new to WPF, and am a bit stuck, so any help would be appreciated.

I am trying to write WPF custom control that encapsulates several elements of functionality that I already having working (i.e sorting, filtering, standard menus, etc.), but in a nice neat package to avoid repetition.

Anyway I have created the custom control (based on control), and then have the following in the Generic.Xaml

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Controls.ListViewExtended">
    <Style TargetType="{x:Type local:ListViewExtended}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:ListViewExtended}">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                        <ListView>
                            <ListView.View>
                                <GridView>
                                <!-- Content goes here -->                             
                                </GridView> 
                            </ListView.View>
                        </ListView>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

When I try to add GridViewColumns (or any control really), as below ...

<elv:ListViewExtended>
   <GridView>
        <GridViewColumn Width="140" Header="Column 1" />
        <GridViewColumn Width="140" Header="Column 2" />
        <GridViewColumn Width="140" Header="Column 3" />
   </GridView>
</elv:ListViewExtended>

I get the "... does not support direct content" error.

I have created a dependancy property (again below) that allows the adding of GridView, but it still doesn't work.

public static DependencyProperty GridViewProperty;

public static string GridViewHeader(DependencyObject target) 
{
    return (string)target.GetValue(GridViewProperty); 
}
public static void GridViewHeader(DependencyObject target, string value) 
{
    target.SetValue(GridViewProperty, value); 
} 

Thanks in advance

回答1:

Inherit your custom control from the ListView, not from the Control. This would definitely cause you to change the template, but I encourage you to read more documentation on how to do it (e.g. Sacha Barber's article: Creating and consuming a custom WPF control).

Good luck in your learning!



回答2:

All You need is to specify ContentPropertyAttribute.

[ContentProperty("MainContent")]
public class GroupPanel : Control
{
    public GroupPanel()
    {
        DefaultStyleKey = typeof(GroupPanel);
    }

    public object MainContent
    {
        get { return GetValue(MainContentProperty); }
        set { SetValue(MainContentProperty, value); }
    }

    public static readonly DependencyProperty MainContentProperty =
        DependencyProperty.Register("MainContent", typeof(object), typeof(GroupPanel), null);
}

Reference: http://msdn.microsoft.com/en-us/library/system.windows.markup.contentpropertyattribute(v=vs.90).aspx



回答3:

I use this simple solution in our projects to support direct content in custom controls:

Add a "CustomControl" to your project and derive this control from class "UserControl" instead of "Control":

public class MyCustomControl: UserControl
{
    static MyCustomControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), 
            new FrameworkPropertyMetadata(typeof(MyCustomControl)));
    }
}

When you add a CustomControl to your project, Visual Studio (I am using 2012) automatically adds a folder "Themes" including a file named "Generic.xaml". This file holds a ResourceDictionary to define the style (template) of your CustomControl.

You will find a basic template for your CustomControl, already used as DefaultStyle. For direct content support place a ContentPresenter somewhere inside this template with parent content binding:

<Style TargetType="{x:Type local:MyCustomControl}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:MyCustomControl}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <ContentPresenter Content="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}" />
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Now it's possible to add content to your CustomControl:

<Window x:Class="MyApplication.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:controls="clr-namespace:MyApplication"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <controls:MyCustomControl>
                <TextBlock>Hello</TextBlock>
        </controls:MyCustomControl>
    </Grid>
</Window>

Hope this helps!