I have a listView that contains a textbox for each binding item and I want to allow overlaying text box content if its content is wider than the textbox width similar to excel cells overlaying behavior. Is there a way to do this?
<ListView ItemsSource="{Binding Path=MyStringCollection}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Width="100"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding}" TextWrapping="WrapWithOverflow"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
CodePudding user response:
You can use tooltip for that. You just need to bind the source with itself.
<ListView ItemsSource="{Binding Path=MyStringCollection}">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" Width="100"/>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding}" TextWrapping="WrapWithOverflow" ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=Text}"/>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
For further information:
CodePudding user response:
To replicate a similar behavior, you can make use of a Popup
.
To implement a similar behavior, you first must disable content wrapping of your TextBox
.
Then replace the current TextBox
with a TextBlock
, which is used to display the text.
The Popup
, which actually contains the editable TextBox
, will then overlay this TextBlock
at the exact position, thus hiding the TextBlock
to make it appear to stretch and overlay the adjacent items.
A MultiTrigger
will close the Popup as soon as the focus moved outside the ListBoxItem
.
To improve performance you should use the VirtualizingStackPanel
as items host.
<ListView ItemsSource="{Binding MyStringCollection}"
HorizontalAlignment="Left"
Width="800">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Border BorderThickness="1"
BorderBrush="Gray">
<TextBlock Text="{Binding}" />
</Border>
<Popup x:Name="EditableTextSiteHost"
PlacementTarget="{Binding ElementName=TextSite}"
Placement="Relative"
Height="{Binding ElementName=TextSite, Path=ActualHeight}"
AllowsTransparency="True"
FocusManager.FocusedElement="{Binding ElementName=EditableTextSite}">
<TextBox x:Name="EditableTextSite"
Text="{Binding TextData}" />
</Popup>
</Grid>
<DataTemplate.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}, Path=IsSelected}"
Value="True" />
<Condition Binding="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}, Path=IsKeyboardFocusWithin}"
Value="True" />
</MultiDataTrigger.Conditions>
<Setter TargetName="EditableTextSiteHost"
Property="IsOpen"
Value="True" />
</MultiDataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<!-- Make items overlap -->
<Setter Property="Margin"
Value="-2,0,0,0" />
<Setter Property="Padding"
Value="0" />
<Setter Property="Width"
Value="50" />
<Style.Triggers>
<!-- Apply zero Margin on the first item -->
<DataTrigger Binding="{Binding RelativeSource={RelativeSource PreviousData}}"
Value="{x:Null}">
<Setter Property="Margin"
Value="0,0,0,0" />
</DataTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>
</ListView>
To-do
This is just a raw example, a proof of concept. You would have to improve the behavior. For example, you would want to close the Popup
when the user scrolls or moves the Window
. Otherwise, since Popup
itself is a Window
, the Popup
would not move to follow the placement target. You could move the related logic to an attached behavior.
You likely also want to improve the selection behavior. Currently the selection highlight border does not (virtually) extend to surround the Popup. You have to mimic this by applying a Border on the TextBox
that will replicate the ListBoxItem
highlight border.