【问题标题】:Coordinates of specific ListViewItem特定 ListViewItem 的坐标
【发布时间】:2011-11-01 15:47:26
【问题描述】:

这会让我发疯。我有一个 WPF ListView,我尝试在双击的单元格上覆盖一个 TextBox 以模拟一个编辑器。到目前为止,这工作正常。我调用 ListView.InputHitTest(e.Position) 来获取点击的项目。从那里我使用 VisualTreeHelper.GetParent() 在 VisualTree 中导航并解析坐标。很好。
现在我想要,如果用户按下光标,这个 TextBox 会移动到下一个项目。我尝试了以下尝试:

ListView.ItemContainerGenerator.ContainerFromIndex(index + 1)
ListView.ItemContainerGenerator.ContainerFromItem(…)  

但是这两种方法都返回 null(在 VB 中为空),但 ListView.ItemContainerGenerator.Status 返回 ContainersGenerated。 如何获取特定 ListViewItem 的坐标?

顺便说一句:我仅限于 Framework 3.5,因此 4.0 中的 Grid 不可用。此外,我不想使用 3rd 方工具。

编辑
这是我使用的一些(实验性)代码:

我使用周围的 Canvas 轻松地将 TextBox 放置在 ListView 上。我包括了第一列的 DataTemplate。其余部分非常相似,因此我删除了此代码。此外,我删除了一些样式定义和转换器 - 我认为它们在这里没有任何意义。

<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>

我注册 PreviewKeyDown 以捕捉 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

所以当我需要重新排列 TextBox 时,我在代码中的位置(PreviewKeyDown-Event),但是在这个例程中,我无法获得方法的结果
ListView.ItemContainerGenerator.ContainerFromIndex(index + 1) 和
ListView.ItemContainerGenerator.ContainerFromItem(…)

进一步测试:
我写了一个类,它继承 ListView 并在 ListViewItem 的简单列表中创建时缓存 ListViewItems

Public 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

有了这个 ListViewItems 可用,我试图获得它们的界限:

Dim bounds As Rect = VisualTreeHelper.GetDescendantBounds(ListView.ListViewItems(editorSelectedItemIndex + 1))

但是,所有矩形都从 x,y 位置 (0, 0) 开始。我不知道为什么。我希望它们位于可视树中的有效位置。 有什么解决这个问题的建议吗?

【问题讨论】:

  • 什么是 ItemsSource? ItemTemplate 怎么样?你能发布一些代码(除非它太长)?
  • @MarioVernari:我希望这是一个可管理的来源。我试图发布重要的行。

标签: wpf listview gridview .net-3.5 listviewitem


【解决方案1】:

不要那样做。

好的,如果你真的想这样做,那就去做吧。但是有一个更简单的方法。

将布尔值IsEditing 和字符串EditableContent 属性添加到您的视图模型。创建一个如下所示的数据模板:

<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>

IsEditing 属性为真时,TextBox 将被显示并且用户可以编辑EditableContent 属性中的任何内容。为false时,显示普通单元格内容。

我不知道您在该边框内粘贴了什么,但是当我这样做以在 TextBlockTextBox 之间交替时,它看起来与(比如说)在 Windows 资源管理器中重命名文件的样子相同,其中我想这就是你所追求的。

您需要处理按键和失去焦点事件来实现其他的就地编辑功能,但是一旦布局正常工作,这些都是相对简单的问题。

【讨论】:

  • 好吧,你的尝试和我的完全不同。我认为您的方式通过设计解决了许多问题。我今天要试试++。非常感谢这个有价值的提示。
  • 我花了一段时间才想到这个想法,但有一些重要的例外(我正在看着你,拖放),如果你发现自己试图在代码中操作 WPF 对象您可能需要重新考虑您的方法。
  • 它工作得很好,它大大减少了代码隐藏文件中的代码
猜你喜欢
  • 2013-01-07
  • 1970-01-01
  • 1970-01-01
  • 2017-05-11
  • 1970-01-01
  • 2014-07-16
  • 2014-12-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多