WPF:如何在创建自定义控件时正确覆盖方法

时间:2010-03-10 19:29:51

标签: wpf custom-controls override

我正在创建一个派生自ItemsControl的自定义控件工具箱。该工具箱应该填充来自数据库的图标。定义如下:

public class Toolbox : ItemsControl
{              
    protected override DependencyObject GetContainerForItemOverride()
    {
        return new ToolboxItem();
    }

    protected override bool IsItemItsOwnContainerOverride(object item)
    {
        return (item is ToolboxItem);
    }
}

Toolboxitem源自ContentControl。

public class ToolboxItem : ContentControl
{               
    static ToolboxItem()
    {
        FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(typeof(ToolboxItem), new FrameworkPropertyMetadata(typeof(ToolboxItem)));
    }
}

由于数据库中存储的图标数量未知,我想使用数据模板:

<DataTemplate x:Key="ToolBoxTemplate">
    <StackPanel>
        <Image Source="{Binding Path=url}" />
    </StackPanel>
</DataTemplate>

然后我希望工具箱使用模板。

<Toolbox x:Name="NewLibrary" ItemsSource="{Binding}" ItemTemplate="ToolBoxtemplate">
</Toolbox> 

我正在使用ADO.NET实体框架连接到数据库。背后的代码:

SystemicsAnalystDBEntities db = new SystemicsAnalystDBEntities();

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    NewLibrary.ItemsSource = from c in db.Components select c;
}

然而,有一个问题。执行代码时,它会显示数据库中的对象(因为ItemSource属性设置为数据库中的对象)而不是图像。它不使用模板。当我使用静态图像源时,它以正确的方式工作

我发现我需要覆盖PrepareContainerForItemOverride方法。但我不知道如何将模板添加到它。

非常感谢任何评论。

其他信息

以下是ToolboxItem的ControlTemplate:

         <ControlTemplate TargetType="{x:Type s:ToolboxItem}"> 
            <Grid> 
                <Rectangle Name="Border" 
                           StrokeThickness="1" 
                           StrokeDashArray="2" 
                           Fill="Transparent" 
                           SnapsToDevicePixels="true" /> 
                <ContentPresenter Content="{TemplateBinding ContentControl.Content}" 
                                  Margin="{TemplateBinding Padding}" 
                                  SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" /> 
            </Grid> 
            <ControlTemplate.Triggers> 
                <Trigger Property="IsMouseOver" 
                         Value="true"> 
                    <Setter TargetName="Border" 
                            Property="Stroke" 
                            Value="Gray" /> 
                </Trigger> 
            </ControlTemplate.Triggers> 
        </ControlTemplate>

4 个答案:

答案 0 :(得分:3)

ToolboxItem会覆盖ContentControl的默认样式。您尚未发布覆盖样式(来自generic.xaml),但我怀疑您的问题与该样式中定义的模板有关。您的ToolboxItem模板需要包含ContentPresenter,例如:

<Style TargetType="{x:Type local:ToolboxItem}">
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type local:ToolboxItem}">
        <Border Background="{TemplateBinding Background}"
                BorderBrush="{TemplateBinding BorderBrush}"
                BorderThickness="{TemplateBinding BorderThickness}">
          <ContentPresenter />
        </Border>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

或者,如果您不需要在ToolboxItem UI中执行任何特殊操作,只需删除DefaultStyleKeyProperty.OverrideMetadata来电。

请注意,您需要覆盖PrepareItemForContainerOverride。

答案 1 :(得分:1)

您已正确实施这些方法。正如我所怀疑的,问题在于您最近发布的ToolBoxItem ControlTemplate。如果它使用了普通的<ContentPresenter />你会没事的。你遇到了ContentPresenter的“魔法”属性,只有在你没有设置任何属性时才会自动设置。

以下是ControlTemplate中的问题代码:

<ContentPresenter
  Content="{TemplateBinding ContentControl.Content}"  
  Margin="{TemplateBinding Padding}"  
  SnapsToDevicePixels="{TemplateBinding UIElement.SnapsToDevicePixels}" />

问题是您正在设置Content属性但未设置ContentTemplate属性。 ContentPresenter具有自定义代码,可自动为其ContentContentTemplateContentTemplateSelector属性创建绑定,但仅当 Content属性未设置时才手动

在您的情况下,正在手动设置Content属性,因此禁用了自动机制,因此ContentTemplate为空。

虽然可以手动设置所有三个自动属性,如下所示:

<ContentPresenter
  Content="{TemplateBinding Content}"  
  ContentTemplate="{TemplateBinding ContentTemplate}"
  ContentTemplateSelector="{TemplateBinding ContentTemplateSelector}"
  Margin="{TemplateBinding Padding}"  
  SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />

您的最佳选择是完全省略它们,只接受ContentPresenter的默认行为,如下所示:

<ContentPresenter
  Margin="{TemplateBinding Padding}"  
  SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />

注意:.NET Framework 3.5添加了一个自动绑定属性ContentStringFormat,如果您手动绑定这三个属性而不是让ContentPresenter自动为您执行此操作,则会遗漏该属性。

答案 2 :(得分:0)

此代码是否直接从您的项目中复制?如果是这样,以下

ItemTemplate="ToolBoxtemplate"

应该是:

ItemTemplate="{StaticResource ToolBoxTemplate}"

我不完全确定,但您可能需要在PrepareContainerForItemOverride中显式设置Toolbox容器的ContentTemplate,因为您可能已经覆盖了自动设置模板的行为。您可能需要将其设置为Binding,因为我不确定在ItemTemplate更改时是否重新生成容器。

答案 3 :(得分:0)

看起来您的问题可能是您的url属性未在ToolBoxItem中公开。当您的项直接绑定到ToolBox时,url属性将直接暴露给DataTemplate。

为了使您的示例有效,ToolBoxItem需要具有:

public ImageTypeHere url { get; private set; }

如果这实际上是一个简单的实现,那么使用(或至少从中派生)ListBox并为ListBoxItems使用自定义DataTemplate和Style而不是创建自己的控件可能会让您受益更多。