WPF: When is a WrapPanel full - wpf

I'm using a WrapPanel to display variable height items in columns. The wrappanel has a constrained size.
Is there an way to determine when the WrapPanel is 'full'? I will then page to another panel with an animation.
I've looked at the ArrangeOverride of the items that are the panels children, but they always seem to be getting all the space they want. I need a way to determine when they begin getting clipped.

Here's an example using a ScrollViewer with a trigger to determine whether it would display using ScrollableHeight. Right now it just changes some text but you could do other things. Removing one of the Rectangles will fire the trigger:
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="100" Height="50">
<ScrollViewer x:Name="scrollViewer" VerticalScrollBarVisibility="Hidden">
<WrapPanel>
<Rectangle Width="50" Height="20" Fill="Red"/>
<Rectangle Width="50" Height="20" Fill="Blue"/>
<Rectangle Width="50" Height="20" Fill="Green"/>
<Rectangle Width="50" Height="20" Fill="Yellow"/>
<Rectangle Width="50" Height="20" Fill="Orange"/>
</WrapPanel>
</ScrollViewer>
<TextBlock IsHitTestVisible="False">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Text" Value="Clipped"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=scrollViewer, Path=ScrollableHeight}" Value="0">
<Setter Property="Text" Value="Not Clipped"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
You could also trigger based on ScrollViewer.ComputedVerticalScrollBarVisibility, but that requires the ScrollBar to actually be visible, whereas when you trigger based on ScrollableHeight, the ScrollBar can be hidden.

Actually using a WrapPanel for what you are trying to achieve doesn't seem like a good idea.
"[...] I will then page to another panel with an animation."
This would be layout to layout animation what isn't to easy, too.
You should write your own panel class: see here or (animated) here

Related

Images and Buttons not filling grid cells WPF XAML

So I have 2 similar problems with the below image. The image is of a UserControl that has been added to a viewbox inside another UserControl:
Image
The button in the top-left does not fill the whole of the grid cell, but the image within it fills the whole button .I have been trying out different methods of binding button images. This is why this is not the same for the other buttons in the grid. However I can't see how they should differ
The black images on the centre top, left, right, and bottom buttons don't fill their buttons. However, when editing the code, the designer shows them filling those buttons. The code for these is identical to the ContentControl above, but has an image path linking to a completely black image instead.
Was wondering if these problems might be related or have a similar fix? I've tried using extra viewboxes and stretch to fill/ alignment parameters but they seem to make no difference.
EDIT:
Implementing Location.xaml in parent UserControl:
<Viewbox Grid.Row="1" Grid.Column="1" Grid.RowSpan="2" Margin="10 10 10 10">
<ContentControl Content="{Binding CurrentLocationViewModel}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type locationViewModel:SmallRoomViewModel}">
<locationView:SmallRoomView/>
</DataTemplate>
<DataTemplate DataType="{x:Type locationViewModel:LargeRoomViewModel}">
<locationView:LargeRoomView/>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</Viewbox>
SmallRoomView.xaml (the UserControl in the image):
<Grid>
<Grid.Resources>
<Button x:Key="wallTile" x:Shared="false" PreviewMouseDown="OnMouseDown" Style="{StaticResource appWallTile}"/>
<Button x:Key="floorTile" x:Shared="false" PreviewMouseDown="OnMouseDown" Style="{StaticResource appFloorTile}"/>
<Button x:Key="doorTile" x:Shared="false" PreviewMouseDown="OnMouseDown" Style="{StaticResource appDoorTile}"/>
</Grid.Resources>
<Button Grid.Row="0" Grid.Column="0" PreviewMouseDown="OnMouseDown">
<Image Stretch="Fill" Source="{Binding tile0_0}"/>
</Button>
<ContentControl Grid.Row="0" Grid.Column="2" Content="{StaticResource doorTile}"/>
<ContentControl Grid.Row="0" Grid.Column="3" Content="{StaticResource wallTile}"/>
...
</Grid>
App.xaml:
<Style x:Key="appDoorTile" TargetType="Button" x:Shared="false">
<Setter Property="Content">
<Setter.Value>
<Image Stretch="Fill" Source="./AllResources/images/tiles/doorTile.png"/>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="appFloorTile" TargetType="Button" x:Shared="false">
<Setter Property="Content">
<Setter.Value>
<Image Stretch="Fill" Source="./AllResources/images/tiles/floors/floorTile.png"/>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="appWallTile" TargetType="Button" x:Shared="false">
<Setter Property="Content">
<Setter.Value>
<Image Stretch="Fill" Source="./AllResources/images/tiles/walls/wallTile.png"/>
</Setter.Value>
</Setter>
</Style>

wpf mouseover margin in stackpanel

I have a tab item, header having the following form: image_margin_textblock.
The trigger IsMouseOver is working properly when the mouse cursor is over the image, and also over the textblock. But, when the mouse cursor is over the margin between the Image and Textblock, the IsMouseOver trigger is not fired. This creates an annoying flickering effect.
Do you have any ideas how to achieve mouseover trigger over the margin?
Here is the code:
<TabItem.Header>
<ContentControl>
<ContentControl.Template>
<ControlTemplate>
<StackPanel x:Name="sp0" Orientation="Horizontal">
<StackPanel x:Name="sp1" Orientation="Horizontal" Background="Blue">
<Image VerticalAlignment="Center" HorizontalAlignment="Center" Source="tab1.png"/>
</StackPanel>
<TextBlock Margin="10,0,0,0" Text="Tab1" VerticalAlignment="Center"/>
</StackPanel>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type TabItem}}}" Value="True">
<Setter TargetName="sp1" Property="StackPanel.Background" Value="Green"/>
</DataTrigger>
<Trigger SourceName="sp0" Property="IsMouseOver" Value="True">
<Setter TargetName="sp1" Property="StackPanel.Background" Value="Green"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</ContentControl.Template>
</ContentControl>
Thank you.
Set Background on outer StackPanel to Transparent so that margin also participates in HitTest (i.e. respond to mouse events).
Right now only image and TextBlock area responds to MouseOver event. Setting background to Transparent will work.
<StackPanel x:Name="sp0" Orientation="Horizontal" Background="Transparent">
Set background of your StackPanel to Transparent. This makes it visible to hit test.
<StackPanel x:Name="sp0" Orientation="Horizontal" Background="Transparent">
<StackPanel x:Name="sp1" Orientation="Horizontal" Background="Blue">
<Image VerticalAlignment="Center" HorizontalAlignment="Center" Source="tab1.png"/>
</StackPanel>
<TextBlock Margin="10,0,0,0" Text="Tab1" VerticalAlignment="Center"/>
</StackPanel>

ComboBox with two columns in popup

I need to create a custom control containing a combobox whose popup will have the Name propeprty of the bound objects aligned to the left, and the CreatedDate property of the bound objects aligned to the right in each pop up item. Also the Name and the CreatedDate must not overlap. The Name of the object is of variable length
I tried solving this problem using a DataTemplate in the Combobox.ItemTemplate, inside the data template I have a grid with two columns aligned appropriately. The Grid's horizontal alignment is set to Stretch but for some reason the Grid doesn't fill out the available space in the popup. Does anyone know how to get around this and why it happens? I am using WPF 3.5.
<UserControl.Resources>
<CollectionViewSource x:Key="cvsProcessingSessionList" Source="{Binding ProcessingSessionList, ElementName=View}"/>
<Style TargetType="{x:Type ComboBox}"
BasedOn="{StaticResource {x:Static res:ObjectResources.LEComboBoxStyle}}">
<Setter Property="Margin" Value="5,3"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="VerticalAlignment" Value="Center"/>
</Style>
</UserControl.Resources>
<GroupBox x:Name="grbProcessingSession">
<GroupBox.Header>
<Bold>Processing Session:</Bold>
</GroupBox.Header>
<GroupBox.Content>
<ComboBox
x:Name="ccbProcessingSessionName"
ItemsSource="{Binding Source={StaticResource cvsProcessingSessionList}}"
Loaded="OnLoaded" TextSearch.TextPath="Name"
IsEditable="True" MaxDropDownHeight="500" >
<ComboBox.ItemTemplate>
<DataTemplate>
<Grid Background="Pink" HorizontalAlignment="Stretch" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="100" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" TextAlignment="Left" HorizontalAlignment="Left"
Margin="0,0,5,0" Text="{Binding Name}"/>
<TextBlock Grid.Column="1" TextAlignment="Right" HorizontalAlignment="Right"
Text="{Binding CreatedDate, StringFormat=dd/MM/yyyy}"/>
</Grid>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</GroupBox.Content>
</GroupBox>
Just add this binding to your grid in yiour DataTemplate ...
Width="{Binding ActualWidth,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type ComboBoxItem}},
Mode=OneTime}"
Also for better effect, apply the background color to the ComboBoxItem and not to the grid...
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="Background" Value="Pink"/>
</Style>
</ComboBox.ItemContainerStyle>
Whenever I want to do the same that you are asking I add:
<ComboBox ...>
<ComboBoxItem HorizontalContentAlignment="Stretch">
</ComboBoxItem>
</ComboBox>
The thing is that I place my custom DataTemplate inside the ComboBoxItem.
Maybe you should try it...
Actually, the better way is to set HorizontalContentAlignment="Stretch" at the ComboBox level. This will work even if width of ComboBox changes (e.g. if it is in resizable container).
However, be aware that this works in WPF and Silverlight 5 but don't work in some older versions of Silverlight.

How to make overlay control above all other controls?

I need to make a control appear above all other controls, so it will partially overlay them.
If you are using a Canvas or Grid in your layout, give the control to be put on top a higher ZIndex.
From MSDN:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" WindowTitle="ZIndex Sample">
<Canvas>
<Rectangle Canvas.ZIndex="3" Width="100" Height="100" Canvas.Top="100" Canvas.Left="100" Fill="blue"/>
<Rectangle Canvas.ZIndex="1" Width="100" Height="100" Canvas.Top="150" Canvas.Left="150" Fill="yellow"/>
<Rectangle Canvas.ZIndex="2" Width="100" Height="100" Canvas.Top="200" Canvas.Left="200" Fill="green"/>
<!-- Reverse the order to illustrate z-index property -->
<Rectangle Canvas.ZIndex="1" Width="100" Height="100" Canvas.Top="300" Canvas.Left="200" Fill="green"/>
<Rectangle Canvas.ZIndex="3" Width="100" Height="100" Canvas.Top="350" Canvas.Left="150" Fill="yellow"/>
<Rectangle Canvas.ZIndex="2" Width="100" Height="100" Canvas.Top="400" Canvas.Left="100" Fill="blue"/>
</Canvas>
</Page>
If you don't specify ZIndex, the children of a panel are rendered in the order they are specified (i.e. last one on top).
If you are looking to do something more complicated, you can look at how ChildWindow is implemented in Silverlight. It overlays a semitransparent background and popup over your entire RootVisual.
Robert Rossney has a good solution. Here's an alternative solution I've used in the past that separates out the "Overlay" from the rest of the content. This solution takes advantage of the attached property Panel.ZIndex to place the "Overlay" on top of everything else. You can either set the Visibility of the "Overlay" in code or use a DataTrigger.
<Grid x:Name="LayoutRoot">
<Grid x:Name="Overlay" Panel.ZIndex="1000" Visibility="Collapsed">
<Grid.Background>
<SolidColorBrush Color="Black" Opacity=".5"/>
</Grid.Background>
<!-- Add controls as needed -->
</Grid>
<!-- Use whatever layout you need -->
<ContentControl x:Name="MainContent" />
</Grid>
Controls in the same cell of a Grid are rendered back-to-front. So a simple way to put one control on top of another is to put it in the same cell.
Here's a useful example, which pops up a panel that disables everything in the view (i.e. the user control) with a busy message while a long-running task is executed (i.e. while the BusyMessage bound property isn't null):
<Grid>
<local:MyUserControl DataContext="{Binding}"/>
<Grid>
<Grid.Style>
<Style TargetType="Grid">
<Setter Property="Visibility"
Value="Visible" />
<Style.Triggers>
<DataTrigger Binding="{Binding BusyMessage}"
Value="{x:Null}">
<Setter Property="Visibility"
Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<Border HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Background="DarkGray"
Opacity=".7" />
<Border HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="White"
Padding="20"
BorderBrush="Orange"
BorderThickness="4">
<TextBlock Text="{Binding BusyMessage}" />
</Border>
</Grid>
</Grid>
Put the control you want to bring to front at the end of your xaml code. I.e.
<Grid>
<TabControl ...>
</TabControl>
<Button Content="ALways on top of TabControl Button"/>
</Grid>
This is a common function of Adorners in WPF. Adorners typically appear above all other controls, but the other answers that mention z-order may fit your case better.
<Canvas Panel.ZIndex="1" HorizontalAlignment="Left" VerticalAlignment="Top" Width="570">
<!-- YOUR XAML CODE -->
</Canvas>

Silverlight 3: ListBox DataTemplate HorizontalAlignment

I have a ListBox with it's ItemTemplate bound to a DataTemplate. My problem is I cannot get the elements in the template to stretch to the full width of the ListBox.
<ListBox x:Name="listPeople" VerticalAlignment="Stretch" HorizontalAlignment="Stretch"
Margin="0,0,0,0" Background="{x:Null}" SelectionMode="Extended" Grid.Row="1"
ItemTemplate="{StaticResource PersonViewModel.BrowserDataTemplate}"
ItemsSource="{Binding Mode=OneWay, Path=SearchResults}" >
</ListBox>
<DataTemplate x:Key="PersonViewModel.BrowserDataTemplate">
<ListBoxItem HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Margin="5,5,5,5">
<Border Opacity=".1" x:Name="itemBorder" Background="#FF000000"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
CornerRadius="5,5,5,5" MinWidth="100" Height="50"/>
</Grid>
</ListBoxItem>
</DataTemplate>
As you can see, I have added a border within the grid to indicate the width of the template. My goal is to see this border expand to the full width of the listbox. Currently its width is determined by its contents or MinWidth, which is the only thing at the moment keeping it visible at all.
I spent an hour trying to resolve this one. Very very frustrasting. You don't have to override the entire default style for the ListBoxItem. I couldn't get this to work. In the end I resolved the issue by simply overriding just the HorizontalContentAlignment property in my ListBox.ItemContainerStyle section e.g:
<ListBox x:Name="ClassList" ItemsSource="{Binding LineClasses}"
ScrollViewer.VerticalScrollBarVisibility="Visible"
SelectionMode="Extended"
ScrollViewer.HorizontalScrollBarVisibility="Hidden" HorizontalContentAlignment="Stretch"
HorizontalAlignment="Stretch" Loaded="ClassList_Loaded"
VerticalAlignment="Stretch" Grid.Row="0">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Border BorderBrush="Black" CornerRadius="3" Background="#FFE88D34"
BorderThickness="1" HorizontalAlignment="Stretch" >
<Grid Background="Transparent" HorizontalAlignment="Stretch" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0" HorizontalAlignment="Stretch"
Margin="2"
FontSize="10"
Text="{Binding DisplayClassNm}"/>
</Grid>
</Border>
</DataTemplate>
</ListBox.ItemTemplate>
This worked a treat for me.
Myles
When creating Data Templates for ListBox, you should not incldue <ListBoxItem>. The contents of the DataTemplate will be placed inside of a generated container. You can control how that container is constructed using ItemContainerStyle.
The default control style for ListBoxItem is used to define the ItemContainerStyle by default. This style sets the ListBoxItem.HorizontalContentAlignment property to 'Left'. Notice how the ContentPresenter binds its HorizontalAlignment to this property.
You need to override the style of the ListBoxItem container that is being generated when you bind to your ListBox. This can be done by setting the ItemContainerStyle. Set the HorizontalContentAlignment property to be "Stretch".
Below is the default ListBoxItem Style. Included for reference.
<Style x:Key="ListBoxItemStyle1" TargetType="ListBoxItem">
<Setter Property="Padding" Value="3"/>
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="VerticalContentAlignment" Value="Top"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="TabNavigation" Value="Local"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid Background="{TemplateBinding Background}">
<!-- VSM excluded for readability -->
<Rectangle x:Name="fillColor" Fill="#FFBADDE9" RadiusX="1" RadiusY="1" IsHitTestVisible="False" Opacity="0"/>
<Rectangle x:Name="fillColor2" Fill="#FFBADDE9" RadiusX="1" RadiusY="1" IsHitTestVisible="False" Opacity="0"/>
<ContentPresenter x:Name="contentPresenter" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}"/>
<Rectangle x:Name="FocusVisualElement" Stroke="#FF6DBDD1" StrokeThickness="1" RadiusX="1" RadiusY="1" Visibility="Collapsed"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Here is an example of using the control templates and data templates for the listbox control. Refer to the XAML markup which streaches the border for the listbox items.
My ListBoxItem contained a CheckBox and the above solutions did not work for me (most likely due to the nature of a CheckBox, not those solutions) I was able to coerce this functionality by not binding to the "Content" property of the checkbox, but explicitely defining the XAML inline:
<CheckBox HorizontalAlignment="Stretch" IsChecked="{Binding Path=IsSelected, Mode=TwoWay}">
<TextBlock
MinWidth="130"
Margin="0,-2,0,0"
HorizontalAlignment="Stretch"
Text="{Binding Path=DisplayText}" />
</CheckBox>
The margin is needed because the TextBox text did not align with the CheckBox's checkmark. The MinWidth was also necessary.
I am working with Silverlight 5 with VS 2012. I have the same issue. I have horizontal listbox with my own custom objects as ItemsSource. I want listbox items to expand. But they are not expanding. I donot want to give any hard coded width for every listbox item. I tried all answers here but nothing works. I just gave ItemsSource ={Binding Persons} DisplayMemberPath="First Name". Thats all
Two things I noticed here, because I had the same issue and wasn't able to solve it the way you're trying.
First, you don't need to explicitly put a ListBoxItem in your DataTemplate. This is created for you automatically, so you actually have your ListBoxItem inside of the one that was created for you. I checked this out in Snoop to confirm.
Second, and I don't know exactly why, but I wasn't able to get the stretching behavior out of the alignment attributes either. I changed it to use RelativeSource binding on the Width attribute to the ActualWidth property of the containing ListBoxItem. This worked for me.
Width="{Binding RelativeSource={RelativeSource
AncestorType={x:Type ListBoxItem}}, Path=ActualWidth}"
If you need to set style properties on the ListBoxItem that is implicitly created for you, use a Style element inside of the ListBox.ItemContainerStyle element.
Hope this helps...

Resources