Кэширование TabControl с кодом за изменениями макета - PullRequest
0 голосов
/ 15 января 2019

В связи с этим вопросом SO здесь возможно ли обновить другое содержимое вкладки из кода позади и позволить кешированию повторно кэшировать измененные элементы пользовательского интерфейса? Как и в сценарии, я обновил DataGrid индекс прокрутки для некоторых tabs на TabControl для некоторых событий,

dgvLogs.ScrollIntoView( log );

Теперь, так как вкладка уже кэширована и выше, изменения не отражаются, когда пользователь переключается на вкладку, где находится dgvLogs.

EDIT

У меня есть вкладка управления (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">

Проблема:

Допустим, у меня есть 3 вкладки в ExTabControl, выбранная вкладка равна 1, и из кода сзади есть индекс обновления прокрутки для вкладки 2 с использованием dgvLogs.ScrollIntoView( someInbetweenlog );. В идеале, если я выберу вкладку 2, тогда выберите индекс прокрутки внутри dgvLogs, где должно быть someInbetweenlog. Но, к сожалению, прокрутка вкладки 2 не движется в соответствии с изменениями, внесенными в код позади ..

Если я использую элемент управления вкладками по умолчанию, то есть TabControl вместо ExTabControl, тогда он работает нормально, как и ожидалось. но если я переместу прокрутку в любой из вкладок на dgvLogs, то это отражается и на других вкладках ..

Пожалуйста, добавьте комментарий Я опубликую больше кода, если потребуется.

РЕДАКТИРОВАТЬ 2

Я создал пример приложения, в котором я пытался продемонстрировать проблему. В этом приложении я добавил контекстное меню для сетки во вкладке и с помощью опции Sync я пытаюсь прокрутить, чтобы увидеть, где был найден первый соответствующий журнал с закрытым выбранным журналом, в других открытых вкладках.

Проблема: ExTabControl невозможно перейти к необходимому элементу журнала в другой открытой вкладке.

https://github.com/ankushmadankar/StackOverflow54198246/

Ответы [ 2 ]

0 голосов
/ 02 февраля 2019

Если я использую элемент управления вкладками по умолчанию, т.е. TabControl вместо ExTabControl, тогда он работает нормально, как и ожидалось. Но если я переместу прокрутку в любой из вкладок на dgvLogs, то это отражается и на других вкладках.

Существует два варианта использования TabControl, распространяющихся на этот пост :

  1. Когда мы связываем 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>
0 голосов
/ 23 января 2019

Вам нужно вывести TabControl, как описано в этом ответе. Используя эту технику, визуальное дерево для каждой вкладки будет сохранено.

Обратите внимание, что если у вас много вкладок, кэширование будет оказывать значительное влияние на производительность. Я бы рекомендовал использовать его максимум для 10 вкладок.

...