Frage

WPF, Browserlike App.
Ich habe eine Seite ein Listview enthält. Nach einem Aufruf Pagefunction ich eine Linie auf das Listview hinzufügen und will die neue Linie in dem Blick blättern:

  ListViewItem item = ItemContainerGenerator.ContainerFromIndex(index) as ListViewItem;
  if (item != null)
    ScrollIntoView(item);

Das funktioniert. Solange die neue Linie im Hinblick auf die Linie wird der Fokus, wie es sollte.

Das Problem ist, die Dinge funktionieren nicht, wenn die Linie nicht sichtbar ist.
Wenn die Zeile nicht sichtbar ist, gibt es keine ListViewItem für die Linie erzeugt, so ItemContainerGenerator.ContainerFromIndex null zurück.

Aber ohne die Artikel, wie blättere ich die Linie in Sicht? Gibt es eine Möglichkeit zur letzten Zeile blättern (oder irgendwo), ohne dass ein ListViewItem zu benötigen?

War es hilfreich?

Lösung

Ich denke, das Problem hier ist, dass die ListViewItem noch nicht erstellt, wenn die Linie nicht sichtbar ist. WPF erstellt das Sichtbare auf Anfrage.

Also in diesem Fall, dass Sie wahrscheinlich null für das Einzelteil erhalten, tun Sie? (Nach Ihrem Kommentar, was Sie tun)

Ich habe eine Link auf MSDN-Foren, die die Scroll direkt vorschlagen, um den Zugriff zu blättern. Für mich stellte die Lösung sieht es sehr ähnlich wie ein Hack, aber Sie können selbst entscheiden.

Hier ist der Code-Schnipsel aus dem link oben :

VirtualizingStackPanel vsp =  
  (VirtualizingStackPanel)typeof(ItemsControl).InvokeMember("_itemsHost",
   BindingFlags.Instance | BindingFlags.GetField | BindingFlags.NonPublic, null, 
   _listView, null);

double scrollHeight = vsp.ScrollOwner.ScrollableHeight;

// itemIndex_ is index of the item which we want to show in the middle of the view
double offset = scrollHeight * itemIndex_ / _listView.Items.Count;

vsp.SetVerticalOffset(offset);

Andere Tipps

Jemand hat mir gesagt, einen noch besseren Weg zu einer bestimmten Zeile zu scrollen, die einfach und funktioniert wie Charme.
Kurz gesagt:

public void ScrollToLastItem()
{
  lv.SelectedItem = lv.Items.GetItemAt(rows.Count - 1);
  lv.ScrollIntoView(lv.SelectedItem);
  ListViewItem item = lv.ItemContainerGenerator.ContainerFromItem(lv.SelectedItem) as ListViewItem;
  item.Focus();
}

Die längere Version in MSDN Foren :

Ich habe einige Änderungen an Sams Antwort. Beachten Sie, dass ich in der letzten Zeile blättern wollte. Leider sometiems die Listview angezeigt nur die letzte Zeile (auch wenn es beispielsweise 100 Zeilen darüber war), so ist dies, wie ich das festgelegt:

    public void ScrollToLastItem()
    {
        if (_mainViewModel.DisplayedList.Count > 0)
        {
            var listView = myListView;
            listView.SelectedItem = listView.Items.GetItemAt(_mainViewModel.DisplayedList.Count - 1);
            listView.ScrollIntoView(listView.Items[0]);
            listView.ScrollIntoView(listView.SelectedItem);
            //item.Focus();
        }
    }

Prost

Eine Abhilfe hierfür ist die Itemspanel des Listview zu ändern. Der Standard-Panel ist das VirtualizingStackPanel die die ListBoxItem schaffen nur das erste Mal, wenn sie sichtbar werden. Wenn Sie nicht zu viele Elemente in Ihrer Liste haben, es sollte kein Problem sein.

<ListView>
   ...
   <ListView.ItemsPanel>
      <ItemsPanelTemplate>
         <StackPanel/>
      </ItemsPanelTemplate>
   </ListView.ItemsPanel>
</ListView>

Vielen Dank für die letzte Spitze Sam. Ich hatte einen Dialog, der geöffnet wird, was bedeutet, mein Gitter geschlossen Fokus jedes Mal, wenn der Dialog verloren. Ich benutze diese:

if(currentRow >= 0 && currentRow < lstGrid.Items.Count) {
    lstGrid.SelectedIndex = currentRow;
    lstGrid.ScrollIntoView(lstGrid.SelectedItem);
    if(shouldFocusGrid) {
        ListViewItem item = lstGrid.ItemContainerGenerator.ContainerFromItem(lstGrid.SelectedItem) as ListViewItem;
        item.Focus();
    }
} else if(shouldFocusGrid) {
    lstGrid.Focus();
}

Versuchen Sie, diese

private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            ScrollViewer scrollViewer = GetScrollViewer(lstVw) as ScrollViewer;
            scrollViewer.ScrollToHorizontalOffset(dataRowToFocus.RowIndex);
            if (dataRowToFocus.RowIndex < 2)
                lstVw.ScrollIntoView((Entity)lstVw.Items[0]);
            else
                lstVw.ScrollIntoView(e.AddedItems[0]);
        } 

 public static DependencyObject GetScrollViewer(DependencyObject o)
        {
            if (o is ScrollViewer)
            { return o; }

            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(o); i++)
            {
                var child = VisualTreeHelper.GetChild(o, i);

                var result = GetScrollViewer(child);
                if (result == null)
                {
                    continue;
                }
                else
                {
                    return result;
                }
            }
            return null;
        } 

private void Focus()
{
 lstVw.SelectedIndex = dataRowToFocus.RowIndex;
 lstVw.SelectedItem = (Entity)dataRowToFocus.Row;

 ListViewItem lvi = (ListViewItem)lstVw.ItemContainerGenerator.ContainerFromItem(lstVw.SelectedItem);
ContentPresenter contentPresenter = FindVisualChild<ContentPresenter>(lvi);
contentPresenter.Focus();
contentPresenter.BringIntoView();

}

Wenn Sie nur zeigen wollen, und konzentrieren sich auf das letzte Element, nachdem ein neues Datenelement zu schaffen, ist diese Methode vielleicht besser. Vergleichen Sie bis zu ScrollIntoView, ScrollToEnd von Scroll ist in meinen Tests zuverlässiger. Bei einigen Tests von Listview mit ScrollIntoView Verfahren wie oben ausgefallen und ich Grund nicht kennen. Aber Scroll mit Blättern dauern kann man arbeiten.

void FocusLastOne(ListView lsv)
{
   ObservableCollection<object> items= sender as ObservableCollection<object>;

   Decorator d = VisualTreeHelper.GetChild(lsv, 0) as Decorator;
   ScrollViewer v = d.Child as ScrollViewer;
   v.ScrollToEnd();

   lsv.SelectedItem = lsv.Items.GetItemAt(items.Count - 1);
   ListViewItem lvi = lsv.ItemContainerGenerator.ContainerFromIndex(items.Count - 1) as ListViewItem;
   lvi.Focus();
}

Ich hatte gerade das gleiche Problem mit ItemContainerGenerator.ContainerFromItem () und ItemContainerGenerator.ContainerFromIndex () Rückkehr null für Gegenstände, die eindeutig in der Listbox existieren. Decasteljau richtig war, aber ich hatte einige graben zu tun, genau herauszufinden, was er meinte. Hier ist die Aufschlüsselung des nächsten Kerl / gal etwas Zuarbeit zu speichern.

Lange Rede kurzer Sinn, ListBoxItems zerstört werden, wenn sie nicht in Sichtweite sind. Folglich ContainerFromItem () und ContainerFromIndex () return null, da die ListBoxItems nicht existieren. Dies ist offensichtlich eine Speicher / Leistungssparfunktion detailliert hier: http://blogs.msdn.com/b/oren/archive/2010/11/08/wp7-silverlight-perf-demo -1-VirtualizingStackPanel-vs-Stackpanel-as-a-listbox-itemspanel.aspx

Der leere <ListBox.ItemsPanel> Code ist, was die Virtualisierung wird ausgeschaltet. Beispielcode, der das Problem für mich festgelegt:

Datenvorlage:

<phone:PhoneApplicationPage.Resources>
    <DataTemplate x:Key="StoryViewModelTemplate">
        <StackPanel>
          <your datatemplated stuff here/>
        </StackPanel>
    </DataTemplate>
</phone:PhoneApplicationPage.Resources>

Der Hauptkörper:

<Grid x:Name="ContentPanel">
    <ListBox Name="lbResults" ItemsSource="{Binding SearchResults}" ItemTemplate="{StaticResource StoryViewModelTemplate}">
        <ListBox.ItemsPanel>
             <ItemsPanelTemplate>
                 <StackPanel>
                 </StackPanel>
             </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
    </ListBox>
</Grid>

Um die Virtualisierung Problem zu überwinden, aber immer noch ScrollIntoView verwenden und Hacking um nicht in den Eingeweiden des Listview, können Sie auch Ihre Viewmodel-Objekte verwenden, um zu bestimmen, was ausgewählt wird. Unter der Annahme, dass Sie Ansichtsmodell Objekte in Ihrer Liste haben, die eine IsSelected Eigenschaft verfügen. Sie würden die Elemente in der Listview in XAML wie diesen Link:

<ListView Name="PersonsListView" ItemsSource="{Binding PersonVMs}">
  <ListView.ItemContainerStyle>
    <Style TargetType="{x:Type ListViewItem}">
      <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
    </Style>
  </ListView.ItemContainerStyle>
</ListView>

Dann kann die Code-Behind-Methode navigieren Sie zu dem ersten ausgewählten Artikel mit diesem:

var firstSelected = PersonsListView.Items
    .OfType<TreeViewItemViewModel>().FirstOrDefault(x => x.IsSelected);
if (firstSelected != null)
    CoObjectsListView.ScrollIntoView(firstSelected);

Das funktioniert auch, wenn der ausgewählte Artikel gut aus der Sicht ist. In meinem Experiment war die PersonsListView.SelectedItem Eigenschaft null, aber natürlich Ihr Ansichtsmodell IsSelected Eigenschaft ist immer da. Achten Sie darauf, diese Methode zu nennen, nachdem alle verbindlich und vollständig geladen ist (mit dem Recht DispatcherPriority).

Mit dem ViewCommand Muster Ihr Ansichtsmodell Code könnte wie folgt aussehen:

PersonVMs.ForEach(vm => vm.IsSelected = false);
PersonVMs.Add(newPersonVM);
newPersonVM.IsSelected = true;
ViewCommandManager.InvokeLoaded("ScrollToSelectedPerson");

In meinem Projekt ich brauche die ausgewählte Indexlinie aus der Listenansicht, um den Benutzer anzuzeigen, so zugewiesen i das ausgewählten Element Listview-Steuerelement. Dieser Code wird die Scrollbar scrollen und das ausgewählte Element angezeigt werden soll.

BooleanListView.ScrollIntoView (BooleanListView.SelectedItem);

oder

var listview = BooleanListView;                 listView.SelectedItem = listView.Items.GetItemAt (BooleanListView.SelectedIndex);                 listView.ScrollIntoView (listView.Items [0]);                 listView.ScrollIntoView (listView.SelectedItem);

nicht sicher, ob dies der richtige Weg zu gehen, aber das funktioniert zur Zeit für mich mit WPF, MVVM Licht und .NET 3.5

Ich habe das Selection Ereignis für ListBox genannte " lbPossibleError_SelectionChanged " Ich habe das Selection Ereignis f&uuml;r List-Box hinzugef&uuml;gt

dann hinter diesem " lbPossibleError_SelectionChanged " Ereignisse, hier ist der Code eingeben Bild Beschreibung hier

funktioniert, wie es für mich sollte.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top