23

WPF、ブラウザライク アプリ。
ListView を含む 1 つのページを取得しました。PageFunction を呼び出した後、ListView に行を追加し、新しい行をスクロールして表示したいと考えています。

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

これは機能します。新しい行が表示されている限り、行は本来のようにフォーカスされます。

問題は、線が表示されていないと機能しないことです。
行が表示されない場合、生成された行の ListViewItem がないため、ItemContainerGenerator.ContainerFromIndex は null を返します。

しかし、アイテムがなければ、行をスクロールして表示するにはどうすればよいですか? ListViewItem を必要とせずに最後の行 (または任意の場所) にスクロールする方法はありますか?

4

11 に答える 11

42

誰かが、特定の行にスクロールするさらに良い方法を教えてくれました。これは簡単で、魅力的に機能します。
要するに:

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();
}

MSDN フォーラムの長いバージョン:

于 2008-10-23T10:33:10.727 に答える
12

ここでの問題は、行が表示されていない場合、ListViewItem がまだ作成されていないことだと思います。WPF はオンデマンドで Visible を作成します。

したがって、この場合、おそらくnullアイテムを手に入れることができますよね?(あなたのコメントによると、あなたはそうします)

MSDN フォーラムで、スクロールするために Scrollviewer に直接アクセスすることを提案するリンクを見つけました。そこに提示された解決策はハックのように見えますが、自分で決めることができます。

上記のリンクからのコード スニペットは次のとおりです。

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);
于 2008-10-17T12:50:30.500 に答える
5

サムの答えにいくつか変更を加えました。最後の行までスクロールしたかったことに注意してください。残念ながら、ListViewの一部には最後の行が表示されただけなので(たとえば、その上に100行あった場合でも)、次のように修正しました。

    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();
        }
    }

乾杯

于 2009-08-20T11:30:09.230 に答える
3

これに対する 1 つの回避策は、ListView の ItemsPanel を変更することです。デフォルトのパネルは VirtualizingStackPanel で、ListBoxItem が初めて表示されるときにのみ作成されます。リストにあまり多くのアイテムがない場合は、問題にはなりません。

<ListView>
   ...
   <ListView.ItemsPanel>
      <ItemsPanelTemplate>
         <StackPanel/>
      </ItemsPanelTemplate>
   </ListView.ItemsPanel>
</ListView>
于 2008-10-17T17:37:29.530 に答える
2

その最後のヒントサムをありがとう。ダイアログが開きました。つまり、ダイアログを閉じるたびにグリッドのフォーカスが失われました。私はこれを使用します:

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();
}
于 2009-03-26T18:08:56.363 に答える
2

これを試して

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();

}
于 2010-04-09T21:12:04.977 に答える
2

新しいデータ項目を作成した後、最後の項目だけを表示してフォーカスしたい場合は、この方法の方がよいでしょう。ScrollIntoView と比較して、ScrollViewer の ScrollToEnd は、私のテストではより信頼性が高いです。上記のようなListViewのScrollIntoViewメソッドを使用したいくつかのテストでは失敗し、理由がわかりません。ただし、ScrollViewer を使用して最後のスクロールまでスクロールすることはできます。

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();
}
于 2012-11-13T14:15:20.410 に答える
1

ItemContainerGenerator.ContainerFromItem() と ItemContainerGenerator.ContainerFromIndex() で同じ問題が発生し、リストボックスに明らかに存在するアイテムに対して null が返されました。Decasteljau は正しかったのですが、彼の意図を正確に理解するために、いくつかの調査を行う必要がありました。これは、次の男/ギャルの手間を省くための内訳です.

簡単に言うと、ListBoxItems がビュー内にない場合、ListBoxItems は破棄されます。その結果、ListBoxItems が存在しないため、ContainerFromItem() および ContainerFromIndex() は null を返します。これは明らかに、ここで詳述されているメモリ/パフォーマンス節約機能です: http://blogs.msdn.com/b/oren/archive/2010/11/08/wp7-silverlight-perf-demo-1-virtualizingstackpanel-vs-stackpanel- as-a-listbox-itemspanel.aspx

空の<ListBox.ItemsPanel>コードは、仮想化を無効にするものです。私の問題を修正したサンプルコード:

データ テンプレート:

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

本体:

<Grid x:Name="ContentPanel">
    <ListBox Name="lbResults" ItemsSource="{Binding SearchResults}" ItemTemplate="{StaticResource StoryViewModelTemplate}">
        <ListBox.ItemsPanel>
             <ItemsPanelTemplate>
                 <StackPanel>
                 </StackPanel>
             </ItemsPanelTemplate>
        </ListBox.ItemsPanel>
    </ListBox>
</Grid>
于 2011-12-07T04:26:57.510 に答える
0

仮想化の問題を克服しScrollIntoView、ListView の内部をハッキングせずに使用するには、ViewModel オブジェクトを使用して、選択されているものを判断することもできます。プロパティを備えたリストにViewModelオブジェクトがあると仮定しますIsSelected。次のように、項目を XAML の ListView にリンクします。

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

次に、コード ビハインド メソッドは、次のように最初に選択された項目までスクロールできます。

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

これは、選択したアイテムが見えない場合にも機能します。私の実験では、PersonsListView.SelectedItemプロパティは でしnullたが、もちろん ViewModelIsSelectedプロパティは常にそこにあります。すべてのバインディングとロードが完了したら、必ずこのメソッドを呼び出してください (右の を使用DispatcherPriority)。

ViewCommandパターンを使用すると、ViewModel コードは次のようになります。

PersonVMs.ForEach(vm => vm.IsSelected = false);
PersonVMs.Add(newPersonVM);
newPersonVM.IsSelected = true;
ViewCommandManager.InvokeLoaded("ScrollToSelectedPerson");
于 2013-03-08T11:42:01.797 に答える
0

これが正しいかどうかはわかりませんが、これは現在、WPF、MVVM Light、および.NET 3.5を使用して機能します

「 lbPossibleError_SelectionChanged」 という ListBox の SelectionChanged イベントを追加しました。ListBox の SelectionChanged イベントを追加しました

この " lbPossibleError_SelectionChanged " イベントの背後にあるコードは次の とおりです。ここに画像の説明を入力

私にとっては正常に機能します。

于 2015-03-16T14:57:56.893 に答える