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属性,或者使用控件中的索引保存滚动条位置。使用索引不是我的选项,因为我需要确定选项卡