scroll - 在TabControl滚动问题中,wpf列表框

  显示原文与译文双语对照的内容
114 2

在TabControl中,我遇到了一个问题。当更改标签时,列表框的滚动条位置将为 0.下面是复制代码:

<TabControl x:Name="_tabs">
 <TabItem Header="1">
 <ListBox ItemsSource="{Binding}" ScrollViewer.VerticalScrollBarVisibility="Auto"/>
 </TabItem>
 <TabItem Header="2">
 <ListBox ItemsSource="{Binding}" ScrollViewer.VerticalScrollBarVisibility="Auto"/>
 </TabItem>
</TabControl>
_tabs.DataContext = Enumerable.Range(1, 300).ToArray();

打开窗口时,打开第二个标签,滚动列表到中间,回到第一个选项卡,然后再次打开第二个标签。由于某些原因,列表滚动到顶部。

为什么会这样我做了一些愚蠢的错误?

时间:原作者:0个回答

128 1

sql的默认行为是卸载不可见的项,这些项包括卸载不可见的TabItems 。这意味着当你返回到选项卡时,会被重新加载,并且任何没有绑定的( 例如滚动位置) 都会被重置。

这里有一个不错的站点 ,它包含扩展TabControl并阻止它在切换选项卡时销毁它的代码,但是站点似乎是关闭了 atm 。

下面是我使用的代码。虽然我对它做了一些改动,但它最初是来自那个网站。当切换选项卡时保留TabItems的ContentPresenter,并使用它在返回到页面时重绘 TabItem 。它占用了更多内存,但是我发现性能更好,因为nmake不再需要创建它的所有控件。

//Extended TabControl which saves the displayed item so you don't get the performance hit of 
//unloading and reloading the VisualTree when switching tabs
//Obtained from http://eric.burke.name/dotnetmania/2009/04/26/22.09.28
//and made a some modifications so it reuses a TabItem's ContentPresenter when doing drag/drop operations
[TemplatePart(Name ="PART_ItemsHolder", Type = typeof(Panel))]
public class TabControlEx : System.Windows.Controls.TabControl
{
//Holds all items, but only marks the current tab's item as visible
 private Panel _itemsHolder = null;
//Temporaily holds deleted item in case this was a drag/drop operation
 private object _deletedObject = null;
 public TabControlEx()
 : base()
 {
//this is necessary so that we get the initial databound selected item
 this.ItemContainerGenerator.StatusChanged += ItemContainerGenerator_StatusChanged;
 }
///<summary>
///if containers are done, generate the selected item
///</summary>
///<param name="sender"></param>
///<param name="e"></param>
 void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
 {
 if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated)
 {
 this.ItemContainerGenerator.StatusChanged -= ItemContainerGenerator_StatusChanged;
 UpdateSelectedItem();
 }
 }
///<summary>
///get the ItemsHolder and generate any children
///</summary>
 public override void OnApplyTemplate()
 {
 base.OnApplyTemplate();
 _itemsHolder = GetTemplateChild("PART_ItemsHolder") as Panel;
 UpdateSelectedItem();
 }
///<summary>
///when the items change we remove any generated panel children and add any new ones as necessary
///</summary>
///<param name="e"></param>
 protected override void OnItemsChanged(NotifyCollectionChangedEventArgs e)
 {
 base.OnItemsChanged(e);
 if (_itemsHolder == null)
 {
 return;
 }
 switch (e.Action)
 {
 case NotifyCollectionChangedAction.Reset:
 _itemsHolder.Children.Clear();
 if (base.Items.Count> 0)
 {
 base.SelectedItem = base.Items[0];
 UpdateSelectedItem();
 }
 break;
 case NotifyCollectionChangedAction.Add:
 case NotifyCollectionChangedAction.Remove:
//Search for recently deleted items caused by a Drag/Drop operation
 if (e.NewItems!= null && _deletedObject!= null)
 {
 foreach (var item in e.NewItems)
 {
 if (_deletedObject == item)
 {
//If the new item is the same as the recently deleted one (i.e. a drag/drop event)
//then cancel the deletion and reuse the ContentPresenter so it doesn't have to be 
//redrawn. We do need to link the presenter to the new item though (using the Tag)
 ContentPresenter cp = FindChildContentPresenter(_deletedObject);
 if (cp!= null)
 {
 int index = _itemsHolder.Children.IndexOf(cp);
 (_itemsHolder.Children[index] as ContentPresenter).Tag =
 (item is TabItem)? item : (this.ItemContainerGenerator.ContainerFromItem(item));
 }
 _deletedObject = null;
 }
 }
 }
 if (e.OldItems!= null)
 {
 foreach (var item in e.OldItems)
 {
 _deletedObject = item;
//We want to run this at a slightly later priority in case this
//is a drag/drop operation so that we can reuse the template
 this.Dispatcher.BeginInvoke(DispatcherPriority.DataBind,
 new Action(delegate()
 {
 if (_deletedObject!= null)
 {
 ContentPresenter cp = FindChildContentPresenter(_deletedObject);
 if (cp!= null)
 {
 this._itemsHolder.Children.Remove(cp);
 }
 }
 }
 ));
 }
 }
 UpdateSelectedItem();
 break;
 case NotifyCollectionChangedAction.Replace:
 throw new NotImplementedException("Replace not implemented yet");
 }
 }
///<summary>
///update the visible child in the ItemsHolder
///</summary>
///<param name="e"></param>
 protected override void OnSelectionChanged(SelectionChangedEventArgs e)
 {
 base.OnSelectionChanged(e);
 UpdateSelectedItem();
 }
///<summary>
///generate a ContentPresenter for the selected item
///</summary>
 void UpdateSelectedItem()
 {
 if (_itemsHolder == null)
 {
 return;
 }
//generate a ContentPresenter if necessary
 TabItem item = GetSelectedTabItem();
 if (item!= null)
 {
 CreateChildContentPresenter(item);
 }
//show the right child
 foreach (ContentPresenter child in _itemsHolder.Children)
 {
 child.Visibility = ((child.Tag as TabItem).IsSelected)? Visibility.Visible : Visibility.Collapsed;
 }
 }
///<summary>
///create the child ContentPresenter for the given item (could be data or a TabItem)
///</summary>
///<param name="item"></param>
///<returns></returns>
 ContentPresenter CreateChildContentPresenter(object item)
 {
 if (item == null)
 {
 return null;
 }
 ContentPresenter cp = FindChildContentPresenter(item);
 if (cp!= null)
 {
 return cp;
 }
//the actual child to be added. cp.Tag is a reference to the TabItem
 cp = new ContentPresenter();
 cp.Content = (item is TabItem)? (item as TabItem).Content : item;
 cp.ContentTemplate = this.SelectedContentTemplate;
 cp.ContentTemplateSelector = this.SelectedContentTemplateSelector;
 cp.ContentStringFormat = this.SelectedContentStringFormat;
 cp.Visibility = Visibility.Collapsed;
 cp.Tag = (item is TabItem)? item : (this.ItemContainerGenerator.ContainerFromItem(item));
 _itemsHolder.Children.Add(cp);
 return cp;
 }
///<summary>
///Find the CP for the given object. data could be a TabItem or a piece of data
///</summary>
///<param name="data"></param>
///<returns></returns>
 ContentPresenter FindChildContentPresenter(object data)
 {
 if (data is TabItem)
 {
 data = (data as TabItem).Content;
 }
 if (data == null)
 {
 return null;
 }
 if (_itemsHolder == null)
 {
 return null;
 }
 foreach (ContentPresenter cp in _itemsHolder.Children)
 {
 if (cp.Content == data)
 {
 return cp;
 }
 }
 return null;
 }
///<summary>
///copied from TabControl; wish it were protected in that class instead of private
///</summary>
///<returns></returns>
 protected TabItem GetSelectedTabItem()
 {
 object selectedItem = base.SelectedItem;
 if (selectedItem == null)
 {
 return null;
 }
 if (_deletedObject == selectedItem)
 { 
 }
 TabItem item = selectedItem as TabItem;
 if (item == null)
 {
 item = base.ItemContainerGenerator.ContainerFromIndex(base.SelectedIndex) as TabItem;
 }
 return item;
 }
}

我通常使用的TabControl模板看起来像这样:

<Style x:Key="TabControlEx_NoHeadersStyle" TargetType="{x:Type local:TabControlEx}">
 <Setter Property="SnapsToDevicePixels" Value="true"/>
 <Setter Property="Template">
 <Setter.Value>
 <ControlTemplate TargetType="{x:Type localControls:TabControlEx}">
 <DockPanel>
 <!-- This is needed to draw TabControls with Bound items -->
 <StackPanel IsItemsHost="True" Height="0" Width="0"/>
 <Grid x:Name="PART_ItemsHolder"/>
 </DockPanel>
 </ControlTemplate>
 </Setter.Value>
 </Setter>
</Style>
原作者:
...