0

I'm relatively new to wpf so I apologise in advance for any bad coding practices.

I'm trying to create a dashboard application which a user can customise by adding different controls (Tables, Graphs etc.) and move them around/resize them.

I was originally using a Canvas to draw my controls and had the moving and resizing working. Due to needing the content to be dynamic, I switched to using an ItemsControl like so:

<ItemsControl Name="dashboardCanvas" ItemsSource="{Binding Path=CanvasContents}" Grid.Row="1">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.Resources>
            <ControlTemplate x:Key="MoveThumbTemplate" TargetType="{x:Type local:MoveThumb}">
                <Rectangle Fill="Transparent"/>
            </ControlTemplate>
            <ControlTemplate x:Key="ResizeDecoratorTemplate" TargetType="Control">
                <Grid>
                    <local:ResizeThumb Height="2" Cursor="SizeNS" Margin="0 -4 0 0" VerticalAlignment="Top" HorizontalAlignment="Stretch"/>
                    <local:ResizeThumb Width="2" Cursor="SizeWE" Margin="-4 0 0 0" VerticalAlignment="Stretch" HorizontalAlignment="Left"/>
                    <local:ResizeThumb Width="2" Cursor="SizeWE" Margin="0 0 -4 0" VerticalAlignment="Stretch" HorizontalAlignment="Right"/>
                    <local:ResizeThumb Height="2" Cursor="SizeNS" Margin="0 0 0 -4" VerticalAlignment="Bottom"  HorizontalAlignment="Stretch"/>
                    <local:ResizeThumb Width="7" Height="7" Cursor="SizeNWSE" Margin="-6 -6 0 0" VerticalAlignment="Top" HorizontalAlignment="Left"/>
                    <local:ResizeThumb Width="7" Height="7" Cursor="SizeNESW" Margin="0 -6 -6 0" VerticalAlignment="Top" HorizontalAlignment="Right"/>
                    <local:ResizeThumb Width="7" Height="7" Cursor="SizeNESW" Margin="-6 0 0 -6" VerticalAlignment="Bottom" HorizontalAlignment="Left"/>
                    <local:ResizeThumb Width="7" Height="7" Cursor="SizeNWSE" Margin="0 0 -6 -6" VerticalAlignment="Bottom" HorizontalAlignment="Right"/>
                </Grid>
            </ControlTemplate>
            <ControlTemplate x:Key="DesignerItemTemplate">
                <Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
                    <local:MoveThumb Template="{StaticResource MoveThumbTemplate}" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" Cursor="SizeAll"/>
                    <Control Template="{StaticResource ResizeDecoratorTemplate}"/>
                    <ContentPresenter Content="{TemplateBinding ContentControl.Content}"/>
                    <Button Content="x" VerticalAlignment="Top" HorizontalAlignment="Right" Width="10" Height="10" Click="Button_Click"/>
                </Grid>
            </ControlTemplate>
        </ItemsControl.Resources>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Grid DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}">
                    <local:MoveThumb Template="{StaticResource MoveThumbTemplate}" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}" Cursor="SizeAll"/>
                    <Control Template="{StaticResource ResizeDecoratorTemplate}"/>
                    <ContentPresenter Content="{TemplateBinding ContentControl.Content}"/>
                    <Button Content="x" VerticalAlignment="Top" HorizontalAlignment="Right" Width="10" Height="10" Click="Button_Click"/>
                </Grid>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemContainerStyle>
            <Style>
                <Setter Property="Canvas.Top" Value="{Binding Path=Y}"/>
                <Setter Property="Canvas.Left" Value="{Binding Path=X}"/>
            </Style>
        </ItemsControl.ItemContainerStyle>
    </ItemsControl>

I now have a problem where any controls I add to the ItemsControl do not have the template applied to them.

I've seen this question: Why Does ItemsControl Not Use My ItemTemplate? But I can't inherit ItemsControl into my controls as they already inherit from ContentControls.

Here is my main Control Object:

class TableControl: DashboardItem
{
    public TableControl()
    {
        Width = 100;
        Height = 100;
        Content = new Ellipse
            {
                Fill = new SolidColorBrush(Colors.Green),
                IsHitTestVisible = false
            };
    }

    public int X
    {
        get
        {
            return 10;
        }
    }

    public int Y
    {
        get
        {
            return 200;
        }
    }
}

DashboardItem at the moment is simply:

class DashboardItem : ContentControl
{

}

In the code behind I have an ObservableCollection of DashboardItems, which the ItemsControl is bound to.

How would I force the ItemsControl Template to be applied to all items in the control?

Community
  • 1
  • 1
Blinx
  • 400
  • 4
  • 17

2 Answers2

2

There is strange mix in your code. You must not have an ObservableCollection of ContentControl. Make your objects DashboardItems , real business objets (or viewmodel objets), may be holding X and Y properties, but without inheritance of ContentControl.

It seems that WPF doesn't care of your Template if you provide a GUI object.

If you need to have different appearances for different items of the Items Control, then you 'll be able to use
-a TemplateSelector (for selecting Template for a given line)
-or the DataType property of the DataItemTemplate

ASh
  • 34,632
  • 9
  • 60
  • 82
Emmanuel DURIN
  • 4,803
  • 2
  • 28
  • 53
  • I've changed DashboardItems to just include an X and Y value (without any inheritance) and added in an ellipse (just as a placeholder) into the DataTemplate of the ItemsControl. It's now applying the template although the moving and resizing aren't working. I think this may be something to do with the dataContexts but I don't know – Blinx Sep 18 '15 at 09:46
  • @Blix, yes you can, just DON't change the Datacontext of your Grid : – Emmanuel DURIN Sep 18 '15 at 09:51
  • By default your DataTemplate has the correct DataContext: ` `. It works for me. – Emmanuel DURIN Sep 18 '15 at 09:52
  • It's all displaying as it should but the functionality of the MoveThumb and ResizeThumb aren't working. MoveThumb and ResizeThumb are both classes in my project. What should the datacontext be for the controls in the template? – Blinx Sep 18 '15 at 10:08
  • Aren't working ? You mean not visible ...? What's the problem ? From what I see in your XAML code you don't use DataBinding in MoveThumb and ResizeThumb so databinding/datacontext doesn't seem important – Emmanuel DURIN Sep 18 '15 at 11:40
  • Added another solution below. You subclass ItemsControl, then return `false` for `IsItemItsOwnContainerOverride`. This forces container creation. By the way, I only figured this out after seeing your answer, so thanks! :) – Mark A. Donohoe Nov 16 '20 at 06:00
1

Just adding another solution. As the accepted answer points out, if you use an ItemsControl directly, any GUI objects you pass in will simply be arranged on the ItemsPanel as-is, thus bypassing your DataTemplate entirely. (If it's not a GUI object, it's wrapped in a ContentPresenter first.)

To get around this, you can create a subclass of ItemsControl that you force to always create a container for your items. You do this by returning false for IsItemItsOwnContainerOverride. This tells the control that the item has to be wrapped in a container first--which again, in the default implementation of an ItemsControl is a simple ContentPresenter--and thus it will always apply any given ItemTemplate.

Here's the code...

public class EnsureContainerItemsControl : ItemsControl {

    protected override bool IsItemItsOwnContainerOverride(object item) {
        return false; // Force the control to always generate a container regardless of what was passed in
    }
}
Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286