views:

298

answers:

5

Hi, I am creating a custom control Toolbox that is derived from ItemsControl. This toolbox is supposed to be filled with icons coming from the database. The definition looks like this:

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

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

Toolboxitem is derived from ContentControl.

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

Since the number of icons stored in a database is not known I want to use the data template:

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

Then I want the Toolbox to use the template.

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

I'm using ADO.NET entity framework to connect to a database. The code behind:

SystemicsAnalystDBEntities db = new SystemicsAnalystDBEntities();

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

However, there is a problem. When the code is executed, it displays the object from the database (as the ItemSource property is set to the object from the database) and not the images. It does not use the template. When I use the static images source it works in the right way

I found out that I need to override the PrepareContainerForItemOverride method.But I don't know how to add the template to it.

Thanks a lot for any comments.

Additional Information

Here is the ControlTemplate for ToolboxItem:

         <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>
A: 

Is this code directly copied from your project? If so, the following

ItemTemplate="ToolBoxtemplate"

should be:

ItemTemplate="{StaticResource ToolBoxTemplate}"

I'm not entirely sure, but you might need to set the ContentTemplate of your Toolbox container explicitly in PrepareContainerForItemOverride, since you may have overridden the behavior that automatically sets the template. You may need to set it as a Binding, as I'm not sure if the containers are re-generated if the ItemTemplate changes.

Dan Bryant
Sorry for that. In the project it is set correctly.
EVA
I even try it like this: <d:Toolbox x:Name="NewLibrary" DefaultItemSize="55,55" ItemsSource="{Binding}" > <ItemsControl.ItemTemplate> <DataTemplate> <StackPanel> <Image Source="{Binding Path=url}" /> </StackPanel> </DataTemplate> </ItemsControl.ItemTemplate> </d:Toolbox>
EVA
He shouldn't need to set the ContentTemplate -- I ran up a quick sample for this and I didn't need to. The ItemsControl base class behaviour will handle that for him.
itowlson
A: 

It looks like your problem could be that your url property is not exposed within ToolBoxItem. When your items are bound directly to ToolBox the url property is directly exposed to the DataTemplate.

In order for your example to work, ToolBoxItem would need to have:

public ImageTypeHere url { get; private set; }

If this is really a simple implementation it would probably benefit you more to use (or at least derive from) a ListBox and use a custom DataTemplate and Style for your ListBoxItems rather than creating your own control.

Jeff Wain
But if I were to use the static resource it works fine. An example:<Toolbox> <ItemsControl.Items> <Image Source="image.jpg" /></closing tag></Toolbox>
EVA
+3  A: 

ToolboxItem is overriding the default style for ContentControl. You haven't posted the overridding style (from generic.xaml), but I suspect your problem is with the template defined in that style. Your ToolboxItem template needs to contain a ContentPresenter, e.g.:

<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>

Alternatively, if you don't need to do anything special in the ToolboxItem UI, just remove the DefaultStyleKeyProperty.OverrideMetadata call.

Note that you do not need to override PrepareItemForContainerOverride.

itowlson
@itowlson: Hey, I'm copying you! No, actually it is a just a curious coincidence that in the last half hour we've both posted several answers to questions only seconds apart from each other. It is *not* a coincidence that our answers have been substantially identical in their messages: It just goes to show we both know what we're talking about. Nice working with you. :-)
Ray Burns
A: 

You have correctly implemented the methods. The problem is, as I suspected, in your ToolBoxItem ControlTemplate which you posted recently. If it had used an ordinary <ContentPresenter /> you would have been fine. You ran into ContentPresenter's "magic" properties which are only set automatically if you don't set any of them.

Here is the problem code in your ControlTemplate:

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

The problem is that you are setting the Content property but not setting the ContentTemplate property. ContentPresenter has custom code that automatically creates bindings for its Content, ContentTemplate, and ContentTemplateSelector properties, but only if the Content property is not set manually.

In your case, the Content property is being set manually, so the automatic mechanism is disabled and so ContentTemplate is null.

Although it would be possible to manually set all three automatic properties like this:

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

Your best choice is to omit them entirely and just accept ContentPresenter's default behavior, like this:

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

Note: NET Framework 3.5 adds a fouth automatically-bound property, ContentStringFormat, which would be missed if you manually bound the three properties instead of letting ContentPresenter do it for you automatically.

Ray Burns
itowlson said the same thing as I did but said it better
Ray Burns
Thanks a lot, but could you have a look at my last answer? There property is set in ToolBoxItem.
EVA
Thanks for posting the ToolBoxItem code. I have updated my answer explaining what went wrong and how to fix it. Now my answer is actually better than itowlson's. :-)
Ray Burns
@EV: You seem to have vanished since I updated this answer in response to your ToolBoxItem post. Did my new answer work for you? If so, you should mark it as the correct answer. It not, how did it fail?
Ray Burns
A: 

Hi, thanks for answering, both of you :) The ToolBox control contains many ToolBox Items that can be dragged and dropped. So I have to change the ContentPresenter in the ToolboxItem style but it alreday has this property defined:

    <Setter Property="Template">
        <Setter.Value>
            <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>
        </Setter.Value>
    </Setter>
</Style>

How to change that then?

EVA