WPF Custom Control for Side by Side Layout

2019-05-19 02:41发布

I want to create a custom control so that I can do something like this:

<SideBySide>
    <StackPanel SideBySide.Left="True">...</StackPanel>
    <StackPanel SideBySide.Right="False">...</StackPanel>
</SideBySide>

I'm going to be using this all over the place, with obviously more options (sizing, etc.).

I've considered using a Panel subclass, but that doesn't seem right (there's a notion of a selected item between the left and the right).

So, I'm trying to use a ItemsControl subclass -- now, does anyone know how to put the items in a control template for an ItemsControl?

This is an abbreviated template for the SideBySide:

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:WpfCustomControlLibrary1">
    <Style TargetType="{x:Type local:SideBySideControl}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:SideBySideControl}">
                    <Border Background="{TemplateBinding Background}"
                            BorderBrush="{TemplateBinding BorderBrush}"
                            BorderThickness="{TemplateBinding BorderThickness}">
                        <Grid>
                            <Grid.Resources>
                                <Style TargetType="{x:Type Rectangle}">
                                    <Setter Property="Margin"
                                            Value="5" />
                                </Style>
                            </Grid.Resources>
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition />
                                <ColumnDefinition Width="Auto" />
                                <ColumnDefinition />
                            </Grid.ColumnDefinitions>
                            <Grid Grid.Column="0"
                                  VerticalAlignment="Stretch">
                                <!-- PART_LeftContent goes here -->
                            </Grid>
                            <GridSplitter Width="3"
                                          Grid.Column="1"
                                          HorizontalAlignment="Center"
                                          VerticalAlignment="Stretch"
                                          ShowsPreview="False">
                            </GridSplitter>
                            <Grid Grid.Column="2">
                                <!-- PART_RightContent goes here -->
                            </Grid>
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</ResourceDictionary>

1条回答
劫难
2楼-- · 2019-05-19 03:09

The direct answer is that you need an ItemsPresenter in your ControlTemplate, which would look something like this:

<ItemsControl x:Class="ItemsControlExample"
              xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
              xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <ItemsControl.Template>
        <ControlTemplate TargetType="ItemsControl">
            <Border SnapsToDevicePixels="True">
                <!-- Collection items are displayed by the ItemsPresenter. --> 
                <ItemsPresenter SnapsToDevicePixels="True" />
            </Border>
        </ControlTemplate>
    </ItemsControl.Template>

    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <!-- Replace the default vertical StackPanel with horizontal. -->
            <StackPanel Orientation="Horizontal" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>

    <ItemsControl.ItemContainerStyle>
        <Style TargetType="...">
            <!-- The same container style applies to all items so where do you put the splitter? -->
        </Style>
    </ItemsControl.ItemContainerStyle>    

</ItemsControl>

But it should be obvious now that ItemsControl doesn't align to your use case. However, you can implement it as a Control using the ControlTemplate you already have with a ContentControl the PART_LeftContent and PART_RightContent grid cells:

<!-- LeftSideContent is a DependencyProperty of type object -->
<ContentControl x:Name="LeftContentControl" Content="{TemplateBinding LeftSideContent}" />

Then extend your code to handle the ContentControl mouse events in order to select and add style triggers for the selected appearance, but that's pretty straightforward stuff. If you haven't implemented lookless controls before you should be aware that you can't define event callbacks in the template, but instead have to hook them in your code:

public override void OnApplyTemplate()
{
    base.OnApplyTemplate();

    ContentControl lc = (ContentControl)base.GetTemplateChild("LeftContentControl"));
    // check for null in case the active template doesn't have a 'LeftContentControl' element
    if (lc != null) 
    {
        // Use these events to set SelectedItem DependencyProperty and trigger selected item 
        // highlight style. Don't forget to capture the mouse for proper click behavior.
        lc.MouseDown += new MouseButtonEventHandler(LeftSide_MouseDown);
        lc.MouseUp += new MouseButtonEventHandler(LeftSide_MouseUp);
    }
 }
查看更多
登录 后发表回答