Координаты конкретного ListViewItem - PullRequest
0 голосов
/ 23 августа 2011

Это сведет меня с ума. У меня есть WPF ListView, и я пытаюсь наложить TextBox на ячейку с двойным щелчком для эмуляции редактора. Пока все работает нормально. Я вызываю ListView.InputHitTest (e.Position), чтобы получить нажатый элемент. Оттуда я использую VisualTreeHelper.GetParent (), чтобы перемещаться по VisualTree и разрешать координаты. Хорошо.
Теперь я хочу, чтобы, если пользователь нажимал курсор вниз, этот TextBox перемещался к следующему элементу. Я попробовал следующие попытки:

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

Но оба метода возвращают ноль (Ничего в VB), но ListView.ItemContainerGenerator.Status возвращает ContainersGenerated. Как я могу получить координаты определенного ListViewItem?

Кстати: я ограничен Framework 3.5, поэтому Grid в 4.0 недоступен. Кроме того, я не хочу использовать сторонние инструменты.

Редактировать
Вот некоторый (экспериментальный) код, который я использую:

Я использую окружающий 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

Итак, у меня есть позиция в коде (PreviewKeyDown-Event), когда мне нужно переставить TextBox, но внутри этой процедуры я не могу получить результат от методов
ListView.ItemContainerGenerator.ContainerFromIndex (index + 1) и
ListView.ItemContainerGenerator.ContainerFromItem (...)

Дальнейшие испытания:
Я написал класс, который наследует ListView и кэширует ListViewItems, когда они создаются в простом списке ListViewItem

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). Понятия не имею почему. Я ожидал, что они будут в визуальном дереве в правильной позиции. Любые предложения, чтобы решить эту проблему?

1 Ответ

2 голосов
/ 24 августа 2011

Не делай этого.

Хорошо, если ты действительно хочешь, сделай это.Но есть намного, намного более простой способ.

Добавьте логическое 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 имеет значение true, будет отображаться TextBox, и пользователь сможет редактировать все, что есть в свойстве EditableContent.Когда оно ложно, будет отображаться обычное содержимое ячейки.

Я не знаю, что вы придерживаетесь внутри этой границы, но когда я делаю это для чередования между TextBlock и TextBox, это выглядитидентично (скажем) тому, как выглядит переименование файлов в проводнике Windows, что, как я полагаю, является тем, что вам нужно.

Вам необходимо обрабатывать события нажатия клавиш и потери фокуса для реализации других частей редактирования вфункциональность размещения, но это относительно простые проблемы, если у вас работает макет.

...