سؤال

WPF, Browserlike التطبيق.
حصلت صفحة واحدة تحتوي على ListView.بعد استدعاء PageFunction إضافة خط إلى ListView و أريد أن انتقل خط جديد في طريقة العرض:

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

يعمل هذا.طالما أن الخط الجديد في عرض خط يحصل على التركيز كما ينبغي.

المشكلة هي أن الأمور لا تزال عند خط غير مرئي.
إذا كان الخط غير مرئية, لا يوجد ListViewItem للخط الصادرة ItemContainerGenerator.ContainerFromIndex إرجاع null.

ولكن من دون هذا البند ، كيف انتقل الخط إلى الرأي ؟ هل هناك أي طريقة أن انتقل إلى السطر الأخير (أو أي مكان) بدون الحاجة ListViewItem?

هل كانت مفيدة؟

المحلول

وأعتقد أن المشكلة هنا هي أن ListViewItem لا يتم إنشاء بعد ما إذا كان خط غير مرئي. WPF يخلق المرئية حسب الطلب.

وحتى في هذه الحالة ربما تحصل null لهذا البند، أليس كذلك؟ (وفقا لتعليقك، لديك)

ولقد وجدت <لأ href = "http://social.msdn.microsoft.com/forums/en-US/wpf/thread/7c5812bd-482f-448c-bc67-02573e48d691" يختلط = "نوفولو noreferrer" > رابط على المنتديات 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);

نصائح أخرى

وأحدهم قال لي طريقة أفضل للانتقال إلى خط معين، والتي من السهل وتعمل مثل السحر.
باختصار:

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

والنسخة الأطول في <لأ href = "http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/2ce4fee4-fbf6-4c0e-9dc6-49c0ac0c3152/" يختلط = "noreferrer" > منتديات MSDN :

وأجريت بعض التغييرات على الجواب سام. لاحظ أن أردت أن انتقل إلى السطر الأخير. للأسف sometiems يكون 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();
        }
    }

وهتاف

والحل واحد لهذا هو لتغيير ItemsPanel من يكون ListView. لوحة الافتراضية هي VirtualizingStackPanel مما يخلق فقط ListBoxItem المرة الأولى التي تصبح مرئية. إذا لم يكن لديك الكثير من البنود في القائمة الخاصة بك، فإنه لا ينبغي أن يكون مشكلة.

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

وشكرا لهذا غيض الماضي سام. كان لي الحوار الذي افتتح، وهذا يعني بلدي الشبكة فقدت التركيز في كل مرة تغلق مربع الحوار. أنا استخدم هذا:

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

وهذه محاولة

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

}

إذا كنت ترغب فقط لإظهار والتركيز على العنصر الأخير بعد إنشاء عنصر بيانات جديدة، وهذا الأسلوب هو ربما أفضل. مقارنة ScrollIntoView، ScrollToEnd من ScrollViewer في بلدي التجارب أكثر موثوقية. في بعض الاختبارات باستخدام طريقة ScrollIntoView من ListView فشل مثل أعلاه وأنا لا أعرف السبب. ولكن باستخدام 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();
}

ولقد كان نفس القضية مع ItemContainerGenerator.ContainerFromItem () وItemContainerGenerator.ContainerFromIndex () تعود فارغة لالعناصر التي كانت موجودة بشكل واضح في مربع القائمة. كان Decasteljau حق ولكن كان علي أن أفعل بعض الحفر لمعرفة بالضبط ما كان يعنيه. هنا هو انهيار لانقاذ الرجل القادم / غال بعض الأبحاث.

وقصة قصيرة طويلة، ودمرت ListBoxItems إذا لم تكن ضمن الرأي. ونتيجة لذلك ContainerFromItem () وContainerFromIndex () لاغية عودة منذ ListBoxItems لا وجود لها. هذا على ما يبدو الذاكرة / الأداء ميزة توفير مفصلة هنا: <لأ href = "http://blogs.msdn.com/b/oren/archive/2010/11/08/wp7-silverlight-perf-demo-1-virtualizingstackpanel -vs-stackpanel-كما هو وجود مربع القائمة itemspanel.aspx "يختلط =" نوفولو "> http://blogs.msdn.com/b/oren/archive/2010/11/08/wp7-silverlight-perf-demo -1-virtualizingstackpanel-مقابل-stackpanel-كما هو وجود مربع القائمة 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>

لتغلب على مسألة المحاكاة الافتراضية ولكن لا تزال تستخدم ScrollIntoView وليس القرصنة حولها في أحشاء يكون ListView، هل يمكن أيضا استخدام الكائنات ViewModel لتحديد ما تم تحديده. على افتراض أن لديك أشياء ViewModel في قائمتك التي تتميز خاصية IsSelected. وكنت ربط العناصر إلى ListView في XAML مثل هذا:

<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، ولكن بالطبع الممتلكات الخاصة بك ViewModel IsSelected هناك دائما. ومن المؤكد أن استدعاء هذا الأسلوب بعد كل ملزمة واكتمال تحميل (مع DispatcherPriority اليمين).

واستخدام ViewCommand نمط ، رمز ViewModel الخاص بك يمكن أن تبدو مثل هذا:

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

في مشروعي أنا بحاجة لعرض خط الفهرس المحدد من ListView إلى المستخدم لذلك أنا تعيين العنصر المحدد لتحكم ListView. وهذا الرمز انتقل شريط التمرير وعرض العنصر المحدد.

وBooleanListView.ScrollIntoView (BooleanListView.SelectedItem)؛

وOR

وفار listView = BooleanListView.                 listView.SelectedItem = listView.Items.GetItemAt (BooleanListView.SelectedIndex)؛                 listView.ScrollIntoView (listView.Items [0])؛                 listView.ScrollIntoView (listView.SelectedItem)؛

لست متأكدا إذا كان هذا هو الطريق للذهاب ولكن هذا يعمل حاليا بالنسبة لي باستخدام WPF, MVVM الضوء .NET framework 3.5

أضفت SelectionChanged الحدث ListBox يسمى "lbPossibleError_SelectionChanged" I added the SelectionChanged event for ListBox

ثم وراء هذا "lbPossibleError_SelectionChanged"الحدث, هنا هو رمز enter image description here

يعمل كما يجب بالنسبة لي.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top