WPF ListboxItems with ItemContainerStyle become unselectable - wpf

I coded the following ListBoxItemStyle to be able to place multiple elements into the listboxitem:
<Style x:Key="lbWithButton" TargetType="ListBoxItem">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{TemplateBinding Content}" Grid.Column="0"/>
<xctk:IntegerUpDown Minimum="0" Value="0" Maximum="1000" Grid.Column="1"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Unfortunately i cant select ListboxItems anymore.
This also happens with only the textblock inside the listboxitem.
Help would be appreciated!

You must use ItemTemplate instead of ItemContainerStyle:
<ListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="3*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Content}" Grid.Column="0"/>
<xctk:IntegerUpDown Minimum="0" Value="0" Maximum="1000" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Related

WPF Grid.IsSharedSizeScope in ItemsControl and ScrollViewer

I'm simplyfying this log viewer. It works mostly as expected, but somehow the column lengths don't share the same size.
Here is my code:
<Window.Resources>
<local:IsGreaterThanConverter x:Key="IsGreaterThanConverter" />
<sys:Int32 x:Key="MaxDisplayLineLength">200</sys:Int32>
<Style TargetType="ItemsControl" x:Key="LogViewerStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<ScrollViewer CanContentScroll="True">
<ItemsPresenter/>
</ScrollViewer>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True"/>
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate DataType="{x:Type logging:LogEntry}">
<Grid IsSharedSizeScope="True">
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Date" Width="Auto"/>
<ColumnDefinition SharedSizeGroup="Index" Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<TextBlock Name="Date" Text="{Binding DateTime, StringFormat={}{0:yyyy.MM.dd HH:mm:ss}}" Grid.Column="0" FontWeight="Bold" Margin="5,0,5,0"/>
<TextBlock Name="Index" Text="{Binding Index, StringFormat=({0})}" Grid.Column="1" FontWeight="Bold" Margin="0,0,2,0" TextAlignment="Left" />
<TextBlock Name="Line" Text="{Binding Line}" Grid.Column="2" TextWrapping="NoWrap" Margin="5,0,5,0"/>
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Source}" Value="LUA">
<Setter Property="Grid.Background" Value="White"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=Source}" Value="PYTHON">
<Setter Property="Grid.Background" Value="LightGray"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
</Grid>
<!--
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=Line.Length, Converter={StaticResource IsGreaterThanConverter}, ConverterParameter={StaticResource MaxDisplayLineLength}}" Value="True">
<Setter TargetName="Line" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
-->
</DataTemplate>
</Window.Resources>
<Grid>
<!-- https://stackoverflow.com/a/16745054/9963147 -->
<ItemsControl ItemsSource="{Binding LuaLog.Data, Mode=OneWay}" Style="{StaticResource LogViewerStyle}" Grid.IsSharedSizeScope="True">
<ItemsControl.Template>
<ControlTemplate>
<ScrollViewer CanContentScroll="True" HorizontalScrollBarVisibility="Visible" utils:AutoScrollBehavior.AutoScroll="True">
<ItemsPresenter/>
</ScrollViewer>
</ControlTemplate>
</ItemsControl.Template>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
I've not found any relevant posts to this problem, probably because I don't know which controls are relevant for this problem.
I've tried adding Grid.IsSharedSizeScope to the following places:
topmost Grid
ItemsControl
ScrollViewer
ItemsPresenter
without any result:
Where do I have to add Grid.IsSharedSizeScope to get this working? Are there any rules of thumb in similar situations?
Edit: Andy-s solutions works.
Some warning to the future generations: it slows down the execution substantially, even with only 100 lines. Probably because of the space calculation. I'm going to use fixed column width.
Solution looks like this (don't forget to remove Grid.IsSharedSizeScope from ItemTemplate Grid)
<ItemsControl ItemsSource="{Binding LuaLog.Data, Mode=OneWay}" Style="{StaticResource LogViewerStyle}" Grid.IsSharedSizeScope="True">
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type logging:LogEntry}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="Date" Width="Auto"/>
<ColumnDefinition SharedSizeGroup="Index" Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
...
I think your problem is defining the datatemplate outside the itemscontrol.
As I understand it, the scope applies to everything inside the control. Your datatemplate is not and hence maybe it's simply "outside" that scope.
I always use itemtemplate or itemscontrol.resources when I used sharedsizescope.
I would also make your last column width * so it takes up any space that's over.
In any event, this is some live markup I have that works:
<ItemsControl ItemsSource="{Binding}" Grid.IsSharedSizeScope="True"
IsTabStop="False"
>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="40"/>
<ColumnDefinition Width="Auto" SharedSizeGroup="sharedWidth"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>

VisualTree property set more than once error

Why does this code throw the "VisualTree set more than once" error on the textblock with the run for Employee number, name, and truck number? It is in within the data template. I tried wrapping that portion in a grid and it didn't work.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*"/>
<ColumnDefinition Width=".5*"/>
</Grid.ColumnDefinitions>
<ListView Grid.ColumnSpan="2" Grid.Row="0" BorderBrush="Transparent" Background="Transparent" Name="lvDriverQueueWork" ItemsSource="{Binding ICollectionQueueList, UpdateSourceTrigger=PropertyChanged}" Margin="10,0,10,0" Foreground="Black" AllowDrop="True">
<ListView.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock FontSize="14" FontWeight="SemiBold" Background="Transparent">
<Run Text="{Binding Path=Items[0].EmployeeNumber}"/>
<Run Text="-"/>
<Run Text="{Binding Name, Mode=OneWay}"/>
<Run Text="-"/>
<Run Text="{Binding Path=Items[0].TruckNumber}"/>
</TextBlock>
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListView.GroupStyle>
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Style.Resources>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="#F7F6E4"/>
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="black"/>
</Style.Resources>
<Setter Property="Margin" Value="0,0,0,-1"/>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="BorderBrush" Value="Transparent" />
</Trigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
<ListView.ItemTemplate>
<ItemContainerTemplate>
<Border BorderThickness="0,1,0,1" Padding="0,2,0,2" BorderBrush="#DCE1E5" Width="250">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Padding="18,0,0,0" Text="{Binding Trailer, UpdateSourceTrigger=PropertyChanged}"/>
<TextBlock Grid.Column="1" Text="{Binding Carrier, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</Border>
</ItemContainerTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>

Shadow under hovered ListBoxItem (and not under it's text)

I need shadow under ListBoxItem on MouseOver. Bottom code works but the whole listbox including the TextBlock's letters have a shadow:
<ListBox ItemContainerStyle="{StaticResource Style1}"
And the item Style:
<Style x:Key="Style1" TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True" >
<Setter Property = "Effect" >
<Setter.Value>
<DropShadowEffect ShadowDepth="10" Direction="0" Opacity="1" BlurRadius="5" Color="Black"/>
</Setter.Value>
</Setter>
</Trigger>
Simplified DataTemplate:
<DataTemplate x:Key="TemplateSimple" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Title}" Grid.Column="0"/>
<TextBlock Text="{Binding FirstName}" Grid.Column="1"/>
<TextBlock Text="{Binding LastName}" Grid.Column="2"/>
Example is simplified.
I also tried adding to the DataTemplate:
<Rectangle Grid.Column="0" Fill="GreenYellow" Grid.ColumnSpan="3">
and assigning the shadow to it, but it would react only if TextBlocks are empty. Other ideas are appreciated.
EDIT:
As you can see it is not really a shadow but a blurry text. If it was a shadow, it would change much on changing shadow length:
See this post, How do I apply an effect to a Border but not to its contents in WPF?, which has some documentation on this "feature".
The easiest workaround in your case might be to give the Grid in your DataTemplate a background color:
<DataTemplate x:Key="TemplateSimple" >
<Grid Background="White" > ...
EDIT:
A more thorough approach would be to apply the DropShadowEffect to an element that lies beneath the text, but doesn't contain the text. For example, add a rectangle to your DataTemplate:
<DataTemplate x:Key="TemplateSimple" >
<Grid Margin="2" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Rectangle Style="{StaticResource RectStyle1}"
Fill="Lime" Grid.ColumnSpan="3" />
<TextBlock Text="{Binding Title}" Grid.Column="0" />
<TextBlock Text="{Binding FirstName}" Grid.Column="1" />
<TextBlock Text="{Binding LastName}" Grid.Column="2" />
</Grid>
</DataTemplate>
..and instead of having the DropShadowEffect in Style1, put it in RectStyle1, but still triggered by IsMouseOver on the parent ListBoxItem:
<Style x:Key="RectStyle1" TargetType="Rectangle" >
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType=ListBoxItem},
Path=IsMouseOver,
Mode=OneWay}"
Value="True" >
<Setter Property="Effect" >
<Setter.Value>
<DropShadowEffect ShadowDepth="10" Direction="0"
Opacity="1" BlurRadius="5"
Color="Black" />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>

Passing BitmapImage as Tag to DataTemplate

I'm a bit unexperienced in WPF and trying to get a simple Template in WPF for a button with an Image icon in addition to text to work.
Style code:
<Style x:Key="DatabaseButtonWithImageTag" TargetType="Button" BasedOn="{StaticResource DatabaseButton}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Width="100" Source="{TemplateBinding Tag}" />
<ContentControl Grid.Column="1" Content="{TemplateBinding Content}" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
View code:
<Image Source="{StaticResource image_new_patient}"/>
<Button Style="{StaticResource DatabaseButtonWithImageTag}" Tag="{StaticResource image_new_patient}" Width="150" Height="100">
Test!
</Button>
The image outside the button is displayed, so is "Test!", but there is no image inside the button.
What am I doing wrong?
TemplateBinding does not work within a DataTemplate.
You can use this workaroud:
<Window.Resources>
<BitmapImage x:Key="image_new_patient" UriSource="bsp.jpg" />
<local:ImageConverter x:Key="imageConverter" />
<Style x:Key="DatabaseButtonWithImageTag" TargetType="Button">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<Image Grid.Column="0" Width="100" Source="{Binding RelativeSource={RelativeSource AncestorType=Button}, Path=Tag}" />
<ContentControl Grid.Column="1" Content="{TemplateBinding Content}" />
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel>
<Image Source="{StaticResource image_new_patient}" Width="50"/>
<Button Style="{StaticResource DatabaseButtonWithImageTag}" Tag="{StaticResource image_new_patient}" Width="150" Height="100">
Test!
</Button>
</StackPanel>

ListBox content does not resize when window is made smaller

I'm using .NET 4.0 (not .NET 4.0 CP) and have run into this kinda unique issue. I created a ListBox to display bound elements, first off here is (a part) of my XAML.
<Grid Grid.Row="2" Background="#EEEEEE">
<Border Margin="6,10,10,10" BorderBrush="#666666" BorderThickness="1">
<ListBox ItemsSource="{Binding}" Name="appList" BorderThickness="0" HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="80" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Margin="5" BorderThickness="3" CornerRadius="2" BorderBrush="Black" HorizontalAlignment="Left" VerticalAlignment="Top" x:Name="ItemBorder">
<Image Width="64" Height="64" Source="{Binding Path=IconUri}" Stretch="UniformToFill" />
</Border>
<StackPanel Margin="0,5,5,5" Grid.Column="1" Orientation="Vertical" HorizontalAlignment="Stretch">
<TextBlock FontSize="18" Text="{Binding Path=DisplayName}" />
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="60"/>
</Grid.ColumnDefinitions>
<ProgressBar Grid.Column="0" Height="24" HorizontalAlignment="Stretch" IsIndeterminate="{Binding Path=IsDiscovering}" Value="{Binding Path=PercentageDownloaded}" />
<TextBlock Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center"><TextBlock x:Name="percentageDownloaded" /><TextBlock x:Name="percentageMeter">%</TextBlock></TextBlock>
</Grid>
</StackPanel>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=IsDiscovering}">
<DataTrigger.Value>True</DataTrigger.Value>
<Setter TargetName="percentageDownloaded" Property="Text" Value="N/A" />
<Setter TargetName="percentageMeter" Property="Visibility" Value="Collapsed" />
</DataTrigger>
<DataTrigger Binding="{Binding Path=IsDiscovering}">
<DataTrigger.Value>False</DataTrigger.Value>
<Setter TargetName="percentageDownloaded" Property="Text" Value="{Binding Path=PercentageDownloaded}" />
<Setter TargetName="percentageMeter" Property="Visibility" Value="Visible" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
</Border>
</Grid>
Sizing the window up stretches the ListBox content just fine, but when I size it down, it retains it's width and spawns vertical scrollbars.
Have you already tried to use a StackPanel as ItemsPanel?
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>

Resources