WPF ItemsControl -> ContentPresenter - wpf

I hope someone can help me with my ItemsControl Problem :)
I use a ItemsControl with a Grid as an ItemsPanel to Add/Remove Column/RowDefinitions in MVVM (and also the Cell Content)
this works fine, but when I try to add a GridSplitter the splitter is shown but he doesnt work. The reason is the ItemsControl adds a new ContentPresenter for each item...
Is there any way to replace the ContentPresenter with the GridSplitter Control when the Type of an item in the children property is typeof SplitterVM ??
I'm also trying to bind the Splitters on the Grid as an DependencyProperty but when I use ItemsPanel it doesn't work because I cannot add Children to the Panel...
Here is my code:
<DataTemplate DataType="{x:Type vm:GridVM}">
<ItemsControl ItemsSource="{Binding Children}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<view:LayoutGridControl Rows="{Binding Rows}" Columns="{Binding Columns}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Row" Value="{Binding Model.RowIndex}"/>
<Setter Property="Grid.Column" Value="{Binding Model.ColumnIndex}"/>
<Setter Property="Grid.RowSpan" Value="{Binding Model.RowSpan}"/>
<Setter Property="Grid.ColumnSpan" Value="{Binding Model.ColumnSpan}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</DataTemplate>

Related

How to make scrollbar visible around a Canvas in WPF

I have a list of items, which I display on a Canvas. I display those items vertically, the next below the previous, just like in ListBox or ListView.
Because I need to be able to drag those items around and for that purpose I have to use a Canvas, since Canvas allows me to set concrete positions for the items.
There are many items in the collection reaching outside the boundaries of the window when displayed and for that I need to use a ScrollViewer. The problem here is that if I don't set the Canvas's height, then the ScrollViewer isn't visible.
<ScrollViewer VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="500" /> // Can't set Height here
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Left}" />
<Setter Property="Canvas.Top" Value="{Binding Top}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</ScrollViewer>
What could I do to make the vertical scroll bar around canvas visible?
So, based on #EdPlunkett's answer I came up with this solution:
I added bindings to MinHeight and MinWidth of the ItemsControl (altought the normal Height and Width would work as well I suppose).
<ScrollViewer VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding AlbumItems}"
MinHeight="{Binding EditorHeight}"
MinWidth="{Binding EditorWidth}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Left}" />
<Setter Property="Canvas.Top" Value="{Binding Top}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</ScrollViewer>
and in my data context I call this method to calculate the size and set the EditorHeight and EditorWidth properties to their calculated values.
private void calculateEditorSize()
{
int maxEditorHeight = AlbumItems.Max((component) => component.Top + component.Height);
int maxEditorWidth = AlbumItems.Max((component) => component.Left + component.Width);
EditorHeight = maxEditorHeight;
EditorWidth = maxEditorWidth;
}

Using a ScrollViewer with an ItemsControl with a Canvas as the ItemsPanel

I have an ItemsControl and I set the ItemsPanel to a Canvas. The Canvas needs to be able to dynamically size with the content I put in it, and I need to be able to scroll when content runs off the bounds of the control. The trouble is I can't get the content to scroll. I have the scroll bar visibilities set to auto, so I don't end up seeing the scroll bars pop up when content runs off the edge.
I tried both putting the ItemsControl inside a ScrollViewer, and I tried using a ScrollViewer in the ItemsControl's Template.
Here's the ItemsControl inside the ScrollViewer:
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<ItemsControl ItemsSource="{Binding Tiles}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding Left}" />
<Setter Property="Canvas.Top" Value="{Binding Top}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplateSelector>
...
</ItemsControl.ItemTemplateSelector>
</ItemsControl>
</ScrollViewer>
And here it is with the ScrollViewer in the Template:
<ItemsControl ItemsSource="{Binding Tiles}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding Left}" />
<Setter Property="Canvas.Top" Value="{Binding Top}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemTemplateSelector>
...
</ItemsControl.ItemTemplateSelector>
</ItemsControl>
This post on MSDN seemed promising, but implementing it on my code, or even explicitly with a Canvas in lieu of a WrapPanel wasn't working (or, should I say, I wasn't able to get it to work).
I've also taken a look at this post but the solution doesn't work for me as I need the canvas to be able to size with the content (otherwise the scroll bars are always visible).
Thank you kindly, in advance!
The canvas's width/height when not explicitly set, will inherit from the ItemsControl. The "dynamic sizing" behavior you're expecting isn't how panels sizing and layout works out of the box.
You options are to:
Explicitly set the ItemsControl Width/Height.
Explicitly set the Canvas Width/Height.

Dragging behavior is not working for items in ItemsControl: MouseDragElementBehavior

I have a class
public class DrawingCanvas : FrameworkElement
which uses MouseDragElementBehavior to implement dragging.
I want to drag the DrawingCanva like a layer in Photoshop relative to another layer
Working alone with (XAML on pic #1)
<Grid x:Name="_layoutRootControl">
<Canvas
Grid.Column="0">
<canvas:DrawingCanvas />
<canvas:DrawingCanvas />
</Canvas>
</Grid>
do allows dragging, but when I place DrawingCanvas inside ItemsControl
and bind ItemsSource to collection of items, draggins doesn't work.
(XAML on pic #2)
<ItemsControl ItemsSource="{Binding Source={StaticResource Locator}, Path=LayersViewModel.Layers}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="0" />
<Setter Property="Canvas.Top" Value="0" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<canvas:DrawingCanvas />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
I don't understand why.
First case is the following XAML structure:
And the second case is the following XAML structure:
Update:
I've found out that in second case with ItemsControl a DrawingCanvas.Parent = null
In connection with that item.Parent inside ItemsControls is always null
I've found the solution in the following topics with explanation:
Why does the Parent property of a container of an ItemsControl return null and not the Panel it sits on?
Combining ItemsControl with draggable items - Element.parent always null
Update:
Now the Parent is not null, still the problem remains.
Update 2
The last lacking thing
I had to define "extenders:DragHelper.CanDrag" inside ContentPresenter instead of
<ItemsControl.ItemTemplate>
The resulting template is as follows, so the issue is solved:
<ItemsControl
ItemsSource="{Binding Source={StaticResource Locator}, Path=LayersViewModel.Layers}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property=**"extenders:DragHelper.CanDrag"** Value="True"/>
<Setter Property="Canvas.Left" Value="{Binding Path=X}" />
<Setter Property="Canvas.Top" Value="{Binding Path=Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<surfaces:DrawingCanvas />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

How to use Canvas as the ItemsPanel for an ItemsControl in Silverlight 3

I am trying to set the Canvas properties in an ItemsControl DataTemplate with Silverlight 3. According to this post, the only way of doing that is to set it using the ItemsContainerStyle for the ContentPresenter type, since the Canvas properties only take effect on direct children of the Canvas. This doesn't seem to work in SL3, since the ItemsControl doesn't have an ItemsContainerStyle property, so I tried a ListBox as advised by this article, but it still doesn't work. From the XAML below, I would expect to see a green square, with the numbers 10, 30, 50, 70 cascading from "NW" to "SE" direction. Can anyone tell me why they are all stacked on top of eachother in the NW corner?
<UserControl x:Class="TestControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:System="clr-namespace:System;assembly=mscorlib" >
<StackPanel>
<ListBox>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="Green" Width="100" Height="100" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding}" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding}" />
<Setter Property="Canvas.Top" Value="{Binding}" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.Items>
<System:Int32>10</System:Int32>
<System:Int32>30</System:Int32>
<System:Int32>50</System:Int32>
<System:Int32>70</System:Int32>
</ListBox.Items>
</ListBox>
</StackPanel>
</UserControl>
I'm not sure if it will work in your scenario, but I've accomplished this in the past using the RenderTransform.
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="Green" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding}">
<TextBox.RenderTransform>
<TranslateTransform X="100" Y="100" />
</TextBox.RenderTransform>
</TextBox>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Items>
<System:Int32>10</System:Int32>
<System:Int32>30</System:Int32>
<System:Int32>50</System:Int32>
<System:Int32>70</System:Int32>
</ItemsControl.Items>
</ItemsControl>
Or in the case of binding you will need to use a converter
<ItemsControl>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="Green" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding}" RenderTransform="{Binding Converter={StaticResource NumberToTransformGroupConverter}}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.Items>
<System:Int32>10</System:Int32>
<System:Int32>30</System:Int32>
<System:Int32>50</System:Int32>
<System:Int32>70</System:Int32>
</ItemsControl.Items>
</ItemsControl>
Converter
public void ConvertTo(object value, ...)
{
int intValue = int.Parse(value.ToString());
return new TransformGroup()
{
Children = new TransformCollection()
{
new TranslateTransform { X = intValue, Y = intValue }
}
};
}
Silverlight4 does not bind to attached properties in style. I suggest using David Anson's approach described here.
<UserControl.Resources>
<Style x:Key="ScreenBindStyle" TargetType="ListBoxItem">
<Setter Property="Helpers:SetterValueBindingHelper.PropertyBinding">
<Setter.Value>
<Helpers:SetterValueBindingHelper>
<Helpers:SetterValueBindingHelper Type="Canvas" Property="Left" Binding="{Binding LocationField.Value.X}" />
<Helpers:SetterValueBindingHelper Type="Canvas" Property="Top" Binding="{Binding LocationField.Value.Y}" />
<Helpers:SetterValueBindingHelper Type="Canvas" Property="ZIndex" Binding="{Binding ZIndex.Value}" />
</Helpers:SetterValueBindingHelper>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
And in listbox:
ItemContainerStyle="{StaticResource ScreenBindStyle}"
Old post but I went into the same problem but now with SL5 that now allows Binding in style setters. I was trying to avoid to use the ListBox (because it handles selection and so on) and the ItemsControl still doesn't have an ItemContainerStyle. So I tried a few things.
I haven't found many subject discussing this problem so let me share my solution (sorry if it duplicates)
In fact, I found a very convenient way to solve the problem by adding an unnamed Style in the ItemsControl resources :
<ItemsControl ItemsSource="{Binding Path=MyData}">
<ItemsControl.Resources>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Top" Value="{Binding Path=Bounds.Top}"/>
<Setter Property="Canvas.Left" Value="{Binding Path=Bounds.Left}"/>
<Setter Property="Width" Value="{Binding Path=Bounds.Width}"/>
<Setter Property="Height" Value="{Binding Path=Bounds.Height}"/>
</Style>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="my:DataType">
...
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Works like a charm in SL5 :)
I can't explain what you are seeing either. Your Xaml is broken in at least a couple of ways.
First the Xaml itself fails because this:-
<Style TargetType="ContentPresenter">
should be
<Style TargetType="ContentControl">
the Item Containers in the case of ListBox are of type ListBoxItem which derive from ContentControl.
Still without that the placing {Binding} in style setters still doesn't work. I guess you were imagining that the style would be applied to each item in turn and get its Value from the current item. However even if binding worked in the style, there would only be one style and it would get its data binding from the ListBox DataContext. This is a different DataContext that applies to each ListBox item (which in this case is each Item in the Items collection).
Still I think Ben has a reasonable solution in place which eliminates this approach.

TemplateBinding in ItemsPanelTemplate

I'm building a custom ItemsControl in Silverlight that (amongst other things) allows items to be displayed horizontally or vertically at runtime. How can I bind the Orientation property of the ItemsPanel to the Orientation property of my parent control? I've tried using TemplateBinding (which works inside the ControlTemplate) but does not seem to work inside the ItemsPanelTemplate, am I doing something wrong?
<Style TargetType="CustomItemsControl">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="{TemplateBinding Orientation}" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
Use a RelativeSource:
<Style TargetType="CustomItemsControl">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<StackPanel Orientation="{Binding Orientation, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type CustomItemsControl}}}" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
Edit after comment: Silverlight doesn't support RelativeSource, but this post by Colin Eberhardt explains how it can be implemented manually.

Resources