WPF-TabItem中奇怪的滚动条行为
我有以下代码:WPF-TabItem中奇怪的滚动条行为,wpf,xaml,datatemplate,tabcontrol,scrollviewer,Wpf,Xaml,Datatemplate,Tabcontrol,Scrollviewer,我有以下代码: <Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="145" Width="156"> <Window.Resources> <DataT
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="145" Width="156">
<Window.Resources>
<DataTemplate x:Key="tabTemplate">
<ScrollViewer>
<StackPanel Orientation="Vertical">
<TextBlock>x</TextBlock>
<TextBlock>x</TextBlock>
<TextBlock>x</TextBlock>
<TextBlock>x</TextBlock>
<TextBlock>x</TextBlock>
<TextBlock>x</TextBlock>
<TextBlock>x</TextBlock>
</StackPanel>
</ScrollViewer>
</DataTemplate>
</Window.Resources>
<Grid>
<TabControl>
<TabItem Header="Tab1" ContentTemplate="{StaticResource ResourceKey=tabTemplate}"/>
<TabItem Header="Tab2" ContentTemplate="{StaticResource ResourceKey=tabTemplate}"/>
</TabControl>
</Grid>
</Window>
x
x
x
x
x
x
x
奇怪的是滚动条的行为-如果我向下滚动第一个选项卡并切换到第二个选项卡,滚动条也向下-当选项卡项具有相同的数据模板时,滚动条的位置是同步的。你知道这个问题有什么解决办法吗
此外,当我修改代码并制作两个数据模板(每个选项卡一个)时,滚动条根本没有保留它们的位置——这意味着如果我向下滚动到选项卡1,再次切换到选项卡2和选项卡1,滚动条将处于默认位置。此项的任何解决方案?要启用
数据模板
为每次使用创建单独的实例,只需将x:Shared
属性设置为False
:
<DataTemplate x:Key="tabTemplate" x:Shared="False">
这将导致第二个问题,即在选项卡更改时保留UI。根据,解决方案是使用一个不同的
ItemsControl
,它看起来像一个TabControl
要使DataTemplate
为每个用法创建单独的实例,只需将x:Shared
属性设置为False
:
<DataTemplate x:Key="tabTemplate" x:Shared="False">
这将导致第二个问题,即在选项卡更改时保留UI。根据,解决方案是使用一个不同的
ItemsControl
,它看起来像一个TabControl
,我使用新控件ZoomPanel:ScrollViewer
解决了第二个问题,其中scollbar的位置是根据DataContext.GetHashCode()
保存的。也许不是最优的解决方案,但对我来说很有效。每个选项卡都有自己的ViewModel,因此滚动条的位置保持不变
public static readonly Dictionary<int, Point> ScrollbarPositions = new Dictionary<int, Point>();
private void ZoomPanelScrollChanged(object sender, ScrollChangedEventArgs e)
{
ZoomPanel panel = (ZoomPanel)sender;
// do not save position when uloading or empty data context
if(!panel.IsLoaded || this.DataContext == null)
{
return;
}
// save scrollbar position
int dataContextHashCode = this.DataContext.GetHashCode();
Point position = new Point(panel.HorizontalOffset, panel.VerticalOffset);
if(ScrollbarPositions.ContainsKey(dataContextHashCode))
{
ScrollbarPositions[dataContextHashCode] = position;
}
else
{
ScrollbarPositions.Add(dataContextHashCode, position);
}
}
private void ZoomPanelLoaded(object sender, RoutedEventArgs e)
{
if(this.DataContext == null)
{
return;
}
// load scrollbar position
int dataContextHashCode = this.DataContext.GetHashCode();
if (ScrollbarPositions.ContainsKey(dataContextHashCode))
{
Point position = ScrollbarPositions[dataContextHashCode];
this.ScrollToHorizontalOffset(position.X);
this.ScrollToVerticalOffset(position.Y);
}
}
publicstatic只读字典ScrollbarPositions=newdictionary();
私有void ZoomPanelScrollChanged(对象发送方,ScrollChangedEventArgs e)
{
ZoomPanel=(ZoomPanel)发送器;
//加载或清空数据上下文时不保存位置
如果(!panel.IsLoaded | | this.DataContext==null)
{
回来
}
//保存滚动条位置
int dataContextHashCode=this.DataContext.GetHashCode();
点位置=新点(panel.HorizontalOffset,panel.VerticalOffset);
if(ScrollbarPositions.ContainsKey(dataContextHashCode))
{
ScrollbarPositions[dataContextHashCode]=位置;
}
其他的
{
添加(dataContextHashCode,位置);
}
}
私有void ZoomPanelLoaded(对象发送方,RoutedEventArgs e)
{
if(this.DataContext==null)
{
回来
}
//加载滚动条位置
int dataContextHashCode=this.DataContext.GetHashCode();
if(ScrollbarPositions.ContainsKey(dataContextHashCode))
{
点位置=滚动条位置[dataContextHashCode];
这个.ScrollToHorizontalOffset(位置.X);
此.ScrollToVerticalOffset(位置.Y);
}
}
我使用新控件ZoomPanel:ScrollViewer解决了第二个问题,其中根据DataContext.GetHashCode()
保存了scollbars的位置。也许不是最优的解决方案,但对我来说很有效。每个选项卡都有自己的ViewModel,因此滚动条的位置保持不变
public static readonly Dictionary<int, Point> ScrollbarPositions = new Dictionary<int, Point>();
private void ZoomPanelScrollChanged(object sender, ScrollChangedEventArgs e)
{
ZoomPanel panel = (ZoomPanel)sender;
// do not save position when uloading or empty data context
if(!panel.IsLoaded || this.DataContext == null)
{
return;
}
// save scrollbar position
int dataContextHashCode = this.DataContext.GetHashCode();
Point position = new Point(panel.HorizontalOffset, panel.VerticalOffset);
if(ScrollbarPositions.ContainsKey(dataContextHashCode))
{
ScrollbarPositions[dataContextHashCode] = position;
}
else
{
ScrollbarPositions.Add(dataContextHashCode, position);
}
}
private void ZoomPanelLoaded(object sender, RoutedEventArgs e)
{
if(this.DataContext == null)
{
return;
}
// load scrollbar position
int dataContextHashCode = this.DataContext.GetHashCode();
if (ScrollbarPositions.ContainsKey(dataContextHashCode))
{
Point position = ScrollbarPositions[dataContextHashCode];
this.ScrollToHorizontalOffset(position.X);
this.ScrollToVerticalOffset(position.Y);
}
}
publicstatic只读字典ScrollbarPositions=newdictionary();
私有void ZoomPanelScrollChanged(对象发送方,ScrollChangedEventArgs e)
{
ZoomPanel=(ZoomPanel)发送器;
//加载或清空数据上下文时不保存位置
如果(!panel.IsLoaded | | this.DataContext==null)
{
回来
}
//保存滚动条位置
int dataContextHashCode=this.DataContext.GetHashCode();
点位置=新点(panel.HorizontalOffset,panel.VerticalOffset);
if(ScrollbarPositions.ContainsKey(dataContextHashCode))
{
ScrollbarPositions[dataContextHashCode]=位置;
}
其他的
{
添加(dataContextHashCode,位置);
}
}
私有void ZoomPanelLoaded(对象发送方,RoutedEventArgs e)
{
if(this.DataContext==null)
{
回来
}
//加载滚动条位置
int dataContextHashCode=this.DataContext.GetHashCode();
if(ScrollbarPositions.ContainsKey(dataContextHashCode))
{
点位置=滚动条位置[dataContextHashCode];
这个.ScrollToHorizontalOffset(位置.X);
此.ScrollToVerticalOffset(位置.Y);
}
}
适用于第一个问题,使用ItemsControl对我的项目来说似乎不是一个好的解决方案。但是失去位置没有同步滚动条的位置那么奇怪,所以我接受这个解决方案。对于第一个问题,使用ItemsControl似乎不是一个好的解决方案。但是丢失位置并没有同步滚动条的位置那么奇怪,所以我接受这个解决方案。最安全的方法是在ViewModel上添加唯一的ID属性,或者使用控件中的索引保存滚动条位置。使用索引不是我的选项,因为我需要从内部控件确定选项卡索引。这种方法的下一个问题是,可以通过关闭并重新打开选项卡来更改选项卡索引。这不是代码的关键部分,只是GUI增强,所以我愿意冒险使用哈希代码。不过,哈希代码有一个很好的地方——注释很棒。请注意,GetHashCode不能保证是唯一的。最安全的方法是在ViewModel上添加一个唯一的ID属性,或者使用控件中的索引保存滚动条位置。使用索引不是我的选项,因为我需要确定选项卡