Coordinates of specific ListViewItem
This is going to drive me mad. I have a WPF ListView and I try to overlay a TextBox over a double-clicked cell to emulate an Editor. This is working fine so far. I call ListView.InputHitTest(e.Position) to get the clicked item. From there I use VisualTreeHelper.GetParent() to navigate through the VisualTree and to resolve the coordinates. Fine.
Now I want, that if the user presses the cursor down, that this TextBox moves to the next Item. I tried the following attempts:ListView.ItemContainerGenerator.ContainerFromIndex(index + 1)
ListView.ItemContainerGenerator.ContainerFromItem(…)
But both methods return null (Nothing in VB), but ListView.ItemContainerGenerator.Status returns ContainersGenerated. How can I get the coordinates of a specific ListViewItem?
By the way: I’m limited to Framework 3.5, therefore the Grid in 4.0 is not available. Additionally I do not want to use 3rd party tools.
Edit
Here is some (experimental) code I use:I use a surrounding Canvas to easily position the TextBox over the ListView. I included the DataTemplate of the first column. The rest is quite similar, so I removed this code. In addition I remove some style definitions and converters - I don't think that they are of any interest here.
<Canvas x:Name="Canvas">
<ListView ItemsSource="{Binding MyListViewList}"
Name="ListView"
MouseDoubleClick="ListView_MouseDoubleClick">
<ListView.Resources>
<DataTemplate x:Key="NameTemplate">
<Border Name="NameBorder">
<!-- Some content for this cell here -->
</Border>
</DataTemplate>
</ListView.Resources>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"></Setter>
<Setter Property="VerticalContentAlignment" Value="Stretch"></Setter>
<Setter Property="IsSelected" Value="{Binding IsSelected}"></Setter>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridView.Columns>
<GridViewColumn Header="Name" x:Name="colName"
CellTemplate="{StaticResource NameTemplate}"></GridViewColumn>
</GridView.Columns>
</GridView>
</ListView.View>
</ListView>
<TextBox Name="EditorTextBox"
Visibility="Hidden"
BorderThickness="0">
</TextBox>
</Canvas>
I register PreviewKeyDown to catch the keypress on Cursor-Down:
Private Sub TextBox_PreviewKeyDown(ByVal sender As Object, ByVal e As KeyEventArgs)
Dim model As MyListViewModel
model = CType(DataContext, MyListViewModel)
Select Case e.Key
Case Key.Down
Dim lvp As MyListViewParameter
If Me.editorSelectedItemIndex + 1 < model.MyListViewList.Count Then
lvp = model.MyListViewList(Me.editorSelectedItemIndex + 1)
' How to position EditorTextBox to the next ListViewItem here?
' The following methods return null/Nothing:
' ListView.ItemContainerGenerator.ContainerFromIndex(editorSelectedItemIndex + 1)
' ListView.ItemContainerGenerator.ContainerFromItem(lvp)
End If
e.Handled = True
Case Key.Up
End Select
End Sub
So I have the position in code (PreviewKeyDown-Event) when I need to re-arrange the TextBox, but inside this routine, I can't get a result of the methods
ListView.ItemContainerGenerator.ContainerFromIndex(index + 1) and ListView.ItemContainerGenerator.ContainerFromItem(开发者_运维问答…)Further tests:
I wrote a class that inherits ListView and caches the ListViewItems when they are created in simple list of ListViewItemPublic Class ExtListView
Inherits ListView
Public Sub New()
Dim ic = TryCast(Me.Items, System.Collections.Specialized.INotifyCollectionChanged)
If Not ic Is Nothing Then
AddHandler ic.CollectionChanged, AddressOf NotifyCollectionChangedEventHandler
End If
AddHandler Me.ItemContainerGenerator.StatusChanged, AddressOf ItemContainerGenerator_ItemsChanged
_ListViewItems = New List(Of ListViewItem)
End Sub
Private _listViewItems As List(Of ListViewItem)
Public ReadOnly Property ListViewItems As List(Of ListViewItem)
Get
Return _listViewItems
End Get
End Property
Private Sub ItemContainerGenerator_ItemsChanged(ByVal sender As Object, ByVal e As EventArgs)
If Me.ItemContainerGenerator.Status = Primitives.GeneratorStatus.ContainersGenerated Then
_listViewItems.Clear()
For index = 0 To Me.Items.Count - 1
_listViewItems.Add(CType(ItemContainerGenerator.ContainerFromIndex(index), ListViewItem))
Next
End If
End Sub
End Class
With this ListViewItems available, I tried to get the bounds of them:
Dim bounds As Rect = VisualTreeHelper.GetDescendantBounds(ListView.ListViewItems(editorSelectedItemIndex + 1))
But, however, all rectangles start at x,y-position (0, 0). I have no clue why. I expected them to be in the visual tree at a valid position. Any suggestions to solve this?
Don't do that.
Okay, if you really want to, do that. But there's a much, much easier way.
Add a boolean IsEditing
and a string EditableContent
property to your view model. Create a data template that looks something like this:
<DataTemplate>
<Grid>
<Border>
<!-- normal cell content goes here -->
</Border>
<TextBox Text="{Binding EditableContent, Mode=TwoWay}">
<TextBox.Style>
<Style TargetType="TextBox">
<Setter Property="Visibility" Value="Hidden"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsEditing}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBox>
<Grid>
</DataTemplate>
When the IsEditing
property is true, the TextBox
will be displayed and the user can edit whatever's in the EditableContent
property. When it's false, the ordinary cell content will be displayed.
I don't know what you're sticking inside that border, but when I do this to alternate between a TextBlock
and TextBox
it looks identical to (say) what renaming files looks like in Windows Explorer, which I assume is what you're after.
You need to handle keypress and lost-focus events to implement the other pieces of edit-in-place functionality, but those are relatively straightforward problems once you've got the layout working.
精彩评论