关于这里的SO问题,是否可以从代码后面更新其他选项卡内容,并允许缓存重新缓存更改的UI元素?就像在场景中一样,我在某个事件的TabControl
上更新了一些tabs
的DataGrid
滚动索引,
dgvLogs.ScrollIntoView( log );
现在,由于选项卡已经被缓存,当用户切换到dgvLogs
所在的选项卡时,上述更改并没有反映出来。
编辑
我在主窗口中有选项卡控件(ExTabControl
),多个选项卡包含datagrid
,其中显示一些应用程序日志
ExTabControl
像这样:
<controls:ExTabControl Grid.Row="1" ItemsSource="{Binding Tabs, Mode=OneWay}" >
<controls:ExTabControl.Resources>
<Style TargetType="{x:Type TabItem}">
</Style>
</controls:ExTabControl.Resources>
</controls:ExTabControl>
单个选项卡的数据网格如下:
<DataGrid Name="dgvLogs" ItemsSource="{Binding Logs}" VerticalScrollBarVisibility="Auto" FrozenColumnCount="4">
问题:
假设我在ExTabControl
中有3个选项卡,所选选项卡是1,并且从后面的代码中可以使用dgvLogs.ScrollIntoView( someInbetweenlog );
更新选项卡2的滚动索引。理想情况下,如果我选择了选项卡2,那么选择dgvLogs
内的滚动索引应该是someInbetweenlog
所在的位置。但不幸的是,选项卡2滚动并没有按照代码后面的更改进行移动。。
如果我确实使用了默认的选项卡控件,即TabControl
而不是ExTabControl
,那么它可以正常工作。但如果我在dgvLogs
的任何选项卡中滚动,那么它也反映在其他选项卡中。。
请添加评论,如果需要,我会发布更多代码。
编辑2
我创建了一个示例应用程序,试图在其中演示这个问题。在这个应用程序中,我在选项卡中添加了网格的上下文菜单,并使用Sync
选项,我试图滚动到其他打开的选项卡中第一个与关闭的选定日志匹配的日志的位置。
问题:ExTabControl
无法滚动到不同打开选项卡中所需的日志项目。
https://github.com/ankushmadankar/StackOverflow54198246/
如果我使用默认的选项卡控件,即
TabControl
而不是ExTabControl
,那么它可以正常工作。但如果我在dgvLogs
的任何选项卡中滚动,那么它也会反映在其他选项卡中。
TabControl
有两种用途,在本文中进行了扩展:
- 当我们将
ItemsSource
绑定到一个项目列表,并且我们为每个项目设置了相同的DataTemplate
时,TabControl
将只创建一个"内容";查看所有项目。当选择不同的选项卡项目时,View
不会更改,但支持DataContext
绑定到新选择项目的视图模型
是否可以从代码后面更新其他选项卡内容,并允许缓存重新缓存更改的UI元素?
更新无法工作的原因是来自UIElement.IsVisible:的另一个WPF优化
IsVisible为false的元素不参与输入事件(或命令),不影响布局的测量或排列过程,不可聚焦,不在选项卡序列中,并且不会在命中测试中报告。
您可以更改缓存元素的属性,但有些操作需要UIElement
可见才能生效。
值得注意的是:
- 如果在不可见的
DataGrid
上调用ScrollIntoView
,它将不会滚动到给定的对象。因此,链接项目中的ScrollToSelectedBehavior
旨在滚动在此过程中可见的数据网格 - 在
ExTabControl
的代码中,方法UpdateSelectedItem
将非活动内容呈现者的可见性设置为折叠
如果您明确要求代码隐藏,
快速破解
TraceViewerView.xaml
<DataGrid IsVisibleChanged="dgvLogs_IsVisibleChanged" ... >
TraceViewerView.xaml.cs
private void dgvLogs_IsVisibleChanged(object sender, System.Windows.DependencyPropertyChangedEventArgs e)
{
if (sender is DataGrid dataGrid && dataGrid.IsVisible)
{
TraceViewerViewModel viewModel = (TraceViewerViewModel)DataContext;
if (viewModel.Log != null)
dataGrid.ScrollIntoView(viewModel.Log);
}
}
几句话:
- 您现在可以删除行
local:ScrollToSelectedBehavior.SelectedValue="{Binding Log}"
,因为我们直接从视图模型中获取同步值 - 这是的黑客攻击,视图被硬编码到你的视图模型中,它可能会在某个时候爆炸
更好的方法
首先,为了保持我们的代码松散耦合,接口。
interface ISync
{
object SyncValue { get; }
}
TraceViewerModel.cs
public class TraceViewerViewModel : PropertyObservable, ITabItem, ISync
将Log
重命名为SyncValue
,并替换原始代码
private TraceLog synclog;
public TraceLog Log
{
get { return synclog; }
private set
{
synclog = value;
OnPropertyChanged();
}
}
带有
public object SyncValue { get; set; }
基本上,我们用Binding
来交换接口。我在这个特定用例中选择接口的原因是,当你移动到一个选项卡时,你只需要检查它的同步值(这使得成熟的Binding
有点过头了)。
接下来,让我们创建一个Behavior
,它可以满足您的需要。
我将使用交互行为,而不是附加属性,它提供了一种更封装的方式来扩展功能(需要System.Windows.Interactivey)
ScrollToSyncValueBehavior.cs
using System.Windows;
using System.Windows.Controls;
using System.Windows.Interactivity;
namespace WpfApp1
{
public class ScrollToSyncValueBehavior : Behavior<DataGrid>
{
protected override void OnAttached()
{
this.AssociatedObject.IsVisibleChanged += OnVisibleChanged;
}
protected override void OnDetaching()
{
this.AssociatedObject.IsVisibleChanged -= OnVisibleChanged;
}
private static void OnVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (sender is DataGrid dataGrid && dataGrid.IsVisible)
{
ISync viewModel = dataGrid.DataContext as ISync;
if (viewModel?.SyncValue != null)
dataGrid.ScrollIntoView(viewModel.SyncValue);
}
}
}
}
TraceViewerView.xaml
<UserControl x:Class="WpfApp1.TraceViewerView"
...
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
<Grid>
<DataGrid CanUserAddRows="false" GridLinesVisibility="None" AutoGenerateColumns="False"
ItemsSource="{Binding Logs}">
<i:Interaction.Behaviors>
<local:ScrollToSyncValueBehavior />
</i:Interaction.Behaviors>
...
</DataGrid>
</Grid>
</UserControl>
您需要导出TabControl
,如本答案中所述。使用该技术,将保留每个选项卡的可视化树。
请注意,如果您有很多选项卡,那么缓存将对性能产生重大影响。我建议最多使用10个标签。