Bind to an ItemsControl's Templated Parent from inside the ItemsControl.ItemTemplate - wpf

What is the proper way to bind to the Parent of an ItemsControl from within the ItemsControl.ItemTemplate?
Non working attempt:
<ControlTemplate TargetType="{x:Type local:ParentUserControl}">
<ItemsControl ItemsSource="{Binding MyCollectionViewSource.View, RelativeSource={RelativeSource TemplatedParent}}"
IsTabStop="False"
Focusable="False">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:ChildUserControl BoundProp1="{Binding Prop1}"
BoundObjProp2="{Binding RelativeSource={RelativeSource
Mode=FindAncestor,
AncestorType={x:Type local:ParentUserControl}}}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ControlTemplate>

The binding looks fine to me, but you do not specify a Binding.Path, are you sure that you want to bind directly to the control and not a property?

I had similar requirement and the following worked for me:
<ItemsControl ItemsSource="{Binding Items}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapGrid Orientation="Horizontal" ItemWidth="{Binding ItemWidth}" ItemHeight="{Binding ItemHeight}"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate >
<views:MyUserControl
Width="{Binding DataContext.ItemWidth, ElementName=PageRoot}"
Height="{Binding DataContext.ItemHeight, ElementName=PageRoot}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
P.S. my app is in XAML for WinRT

I have not found a solution to this problem. In the end I had to use work arounds which violated my desired separation of concerns, but functioned as expected. I believe this comes down to an issue in the wpf framework, hopefully 4.5 will fix it.

Related

How to use UI virtualization with redefined ListBox templates

I'm trying to use ListBox as a view containing multiple items and, of course, I need to use UI virtualization in it.
The problem is virtualization works only when I declare ListBox this way:
<ListBox
ItemsSource="{Binding ItemsSource}"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling">
<ListBox.ItemTemplate>
<DataTemplate>
<views:SiteEntryView />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
But if I try to customize it, it doesn't virtualizing anymore:
<ListBox
ItemsSource="{Binding ItemsSource}"
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling">
<ListBox.Template>
<ControlTemplate>
<ScrollViewer>
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<views:SiteEntryView />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
As far as I've found, this sample contains just the same that ListBox contains by default. But virtualization isn't working. I've read several articles and also couple of answers here, but still can't figure out the "general way" - what and where I must set, bind, add, etc to make virtualization work with custom templates?
Two things:
Update your PanelTemplate to use a VirtualizingStackPanel and add your virtualization options to the ScrollViewer of the ControlTemplate.
<ListBox.Template>
<ControlTemplate>
<ScrollViewer VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling">
<ItemsPresenter />
</ScrollViewer>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<views:SiteEntryView />
</DataTemplate>
</ListBox.ItemTemplate>
The reason is that you're using a StackPanel for your ItemsPanel - you should be using a VirtualizingStackPanel instead (which is also the default ItemsPanel for ListBox).
Either remove your ItemsPanel definition or modify it to use a VirtualizingStackPanel:
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>

Interaction.Triggers fail in MVVM listbox view

The xaml below works correctly when I click (MouseDown) anywhere on the background of the listbox. The problem is that I can not get it to work when I click on any single item in the listbox. I've tried putting the trigger code inside the border, image or TextBlock with no success.
<ListBox Name="SelectL2List" ItemsSource="{Binding L2Items, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" IsSynchronizedWithCurrentItem="True">
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseDown">
<si:CallDataMethod Method="HideSelectL2View" Target="{Binding}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate DataType="{x:Type ViewModels:MediaItemViewModel}" >
<Border BorderThickness="1,1,3,3" Margin="10" CornerRadius="3">
<StackPanel Orientation="Horizontal" >
<Image Source="{Binding Image}"/>
<TextBlock Text="{Binding L2Title}" />
</StackPanel>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Any help would be appreciated.
You have to make sure that things are hit testing as well, you can set the Background of the Border to Transparent to ensure that.
Also the mouse events are intercepted by the ListBoxItems, use the tunneling versions, i.e. PreviewMouseDown.

Using MouseDragElementBehavior with an ItemsControl and Canvas

I am currently having a problem using the MouseDragElementsBehavior from the Blend SDK when using a ItemsControl and a Custom Canvas. My custom canvas simply adds or removes the MouseDragElement from its children depending on a DependencyProperty. This worked just fine when I was manually adding Items to the Canvas' children but appears to have broken when moving to an ItemsControl.
I am currently using the following ItemsControl code:
<ItemsControl ItemsSource="{Binding Path=CanvasItems}">
<ItemsControl.DataContext>
<ViewModels:ViewModel/>
</ItemsControl.DataContext>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<my:CustomCanvas Background="Black" IsEditable="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}, Path=DataContext.CanEdit}" AllowDrop="{Binding RelativeSource={RelativeSource Self}, Path=IsEditable}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
Adding the Drag Behavior in the Canvas.VisualChildrenChanged method does not allow the newly created object to be moved like before.
Do I need to add the Drag behavior to something other then the ContentPresenter that is passed to VisualChildrenChanged or provide a special style?
I don't really know the blend sdk behaviours, but I've worked with behaviours in general, so I hope the same mechanisms apply.
If you want to add a behaviour to the controls created by an ItemsControl the best way is adding it via a setter in the ItemsControl.ItemContainerStyle, though in this case I found it easier to add it in the ItemsControl.ItemTemplate
Something like
<ItemsControl ItemsSource="{Binding CanvasItems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="Transparent" AllowDrop="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Green" BorderThickness="1" Background="AntiqueWhite">
<i:Interaction.Behaviors>
<ei:MouseDragElementBehavior ConstrainToParentBounds="True" DragBegun="MouseDragElementBehavior_DragBegun"/>
</i:Interaction.Behaviors>
<ContentControl Content="{Binding}" />
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<ItemsControl ItemsSource="{Binding CanvasItems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="YourControl">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="YourControl">
<Border>
<Grid>
<SystemWindowsInteractivity:Interaction.Behaviors>
<MicrosoftExpressionInteractivityLayout:MouseDragElementBehavior />
</SystemWindowsInteractivity:Interaction.Behaviors>
<ContentPresenter />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>

Silverlight CheckBoxList/RelativeSource Problem

I'm tyring to build a CheckBoxList to a Silverlight control I'm building and I'm having some trouble getting it right.
What I'm after, is a CheckBoxList that wraps the CheckBoxes vertically within a GridRow of * height. The problem I have is that I want to specify the height of the WrapPanel to be that of the row it is within.
In WPF it looks like the following:
<ScrollViewer BorderThickness="0"
HorizontalScrollBarVisibility="Auto"
VerticalScrollBarVisibility="Hidden" >
<ItemsControl Name="ic">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<toolkit:WrapPanel Orientation="Vertical"
Height="{Binding Path=ActualHeight,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType={x:Type ScrollContentPresenter}}}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Path=Description}" Margin="0,0,10,2" FontSize="12"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
Silverlight doesn't support RelativeSource in the same way so I'm unable to do it in the same way I did with WPF. I've seen some work arounds for RelativeSource, but they're either massively verbous or I can't seem to get them working.
Surely there's a simple way of setting the height of the WrapPanel in Silverlight?
In case anyone comes across this and doesn't already know, RelativeSource is being added in Silverlight 5

WPF ItemsPanelTemplate not working

I'm trying to get an ItemsPanelTemplate working for a ListBox. The ListBox is used in a DataTemplate, and none of my implicit ListBox styles override the default visual style. For some reason, the ItemsPanelTemplate I'm specifiying for the control (a WrapPanel) is being ignored and a StackPanel is used instead.
This is the entire template, I can only assume there's something I'm missing which is causing this.
<DataTemplate x:Key="GroupLargeIconsTemplate" DataType="{x:Type Core:IGroup}">
<ListBox ItemsSource="{Binding Children}" OverridesDefaultStyle="True">
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel HorizontalAlignment="Left" VerticalAlignment="Top" IsItemsHost="True" ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Auto"
Width="{Binding (FrameworkElement.ActualWidth), RelativeSource={RelativeSource AncestorType=ScrollContentPresenter}}" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<DataTemplate.Resources>
<conv:IconConverter x:Key="IconConverter"/>
</DataTemplate.Resources>
<StackPanel Margin="2" Width="100" Height="140">
<Image Source="{Binding Icon,Converter={StaticResource IconConverter},ConverterParameter=96}"/>
<TextBlock Text="{Binding Name}" TextWrapping="Wrap" TextTrimming="CharacterEllipsis" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DataTemplate>
My guess is that the width property in the WrapPanel is making it behave as a Stackpanel.
Strange, the control template is not supposed to override the explicitly specified values, rather the opposite scenario comes to my mind...
That is, unless the control template provides a panel with "IsItemsHost" set to true. Then the ItemsPanel is ignored. Which, probably, is your case.

Resources