自定义布局控件和逻辑树

时间:2014-01-16 11:49:31

标签: c# wpf xaml custom-controls dependency-properties

我一直致力于控制一段时间,可用于在我们的应用程序中重复布局。它工作得很好,但问题是布局控件中的控件没有参与逻辑树。

这是一个自定义控件,左侧标题,右侧标题,正文和标准页脚。

Generic.xaml:

<Style TargetType="{x:Type local:FrameControl}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type local:FrameControl}">
                <Border Background="{TemplateBinding Background}"
                        BorderBrush="{TemplateBinding BorderBrush}"
                        BorderThickness="{TemplateBinding BorderThickness}">
                    <Grid>
                        <Border VerticalAlignment="Top" Height="50" Background="LightGray"/>

                        <DockPanel Background="{TemplateBinding HeaderColor}">
                            <ItemsControl ItemsSource="{TemplateBinding HeaderLeftContent}">
                                <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <StackPanel DockPanel.Dock="Left" FlowDirection="LeftToRight" HorizontalAlignment="Left"/>
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>
                            </ItemsControl>

                            <ItemsControl ItemsSource="{TemplateBinding HeaderRightContent}">
                                <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <StackPanel DockPanel.Dock="Right" Height="40" 
                                                    Margin="5, 5, 5, 0" VerticalAlignment="Top"
                                                    Orientation="Horizontal" FlowDirection="RightToLeft"/>
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>
                            </ItemsControl>
                        </DockPanel>

                        <Border BorderThickness="1" BorderBrush="Black" Margin="0,49,0,0" Background="White">
                            <ItemsControl ItemsSource="{TemplateBinding BodyContent}">
                                <ItemsControl.ItemsPanel>
                                    <ItemsPanelTemplate>
                                        <DockPanel Background="White" Margin="5"/>
                                    </ItemsPanelTemplate>
                                </ItemsControl.ItemsPanel>
                            </ItemsControl>
                        </Border>
                        <Border VerticalAlignment="Bottom" Height="0" Background="LightGray"/>
                    </Grid>
                </Border>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

FrameControl.cs

public class FrameControl : Control
{
    public Brush HeaderColor
    {
        get { return (Brush)GetValue(HeaderColorProperty); }
        set { SetValue(HeaderColorProperty, value); }
    }

    public static readonly DependencyProperty HeaderColorProperty =
        DependencyProperty.Register("HeaderColor", typeof(Brush), typeof(FrameControl), new UIPropertyMetadata(null));

    public Collection<Object> HeaderLeftContent
    {
        get { return (Collection<Object>)GetValue(HeaderLeftContentProperty); }
        set { SetValue(HeaderLeftContentProperty, value); }
    }

    public static readonly DependencyProperty HeaderLeftContentProperty =
       DependencyProperty.Register("HeaderLeftContent", typeof(Collection<Object>), typeof(FrameControl),
       new UIPropertyMetadata(null));

    public Collection<Object> HeaderRightContent
    {
        get { return (Collection<Object>)GetValue(HeaderRightContentProperty); }
        set { SetValue(HeaderRightContentProperty, value); }
    }

    public static readonly DependencyProperty HeaderRightContentProperty =
        DependencyProperty.Register("HeaderRightContent", typeof(Collection<Object>), typeof(FrameControl),
        new UIPropertyMetadata(null));

    public Collection<Object> BodyContent
    {
        get { return (Collection<Object>)GetValue(BodyContentProperty); }
        set { SetValue(BodyContentProperty, value); }
    }

    public static readonly DependencyProperty BodyContentProperty =
        DependencyProperty.Register("BodyContent", typeof(Collection<Object>), typeof(FrameControl),
        new UIPropertyMetadata(null));


    static FrameControl()
    {
        DefaultStyleKeyProperty.OverrideMetadata(typeof(FrameControl), new FrameworkPropertyMetadata(typeof(FrameControl)));
    }

    public FrameControl()
    {
        SetValue(HeaderLeftContentProperty, new Collection<Object>());
        SetValue(HeaderRightContentProperty, new Collection<Object>());
        SetValue(BodyContentProperty, new Collection<Object>());
    }
}

使用示例

   <fc:FrameControl>
        <fc:FrameControl.HeaderLeftContent>
            <Label Content="Left content of the header"/>
        </fc:FrameControl.HeaderLeftContent>
        <fc:FrameControl.HeaderRightContent>
                <Button>
                <TextBlock Text="Example button"/>
            </Button>
                <ComboBox SelectedIndex="0">
                    <ComboBoxItem Content="Filter A"/>
                    <ComboBoxItem Content="Filter B"/>
                    <ComboBoxItem Content="Filter C"/>
                </ComboBox>
        </fc:FrameControl.HeaderRightContent>
        <fc:FrameControl.BodyContent>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="Auto"/>
                    <ColumnDefinition Width="*"/>
                </Grid.ColumnDefinitions>
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                    <RowDefinition Height="Auto"/>
                </Grid.RowDefinitions>

                <Label Grid.Row="0" Grid.Column="0"  Content="name"/>
                <TextBox Grid.Row="0" Grid.Column="2"/>

                <Label Grid.Row="1" Grid.Column="0"  Content="Type name"/>
                <TextBox Grid.Row="1" Grid.Column="2" />

                <Label Grid.Row="2" Grid.Column="0"  Content="Keyword"/>
                <TextBox Grid.Row="2" Grid.Column="2"/>

                <Label Grid.Row="3" Grid.Column="0"  Content="Parameter"/>

                <ComboBox Grid.Row="4" Grid.Column="0" SelectedIndex="0">
                    <ComboBoxItem Content="Width"/>
                    <ComboBoxItem Content="Height"/>
                </ComboBox>
                <ComboBox Grid.Row="4" Grid.Column="1" SelectedIndex="0">
                    <ComboBoxItem Content="..."/>
                    <ComboBoxItem Content="="/>
                    <ComboBoxItem Content="&lt;"/>
                    <ComboBoxItem Content="&gt;"/>
                </ComboBox>
                <TextBox Grid.Row="4" Grid.Column="2"/>
            </Grid>
        </fc:FrameControl.BodyContent>
    </fc:FrameControl>

我尝试使用不同类型的属性来保存不同区域的元素,如UIElementCollection,但没有区别。我也尝试手动将控件添加到逻辑树中,但我没有成功。我错过了什么吗?

1 个答案:

答案 0 :(得分:0)

您说实际的问题是无法使用此控件使用ElementName进行绑定。无法以这种方式接近FrameControl内部的元素。但是,该问题与可视化树完全无关。视觉树中的那些元素,但您无法访问它们,因为它们是在ControlTemplate内定义的。

您可以使用ControlTemplate.FindName method为您找到元素。但是,您需要有ControlTemplate已应用的元素以及ControlTemplate中的命名元素:

// Assuming that your DockPanel in the ControlTemplate was named DockPanel
DockPanel dockPanel = 
    frameControl.Template.FindName("DockPanel", frameControl) as DockPanel;
if (dockPanel != null) // You must check for null
{
    DoSomethingHereWith(dockPanel);
}

请参阅链接页面以获取更多帮助。