ListBox internal layout - wpf

My WPF application has a ListBox whose ItemTemplate looks like this:
<DataTemplate x:Key="DomainTemplate" DataType="DomainViewModel">
<Border BorderBrush="{Binding Converter={StaticResource BrushConverter}, Path=IsSelected}"
BorderThickness="3"
Grid.Column="0"
Name="SelectedBorder"
Padding="5">
<Button Click="SelectDomain_Click"
FontSize="16"
FontWeight="Bold"
IsEnabled="{Binding Path=CurrentSiteIsValid, RelativeSource={RelativeSource AncestorType={x:Type c:DomainPicker}}}"
MinHeight="60"
Tag="{Binding Path=DomainId}"
Width="120">
<TextBlock Text="{Binding Path=Name}"
TextAlignment="Center"
TextWrapping="WrapWithOverflow" />
</Button>
</Border>
</DataTemplate>
The window's width is driven by the width of the ListBox. This is by design. There seems to a very large space between the vertical edges of the ListBox and the items in it. Using Snoop, I see that the ListBoxItem contains a Border that is the same width as the ListBox, has a Margin of 0, and Padding set to 2,0,0,0.
The Border contains a ContentPresenter whose width is 29 units smaller than the Border that contains it. The Padding on the Border would seem to account for 2 of the units. Its Margin is 0 and it has no padding property.
I actually would like to make this window a bit narrower if I can without making the Buttons in the template any narrower. Where is that 29 unit space coming from? Is there any way to change its size?

I've ran into this problem too in which I used a WrapPanel as the ListBox's ItemPanel, and i ended up with a gap between items in certain cases due to the problem you mentioned. Here is the fix:
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Padding" Value="0"/>
</Style>
</ListBox.ItemContainerStyle>

Related

Why in my WPF view is the Content text of my Label control not visible?

I have the following markup:
<StackPanel Grid.Row="0" Orientation="Horizontal">
<StackPanel Orientation="Horizontal" Visibility="{Binding OrgListVisibility}">
<Label Content="Org:" />
<ComboBox ItemsSource="{Binding OrgSelectList, NotifyOnSourceUpdated=True}" SelectedValuePath="Key" DisplayMemberPath="Value" SelectedItem="{Binding OrgId}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Visibility="{Binding BranchListVisibility}">
<TextBlock Text="Branch:" Style="{StaticResource FormLabel}" />
<ComboBox x:Name="BranchList" ItemsSource="{Binding BranchSelectList}" SelectedValuePath="Key" DisplayMemberPath="Value" SelectedItem="{Binding BranchId}" />
</StackPanel>
</StackPanel>
Yet when I run the app, only the text from the TextBlock is visible, and not that of the Label. The latter is in the Visual Tree, with a TextBlock deep down, but that is as far as I can see.
AS REQUESTED: Here is the style for FormLabel:
<Style TargetType="TextBlock" x:Key="FormLabel">
<Setter Property="Height" Value="20" />
<Setter Property="Margin" Value="10" />
<Setter Property="TextAlignment" Value="Right" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
A SIMILAR PROBLEM:
I found an almost similar problem with a combobox when I bound it to a collection of instances of a generic class. The items' text simply did not show, but they were present in the comboboxes. Selecting on the one by knowing the position of my sought item correctly cascaded to the 2nd combobox, which had visible items, and I could see the correct but invisible item had been selected.
As soon as I change the item source to a list of non-generic objects, the items in the dropdown were visible again.
The code looks fine and as you have mentioned in the comments section that it takes layout space then it may very well happen that the color of your label and the background color of the containing layout be same.
To troubleshoot this, try giving some different background and foreground colors e.g. red or blue to the Label. Hope this helps
Ctrl+Q -> Live Visual Tree
Then hit the "pick element" button and select your label. Check the following properties:
Visibility
Opacity
Content
Also check the child elements of the Label. Setting the Content should result in a tree like this:
If a default style has changed the control template, you might not see the TextBlock as a child here. Also drill into the TextBlock and make sure it has the right Text property, then make sure it and all its parents have the right Opacity and Visibility . Also make sure that the inner TextBlock has space allocated to it by selecting it and turning on the highlighting feature in the live visual tree window.
Can you try this code to see if it works?
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<StackPanel Grid.Column="0"
Orientation="Horizontal" Visibility="{Binding OrgListVisibility}">
<Label Content="Org:" />
<ComboBox ItemsSource="{Binding OrgSelectList, NotifyOnSourceUpdated=True}"
SelectedValuePath="Key"
DisplayMemberPath="Value"
SelectedItem="{Binding OrgId}" />
</StackPanel>
<StackPanel Grid.Column="1"
Orientation="Horizontal" Visibility="{Binding BranchListVisibility}">
<TextBlock Text="Branch:" Style="{StaticResource FormLabel}" />
<ComboBox x:Name="BranchList"
ItemsSource="{Binding BranchSelectList}"
SelectedValuePath="Key"
DisplayMemberPath="Value"
SelectedItem="{Binding BranchId}" />
</StackPanel>
</Grid>
The Label would take up layout space while not being visible when its Visibility == Hidden. You should check and make sure that your application does not define a global style (one with no Key) for TargetType="Label" where this value could be set:
<Style TargetType="Label"> !!!note that this has no 'Key' associated
[...]
<Setter Property="Visibility" Value="Hidden" />
[...]
</Style>
This would not need to be in the same xaml file in order to be automatically applied, you should check the global dictionary or any other ResourceDictionary linked in the file.
I had the same problem. It turned out that the label Height was too small. Increased the height and its content became visible.

Zero-height of ScrollViewer-templated item inside ItemsControl?

I have an ItemsControl with arbitrary items. Some of the items are wrapped inside a ScrollViewer. The code-behind for these scrollable items makes use of the ViewportWidth (almost equivalent to ActualWidth) and ViewportHeight (almost equivalent to ActualHeight) properties to arrange/size its visual children. This works as long as I don't put the item inside an ItemsControl. When the item appears in an ItemsControl the value of ViewportHeight equals 0 - effectively making my item invisible. Note that I want to arrange the items vertically, giving all items equal height! No fancy stuff, just a regular StackPanel.
The templates are applied automatically using DataType:
<MyControl.Resources>
<DataTemplate DataType="{x:Type MyScrollableItem}">
<MyControlWrappedInScrollViewer Text="{Binding Text}" />
</DataTemplate>
<DataTemplate DataType="{x:Type MyItem}">
<TextBlock Text="{Binding Text}"/>
</DataTemplate>
</MyControl.Resources>
<ItemsControl ItemsSource="{Binding MyCollection}" />
The structure of MyControlWrappedInScrollViewer looks something like this:
<UserControl>
<Grid>
<ScrollViewer CanContentScroll="True">
<Canvas />
</ScrollViewer>
</Grid>
</UserControl>
Why does my ScrollViewer get the height of 0? How can I tell my ItemsControl to size the item appropriately? E.g. One item yields a height of the ItemsControl height. Two items yield half of it, and so on.
This did the trick:
<Style TargetType="{x:Type ItemsControl}">
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<UniformGrid Columns="1" IsItemsHost="True"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>

Panel.ZIndex property in a treeviewitem in wpf

I Wish the treeviewitem to overlap the other items when i mouse hover it.
To do this i made the parent element (in my case its Border) within the HierarchicalDataTemplate to have the ZIndex as 0 and changed this value to 1 when the user hovers the mouse in the HierarchicalDataTemplate.Triggers section
<HierarchicalDataTemplate DataType="{x:Type d:MyClass}">
<Border Name="brd" CornerRadius="5" BorderThickness="1" Padding="3" Margin="0,0,60,0" Panel.ZIndex="0" >
<StackPanel Orientation="Horizontal" Margin="0,0,0,0" >
<Image Source="../Images/icon.jpg" Height="30"></Image>
<TextBlock TextAlignment="Center" Text="{Binding Text}"
Margin="3,0,10,0" >
</TextBlock>
<Image Margin="0,0,3,0"
Source="../Images/Img1.jpg" Height="30" />
<Image Margin="0,0,0,0"
Source="../Images/Img2" Height="30"/>
</StackPanel>
</Border>
<HierarchicalDataTemplate.Triggers>
<Trigger SourceName="brd" Property="IsMouseOver" Value="True">
<Setter TargetName="brd" Property="Panel.ZIndex" Value="1"></Setter>
</Trigger>
</HierarchicalDataTemplate.Triggers>
</HierarchicalDataTemplate>
The whole idea to implement this was:
Whenever the user hovers the mouse over a treeviewitem, the item should overlap the other controls and should be completely visible. Example: if the item is a long text, then the user should not be forced to use the scroll bar, rather if he just points it the item should be overlapping the other controls to display the complete item.
But i couldn't achieve this using the above triggers.
Please help me doing this.
Did you try using a ToolTip? I didn't try it myself but after seeing this I'm convinced that it's possible to define a DataTemplate which can be used at this property so the item is shown the way you like.
I hope this helps.
Regards

How to make the StackPanel or DockPanel stretch to fit their containers in WPF?

I have a Grid as a root container with two columns defined (There's only one row).
The first column has flexible width and second column has 300px fixed width.
Next, I have placed a ListBox inside the second column to stretch both horizontally and vertically, i.e. to fill the entire second column.
Lastly, I have defined an Items Template for the ListBox to be a vertically oriented StackPanel, with one DockPanel and a couple of TextBlocks inside.
<!-- Data template for ListBox -->
<DataTemplate DataType="{x:Type entities:Track}">
<StackPanel Orientation="Vertical">
<DockPanel>
<TextBlock DockPanel.Dock="Left" Text="Now playing" />
<TextBlock DockPanel.Dock="Right" Text="Time remaining" />
</DockPanel>
<TextBlock Text="{Binding Artist}" />
<TextBlock Text="{Binding Title}" />
</StackPanel>
</DataTemplate>
...
<ListBox
Grid.Column="1"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" />
I have two questions:
How do I make the StackPanel fill the entire width of the ListBox? Right now, it only takes enough space to accomodate the DockPanel and the TextBlocks inside. It won't fit the width of the ListBox or ListBoxItem.
Next, how do I make the DockPanel fill the entire width of the StackPanel, i.e. its parent? What happens right now is that even if the width of the TextBlocks in the StackPanel exceed the width of the DockPanel, it won't stretch to match their width.
Thanks for the help.
You can make the Content of every ListBoxItem stretch by setting HorizontalContentAlignment in the ItemContainerStyle
<ListBox ...>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>

Force TextBlock to wrap in WPF ListBox

I have a WPF listbox which displays messages. It contains an avatar on the left side and the username and message stacked vertically to the right of the avatar. The layout is fine until the message text should word wrap, but instead I get a horizontal scroll bar on the listbox.
I've Googled and found solutions to similar issues, but none of them worked.
<ListBox HorizontalContentAlignment="Stretch" ItemsSource="{Binding Path=FriendsTimeline}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Border BorderBrush="DarkBlue" BorderThickness="3" CornerRadius="2" Margin="3" >
<Image Height="32" Width="32" Source="{Binding Path=User.ProfileImageUrl}"/>
</Border>
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Path=User.UserName}"/>
<TextBlock Text="{Binding Path=Text}" TextWrapping="WrapWithOverflow"/> <!-- This is the textblock I'm having issues with. -->
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Contents of the TextBlock can be wrapped using property TextWrapping.
Instead of StackPanel, use DockPanel/Grid.
One more thing - set ScrollViewer.HorizontalScrollBarVisibility property to Disabled value for the ListBox.
Updated Hidden to Disabled based on comment from Matt. Thanks Matt.
The problem might not be located in the ListBox. The TextBlock won't wrap, if one of the parent controls provides enough space, so that it hasn't the need to wrap. This might be caused by a ScrollViewer control.
If you want to prevent TextBlock to grow, and you want it to just fit in the size of the listbox, you should set the width of it explicitly.
In order to change it dynamically, it means not a fix value, but you need to bind it to its proper parent element in the visual tree. You can have something like this:
<ListBox ItemsSource="{Binding MyItems}" Name="MyListBox">
<ListBox.Resources>
<Style TargetType="ListBoxItem">
<Setter Property="Width"
Value="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ScrollContentPresenter}, Path=ActualWidth}" />
</Style>
</ListBox.Resources>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" TextWrapping="Wrap" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
If it does not work, try to find the proper elements (which has to be binded to what) with the Live Visual Tree in Visual Studio.

Resources