Если я использую элемент управления вкладками по умолчанию, т.е. TabControl
вместо ExTabControl
, тогда он работает нормально, как и ожидалось. Но если я переместу прокрутку в любой из вкладок на dgvLogs
, то это отражается и на других вкладках.
Существует два варианта использования TabControl
, распространяющихся на этот пост :
- Когда мы связываем
ItemsSource
со списком элементов, и мы устанавливаем одинаковые DataTemplate
для каждого элемента, TabControl
создаст только одно представление «Содержимое» для всех элементов. И когда выбран другой элемент вкладки, View
не изменяется, но подложка DataContext
привязывается к модели представления вновь выбранного элемента.
Можно ли обновить содержимое другой вкладки из кода и позволить кешированию повторно кэшировать измененные элементы интерфейса?
Причина, по которой обновления не будут работать, из-за другой оптимизации WPF из UIElement.IsVisible :
Элементы, для которых IsVisible имеет значение false, не участвуют во входных событиях (или командах), не влияют ни на меру, ни на организацию проходов макета, не фокусируются, не находятся в последовательности вкладок и не будут сообщаться при тестировании попаданий. .
Вы можете изменить свойства кэшированных элементов, но некоторые операции требуют, чтобы UIElement
был видимым, чтобы вступить в силу.
Стоит отметить:
- Если вы вызовете
ScrollIntoView
для DataGrid
, который не отображается, он не будет прокручиваться до заданного объекта. Таким образом, ScrollToSelectedBehavior
из вашего связанного проекта предназначен для прокрутки сетки данных, видимой во время процесса.
- В коде
ExTabControl
метод UpdateSelectedItem
устанавливает видимость неактивных contentpresenters как свернутых.
Учитывая, что вы явно запросили код,
Быстрый взлом
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.Interactivity ).
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>