Table of Contents

VTC (View Template Comparer) Enhanced Design Document

Overview

This document outlines the enhanced design for the VTC tool based on stakeholder feedback. The tool enables comparison, selective merging, and conflict resolution of Revit view template settings with a UI that mirrors Revit's native Visibility/Graphics dialog organization.


1. Category Organization - Revit VG Tab Structure

Design Decision

Category organization will exactly mimic Revit's Visibility/Graphics dialog tabs.

Tab Structure

Tab CategoryType Filter Notes
Model Categories CategoryType.Model Exclude imported categories (Id > 0 with no BuiltInCategory)
Annotation Categories CategoryType.Annotation Tags, dimensions, text, symbols
Analytical Model Categories CategoryType.AnalyticalModel Structural analytical elements
Imported Categories CategoryType.Model Where Category.Id.IntegerValue > 0 and no valid BuiltInCategory
Filters N/A View filters (ParameterFilterElement)
Revit Links N/A Linked RVT files - spawns child window
Worksets N/A Conditional - only if model uses worksets
Design Options N/A Conditional - only if design options exist

Implementation

public enum VtcCategoryTab
{
    ModelCategories,
    AnnotationCategories,
    AnalyticalModelCategories,
    ImportedCategories,
    Filters,
    RevitLinks,
    Worksets,
    DesignOptions
}

public static class VtcCategoryClassifier
{
    public static VtcCategoryTab GetTab(Category category)
    {
        // Check for imported category first (Model type but not built-in)
        if (category.CategoryType == CategoryType.Model)
        {
            var idValue = ElementIdCompat.GetValue(category.Id);
            if (idValue > 0 && !IsBuiltInCategory(idValue))
                return VtcCategoryTab.ImportedCategories;
            return VtcCategoryTab.ModelCategories;
        }

        return category.CategoryType switch
        {
            CategoryType.Annotation => VtcCategoryTab.AnnotationCategories,
            CategoryType.AnalyticalModel => VtcCategoryTab.AnalyticalModelCategories,
            _ => VtcCategoryTab.ModelCategories // Fallback
        };
    }

    private static bool IsBuiltInCategory(long categoryId)
    {
        return Enum.IsDefined(typeof(BuiltInCategory), (int)categoryId);
    }
}

Data Model Changes

// Enhanced category data with tab classification
public sealed record VtcCategoryGraphicsData(
    long CategoryId,
    string Name,
    bool IsVisible,
    VtcOverrideGraphicsData Overrides,
    VtcCategoryTab Tab);  // NEW: Tab classification

// Enhanced tree node with tab grouping
public enum VtcDiffNodeType
{
    Root,           // Top-level root
    Tab,            // NEW: VG Tab (Model Categories, Annotation, etc.)
    CategoryGroup,  // Parent category with subcategories
    Setting         // Leaf node with actual value
}

2. Containerized Diff View Component

Design Decision

The diff view will be containerized as a reusable UserControl so it can be embedded in:

  1. The main VTC window
  2. Child windows for Linked File overrides
  3. Future comparison contexts

Architecture

VtcDiffViewControl (UserControl)
├── CategoryTabControl (TabControl)
│   ├── Model Categories Tab
│   ├── Annotation Categories Tab
│   ├── Analytical Model Categories Tab
│   ├── Imported Categories Tab
│   └── Filters Tab
├── Side-by-side TreeViews (Left/Right)
├── Selection Controls
└── Action Bar (Apply/Merge buttons)

Child Window Pattern for Linked Files

public sealed class VtcLinkedFileWindow : DbtWindowBase
{
    // Reuses the same VtcDiffViewControl
    public VtcLinkedFileWindow(
        RevitLinkInstance linkInstance,
        VtcTemplateSettingsModel leftTemplate,
        VtcTemplateSettingsModel rightTemplate)
    {
        Title = $"Linked File Overrides: {linkInstance.Name}";
        Content = new VtcDiffViewControl
        {
            DataContext = new VtcLinkedFileDiffViewModel(
                linkInstance, leftTemplate, rightTemplate)
        };
    }
}

Component Interface

public interface IVtcDiffViewHost
{
    /// <summary>
    /// The comparison result to display.
    /// </summary>
    VtcComparisonResult Comparison { get; }

    /// <summary>
    /// Tree roots organized by VG tab structure.
    /// </summary>
    ObservableCollection<VtcDiffTreeNode> DiffTreeRoots { get; }

    /// <summary>
    /// Currently selected tab.
    /// </summary>
    VtcCategoryTab SelectedTab { get; set; }

    /// <summary>
    /// Collect all current selections.
    /// </summary>
    IReadOnlyCollection<VtcSelection> CollectSelections();

    /// <summary>
    /// Apply selections and return conflict report.
    /// </summary>
    VtcApplyResult ApplySelections(VtcTemplateSide targetSide);
}

3. Selection UI - Single and Nested Selection

Design Decision

Provide dual-action selection controls for both single-item and cascading (single + all nested) selection/deselection.

UI Pattern: Dual Checkbox Column

┌─────────────────────────────────────────────────────────────────┐
│ Setting                    │ Sel │ All │  Value (Template A)   │
├─────────────────────────────────────────────────────────────────┤
│ ▼ Structural Framing       │ [☑] │ [☑] │                       │
│    ├─ Visibility           │ [☑] │     │  Visible              │
│    ├─ Line Color           │ [☑] │     │  RGB(0,0,255)         │
│    └─ Line Weight          │ [ ] │     │  3                    │
│ ▼ Walls                    │ [ ] │ [ ] │                       │
│    ├─ Visibility           │ [ ] │     │  Visible              │
│    └─ Cut Pattern          │ [ ] │     │  Solid Fill           │
└─────────────────────────────────────────────────────────────────┘

Legend:
[Sel] = Select this single item only
[All] = Select this item AND all nested children (only shown for parent nodes)

Data Model Extension

public sealed class VtcDiffTreeNode : INotifyPropertyChanged
{
    // Existing properties...

    /// <summary>
    /// Select/deselect this single node only.
    /// </summary>
    public bool SelectLeft { get; set; }
    public bool SelectRight { get; set; }

    /// <summary>
    /// Select/deselect this node AND all descendants.
    /// Only applicable to non-leaf nodes (Root, Tab, CategoryGroup).
    /// </summary>
    public bool SelectLeftWithChildren
    {
        get => _selectLeftWithChildren;
        set
        {
            _selectLeftWithChildren = value;
            if (value)
                PropagateSelectionToChildren(VtcTemplateSide.Left, true);
            OnPropertyChanged();
        }
    }

    public bool SelectRightWithChildren
    {
        get => _selectRightWithChildren;
        set
        {
            _selectRightWithChildren = value;
            if (value)
                PropagateSelectionToChildren(VtcTemplateSide.Right, true);
            OnPropertyChanged();
        }
    }

    /// <summary>
    /// Shows whether this node can cascade selection (has children).
    /// </summary>
    public bool CanCascadeSelection => Children.Count > 0;

    private void PropagateSelectionToChildren(VtcTemplateSide side, bool selected)
    {
        foreach (var child in Children)
        {
            if (side == VtcTemplateSide.Left)
                child.SelectLeft = selected;
            else
                child.SelectRight = selected;

            child.PropagateSelectionToChildren(side, selected);
        }
    }
}

Alternative UI Consideration: Context Menu

For cleaner UI, offer right-click context menu:

Right-click on "Structural Framing" →
┌─────────────────────────────────┐
│ ☑ Select (Template A)          │
│ ☐ Select (Template B)          │
├─────────────────────────────────┤
│ ☑ Select All Nested (A)        │
│ ☐ Select All Nested (B)        │
├─────────────────────────────────┤
│   Clear Selection              │
│   Clear All Nested             │
└─────────────────────────────────┘

A split button provides the cleanest UX:

┌───────────────────────────────────────────────────────────────────┐
│ Setting                    │ Use A          │  Value             │
├───────────────────────────────────────────────────────────────────┤
│ ▼ Structural Framing       │ [☑ ▾]          │                    │
│                            │  ├─ Select     │                    │
│                            │  └─ Select All │                    │
└───────────────────────────────────────────────────────────────────┘

The checkbox selects the single item; clicking the dropdown arrow shows "Select" / "Select All Children" options.


4. Merge Strategy - Disk-Based Workflow

Design Decision

  • Option A: Write changes to disk (JSON snapshots)
  • Create merged templates as new elements
  • Update existing templates based on selected properties

Workflow

┌─────────────────────────────────────────────────────────────────┐
│                        MERGE WORKFLOW                            │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  1. COMPARE                                                      │
│     ├─ Extract Template A settings → VtcTemplateSettingsModel   │
│     ├─ Extract Template B settings → VtcTemplateSettingsModel   │
│     └─ Generate diff tree with tab organization                  │
│                                                                  │
│  2. SELECT                                                       │
│     ├─ User picks settings from A or B per property             │
│     ├─ Single or cascading selection                            │
│     └─ Selections stored in VtcSelection list                   │
│                                                                  │
│  3. PREVIEW (Optional)                                          │
│     └─ Show merged result before applying                       │
│                                                                  │
│  4. APPLY                                                        │
│     ├─ Option: Apply to Template A (in-place update)            │
│     ├─ Option: Apply to Template B (in-place update)            │
│     └─ Option: Create New Template (merged copy)                │
│                                                                  │
│  5. PERSIST (Optional)                                          │
│     ├─ Save merged template snapshot to JSON                    │
│     └─ Store in Template Library for future use                 │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Merge Operation Types

public enum VtcMergeOperation
{
    /// <summary>
    /// Update Template A with selected settings from comparison.
    /// </summary>
    ApplyToLeft,

    /// <summary>
    /// Update Template B with selected settings from comparison.
    /// </summary>
    ApplyToRight,

    /// <summary>
    /// Create a new template by duplicating base and applying selections.
    /// </summary>
    CreateMerged,

    /// <summary>
    /// Export merged settings to JSON file (no Revit changes).
    /// </summary>
    ExportToJson
}

5. Conflict Handling and Apply Workflow

Design Decision

  1. Attempt all selected changes
  2. Report conflicts after apply attempt
  3. Retain selections (don't clear on partial failure)
  4. Highlight problematic selections
  5. Filter control to show/hide conflicts (hidden until apply attempted)

Conflict Types

public enum VtcConflictType
{
    /// <summary>
    /// No conflict - change applied successfully.
    /// </summary>
    None,

    /// <summary>
    /// Category/Filter doesn't exist in target document.
    /// </summary>
    ElementNotFound,

    /// <summary>
    /// Parameter is read-only or controlled by another template.
    /// </summary>
    ReadOnlyParameter,

    /// <summary>
    /// Value format incompatible (e.g., pattern ID doesn't exist).
    /// </summary>
    IncompatibleValue,

    /// <summary>
    /// Revit API threw an exception during apply.
    /// </summary>
    ApiError
}

public sealed record VtcConflictInfo(
    string SettingKey,
    VtcConflictType ConflictType,
    string Message,
    Exception? Exception);

public sealed record VtcApplyResult(
    int TotalAttempted,
    int SuccessCount,
    int FailedCount,
    IReadOnlyList<VtcConflictInfo> Conflicts);

Enhanced Tree Node with Conflict State

public sealed class VtcDiffTreeNode : INotifyPropertyChanged
{
    // Existing properties...

    /// <summary>
    /// Conflict information after apply attempt. Null if not yet attempted or no conflict.
    /// </summary>
    public VtcConflictInfo? Conflict { get; set; }

    /// <summary>
    /// Whether this node has a conflict after apply.
    /// </summary>
    public bool HasConflict => Conflict != null;

    /// <summary>
    /// Whether apply has been attempted on this node.
    /// </summary>
    public bool ApplyAttempted { get; set; }
}

UI State Machine

┌─────────────────────────────────────────────────────────────────┐
│                      UI STATE MACHINE                            │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  State: INITIAL                                                  │
│  ├─ Conflict filter: HIDDEN                                     │
│  ├─ Apply button: ENABLED                                       │
│  └─ Conflict highlights: NONE                                   │
│                                                                  │
│  ──[User clicks Apply]──────────────────────────────────────────│
│                                                                  │
│  State: APPLYING                                                 │
│  ├─ Conflict filter: HIDDEN                                     │
│  ├─ Apply button: DISABLED (with spinner)                       │
│  └─ Progress indicator shown                                    │
│                                                                  │
│  ──[Apply completes]────────────────────────────────────────────│
│                                                                  │
│  State: POST_APPLY (if any conflicts)                           │
│  ├─ Conflict filter: VISIBLE                                    │
│  │   ├─ [Show All]                                              │
│  │   ├─ [Show Conflicts Only]                                   │
│  │   └─ [Hide Conflicts]                                        │
│  ├─ Apply button: ENABLED (for retry)                           │
│  ├─ Conflict highlights: ACTIVE                                 │
│  │   ├─ Red border on conflicting rows                          │
│  │   ├─ Warning icon with tooltip                               │
│  │   └─ Conflict count badge                                    │
│  └─ Selections: RETAINED (user can modify and retry)            │
│                                                                  │
│  State: POST_APPLY (no conflicts)                               │
│  ├─ Conflict filter: HIDDEN                                     │
│  ├─ Success message shown                                       │
│  └─ Selections: CLEARED or RETAINED (configurable)              │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Conflict Filter Control

<!-- Only visible after apply has been attempted AND conflicts exist -->
<StackPanel Orientation="Horizontal"
            Visibility="{Binding ShowConflictFilter, Converter={StaticResource BoolToVisibility}}">

    <TextBlock Text="Filter:" Margin="0,0,8,0" VerticalAlignment="Center" />

    <RadioButton Content="All"
                 IsChecked="{Binding ConflictFilter, Converter={StaticResource EnumToBool},
                            ConverterParameter=All}"
                 GroupName="ConflictFilter" />

    <RadioButton Content="Conflicts Only"
                 IsChecked="{Binding ConflictFilter, Converter={StaticResource EnumToBool},
                            ConverterParameter=ConflictsOnly}"
                 GroupName="ConflictFilter" />

    <RadioButton Content="Successful Only"
                 IsChecked="{Binding ConflictFilter, Converter={StaticResource EnumToBool},
                            ConverterParameter=SuccessfulOnly}"
                 GroupName="ConflictFilter" />

    <!-- Conflict count badge -->
    <Border Background="{DynamicResource Error}" CornerRadius="10"
            Padding="6,2" Margin="12,0,0,0"
            Visibility="{Binding HasConflicts, Converter={StaticResource BoolToVisibility}}">
        <TextBlock Text="{Binding ConflictCount, StringFormat='{0} conflicts'}"
                   Foreground="White" FontSize="11" />
    </Border>
</StackPanel>

Conflict Highlighting in TreeView

<!-- Enhanced tree node template with conflict indication -->
<Style x:Key="Vtc.ConflictBorder" TargetType="Border">
    <Style.Triggers>
        <DataTrigger Binding="{Binding HasConflict}" Value="True">
            <Setter Property="BorderBrush" Value="{DynamicResource Error}" />
            <Setter Property="BorderThickness" Value="2" />
            <Setter Property="Background" Value="{DynamicResource ErrorBackground}" />
        </DataTrigger>
    </Style.Triggers>
</Style>

<!-- Conflict warning icon -->
<TextBlock x:Name="ConflictIcon"
           FontFamily="Segoe MDL2 Assets"
           Text="&#xE7BA;"
           Foreground="{DynamicResource Error}"
           ToolTip="{Binding Conflict.Message}"
           Visibility="{Binding HasConflict, Converter={StaticResource BoolToVisibility}}" />

6. Complete ViewModel Structure

public sealed partial class VtcWindowViewModel : ObservableObject, IVtcDiffViewHost
{
    // Template selection
    [ObservableProperty] private VtcTemplateOption? _selectedLeft;
    [ObservableProperty] private VtcTemplateOption? _selectedRight;

    // Tab navigation (mirrors Revit VG tabs)
    [ObservableProperty] private VtcCategoryTab _selectedTab = VtcCategoryTab.ModelCategories;
    public ObservableCollection<VtcCategoryTab> AvailableTabs { get; }

    // Diff tree organized by tabs
    public ObservableCollection<VtcDiffTreeNode> DiffTreeRoots { get; }

    // Conflict management (hidden until apply attempted)
    [ObservableProperty] private bool _applyAttempted;
    [ObservableProperty] private VtcConflictFilter _conflictFilter = VtcConflictFilter.All;
    [ObservableProperty] private VtcApplyResult? _lastApplyResult;

    public bool ShowConflictFilter => ApplyAttempted && (LastApplyResult?.FailedCount ?? 0) > 0;
    public bool HasConflicts => (LastApplyResult?.FailedCount ?? 0) > 0;
    public int ConflictCount => LastApplyResult?.FailedCount ?? 0;

    // Linked files (spawns child windows)
    public ObservableCollection<VtcLinkedFileInfo> LinkedFiles { get; }

    // Commands
    [RelayCommand] private void ApplyToLeft() => ApplyWithConflictHandling(VtcTemplateSide.Left);
    [RelayCommand] private void ApplyToRight() => ApplyWithConflictHandling(VtcTemplateSide.Right);
    [RelayCommand] private void OpenLinkedFileOverrides(VtcLinkedFileInfo link) => SpawnLinkedFileWindow(link);
    [RelayCommand] private void SelectAllInTab() => SelectAllNodesInCurrentTab(true);
    [RelayCommand] private void DeselectAllInTab() => SelectAllNodesInCurrentTab(false);

    private void ApplyWithConflictHandling(VtcTemplateSide targetSide)
    {
        var selections = CollectSelections();
        var result = _comparer.ApplySelectionsWithReport(target, _comparison!, selections);

        ApplyAttempted = true;
        LastApplyResult = result;

        // Update tree nodes with conflict info
        foreach (var conflict in result.Conflicts)
        {
            var node = FindNodeByKey(conflict.SettingKey);
            if (node != null)
            {
                node.Conflict = conflict;
                node.ApplyAttempted = true;
            }
        }

        // Mark successful nodes
        foreach (var selection in selections)
        {
            if (!result.Conflicts.Any(c => c.SettingKey == selection.Key))
            {
                var node = FindNodeByKey(selection.Key);
                if (node != null)
                {
                    node.Conflict = null;
                    node.ApplyAttempted = true;
                }
            }
        }

        OnPropertyChanged(nameof(ShowConflictFilter));
        OnPropertyChanged(nameof(HasConflicts));
        OnPropertyChanged(nameof(ConflictCount));

        ShowApplyResultSummary(result);
    }
}

public enum VtcConflictFilter
{
    All,
    ConflictsOnly,
    SuccessfulOnly
}

7. File Structure After Implementation

VTC/
├── Documentation/
│   └── VTC_Enhanced_Design.md          # This document
├── Contracts/
│   ├── VtcDataModels.cs                # Enhanced with Tab classification
│   ├── VtcEnums.cs                     # Add VtcCategoryTab, VtcConflictType
│   ├── IVtcComparisonService.cs        # Add ApplySelectionsWithReport
│   └── VtcConflictModels.cs            # NEW: Conflict handling types
├── UI/
│   ├── Controls/
│   │   └── VtcDiffViewControl.xaml     # NEW: Reusable diff view control
│   ├── ViewModels/
│   │   ├── VtcWindowViewModel.cs       # Enhanced with conflict handling
│   │   ├── VtcDiffTreeNode.cs          # Enhanced with conflict state
│   │   ├── VtcDiffTreeBuilder.cs       # Enhanced with tab organization
│   │   ├── VtcLinkedFileDiffViewModel.cs # NEW: For child windows
│   │   └── VtcCategoryClassifier.cs    # NEW: Tab classification logic
│   ├── Views/
│   │   ├── VtcWindow.xaml              # Main window (uses VtcDiffViewControl)
│   │   └── VtcLinkedFileWindow.xaml    # NEW: Child window for linked files
│   ├── Converters/
│   │   ├── VtcDiffBackgroundConverter.cs
│   │   ├── VtcConflictToVisibilityConverter.cs  # NEW
│   │   └── VtcCategoryTabToIconConverter.cs     # NEW
│   └── Behaviors/
│       └── ScrollSyncBehavior.cs
├── Revit/
│   └── Services/
│       ├── VtcComparisonService.cs     # Enhanced with conflict reporting
│       └── VtcCategoryClassifier.cs    # NEW: Revit-specific classification
└── Services/
    └── VtcTemplateStorageService.cs

8. Implementation Priority

Priority Feature Complexity Dependencies
1 Tab-based category organization Medium VtcCategoryClassifier
2 Dual selection (single/nested) Medium VtcDiffTreeNode changes
3 Conflict handling workflow High Apply result reporting
4 Containerized diff view control Medium Extract from VtcWindow
5 Linked file child windows Medium VtcDiffViewControl
6 Conflict filter UI Low After conflict handling

9. Linked File Extraction (RevitLinkType Overrides)

Design Decision

Extract and display RevitLinkType visibility overrides from view templates, enabling full comparison and merging of linked file settings.

Revit API Integration

/// <summary>
/// Data model for linked file visibility overrides within a view template.
/// </summary>
public sealed record VtcLinkedFileOverrideData(
    long LinkTypeId,
    string LinkName,
    string FilePath,
    bool IsVisible,
    VtcLinkedFileVisibility LinkedViewId,  // None, ByHostView, or specific view ID
    IReadOnlyList<VtcCategoryGraphicsData> CategoryOverrides);

public enum VtcLinkedFileVisibility
{
    None,       // Link hidden
    ByHostView, // Use host view's VG settings
    Custom      // Uses stored view ID for overrides
}

Extraction Implementation

public List<VtcLinkedFileOverrideData> ExtractLinkedFileOverrides(View template)
{
    var results = new List<VtcLinkedFileOverrideData>();

    // Get all RevitLinkType elements in the document
    var linkTypes = new FilteredElementCollector(_document)
        .OfClass(typeof(RevitLinkType))
        .Cast<RevitLinkType>();

    foreach (var linkType in linkTypes)
    {
        var linkId = linkType.Id;

        // Get visibility state from template
        var isVisible = !template.GetCategoryHidden(linkId);

        // Get linked view override (determines how link displays)
        var linkedViewId = template.GetLinkedOverrideViewId(linkId);
        var visibility = linkedViewId == ElementId.InvalidElementId
            ? VtcLinkedFileVisibility.ByHostView
            : VtcLinkedFileVisibility.Custom;

        // Extract category-level overrides for this link
        var categoryOverrides = ExtractLinkedCategoryOverrides(template, linkType);

        results.Add(new VtcLinkedFileOverrideData(
            ElementIdCompat.GetValue(linkId),
            linkType.Name,
            linkType.GetExternalResourceReference()?.GetReferenceInformation()?.GetPath() ?? string.Empty,
            isVisible,
            visibility,
            categoryOverrides));
    }

    return results;
}

10. Worksets & Design Options Support

Design Decision

Conditionally display Worksets and Design Options tabs when the model contains these features.

Detection Logic

public bool HasWorksets(Document doc) => doc.IsWorkshared;

public bool HasDesignOptions(Document doc)
{
    return new FilteredElementCollector(doc)
        .OfClass(typeof(DesignOption))
        .Any();
}

Data Models

/// <summary>
/// Workset visibility override data.
/// </summary>
public sealed record VtcWorksetOverrideData(
    long WorksetId,
    string Name,
    WorksetVisibility Visibility);  // Visible, Hidden, UseGlobalSetting

/// <summary>
/// Design option visibility override data.
/// </summary>
public sealed record VtcDesignOptionOverrideData(
    long DesignOptionId,
    string Name,
    string SetName,  // Parent design option set
    bool IsActive);

Tab Visibility Control

// In VtcWindowViewModel
public bool ShowWorksetsTab => _hasWorksets;
public bool ShowDesignOptionsTab => _hasDesignOptions;

// Initialize during comparison
private void CheckConditionalTabs()
{
    _hasWorksets = _documentInfo.IsWorkshared;
    _hasDesignOptions = new FilteredElementCollector(_document)
        .OfClass(typeof(DesignOption))
        .Any();

    OnPropertyChanged(nameof(ShowWorksetsTab));
    OnPropertyChanged(nameof(ShowDesignOptionsTab));
}

11. Search/Filter by Name

Design Decision

Provide a text search box that filters settings by name across all categories in real-time.

UI Layout

┌─────────────────────────────────────────────────────────────────┐
│ [🔍] Search settings...                         [Clear] [×]     │
├─────────────────────────────────────────────────────────────────┤
│ ▼ Model Categories (3 matches)                                  │
│   ├─ Structural Framing        [☑] Visible    [☐] Hidden       │
│   └─ Structural Columns        [☑] Visible    [☐] Hidden       │
│ ▼ Annotation Categories (1 match)                               │
│   └─ Structural Framing Tags   [☑] Visible    [☐] Visible      │
└─────────────────────────────────────────────────────────────────┘

Implementation

[ObservableProperty]
[NotifyPropertyChangedFor(nameof(FilteredDiffTreeRoots))]
private string _searchText = string.Empty;

public IEnumerable<VtcDiffTreeNode> FilteredDiffTreeRoots
{
    get
    {
        var roots = GetConflictFilteredRoots();

        if (string.IsNullOrWhiteSpace(SearchText))
            return roots;

        return roots
            .Select(root => FilterNodeBySearch(root, SearchText))
            .Where(node => node != null)!;
    }
}

private static VtcDiffTreeNode? FilterNodeBySearch(VtcDiffTreeNode node, string searchText)
{
    // Check if this node's name matches
    var nameMatches = node.DisplayName.Contains(searchText, StringComparison.OrdinalIgnoreCase);

    // Filter children recursively
    var filteredChildren = node.Children
        .Select(c => FilterNodeBySearch(c, searchText))
        .Where(c => c != null)
        .ToList();

    // Include node if name matches OR has matching children
    if (!nameMatches && filteredChildren.Count == 0)
        return null;

    // Create filtered copy
    var filteredNode = new VtcDiffTreeNode(node.DisplayName, node.NodeType)
    {
        Change = node.Change,
        CategoryTab = node.CategoryTab
    };

    foreach (var child in filteredChildren)
        filteredNode.Children.Add(child!);

    filteredNode.IsExpanded = true;  // Auto-expand when filtering
    return filteredNode;
}

[RelayCommand]
private void ClearSearch() => SearchText = string.Empty;

12. Copy Linked File Overrides

Design Decision

Enable copying visibility overrides from one linked file to another within the same view template.

Workflow

┌─────────────────────────────────────────────────────────────────┐
│ Copy Linked File Overrides                                       │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  Source Link:  [▼ Architectural.rvt                    ]        │
│                                                                  │
│  Target Link:  [▼ Structural.rvt                       ]        │
│                                                                  │
│  Options:                                                        │
│  [☑] Copy visibility state                                      │
│  [☑] Copy category overrides                                    │
│  [☑] Copy linked view reference                                 │
│                                                                  │
│                              [Cancel]  [Copy Overrides]          │
└─────────────────────────────────────────────────────────────────┘

Implementation

public sealed record VtcCopyLinkedOverridesOptions(
    long SourceLinkTypeId,
    long TargetLinkTypeId,
    bool CopyVisibility,
    bool CopyCategoryOverrides,
    bool CopyLinkedViewReference);

public void CopyLinkedFileOverrides(View template, VtcCopyLinkedOverridesOptions options)
{
    var sourceId = new ElementId(options.SourceLinkTypeId);
    var targetId = new ElementId(options.TargetLinkTypeId);

    using var tx = new Transaction(_document, "Copy Linked File Overrides");
    tx.Start();

    if (options.CopyVisibility)
    {
        var isHidden = template.GetCategoryHidden(sourceId);
        template.SetCategoryHidden(targetId, isHidden);
    }

    if (options.CopyCategoryOverrides)
    {
        var ogs = template.GetCategoryOverrides(sourceId);
        template.SetCategoryOverrides(targetId, ogs);
    }

    if (options.CopyLinkedViewReference)
    {
        var linkedViewId = template.GetLinkedOverrideViewId(sourceId);
        template.SetLinkedOverrideViewId(targetId, linkedViewId);
    }

    tx.Commit();
}

13. Interactive Diff View with Inline Override Controls

Design Decision

Mirror Revit's Visibility/Graphics dialog by providing inline controls for each category row, allowing direct editing of override settings.

Row Control Layout

┌─────────────────────────────────────────────────────────────────────────────────┐
│ Category Name    │ Projection/Surface         │ Cut           │ HT │ Detail    │
│                  │ [Lines] [Patterns] [Trans] │ [Lines] [Pat] │ [☐]│ [▼ Fine ] │
├─────────────────────────────────────────────────────────────────────────────────┤
│ Structural       │  [⚫]    [◻]       [50% ]  │  [⚫]   [◻]   │ [☐]│ [▼ Med  ] │
│ Framing          │                            │               │    │           │
└─────────────────────────────────────────────────────────────────────────────────┘

Legend:
[⚫] = Lines button (opens Line Settings dialog)
[◻]  = Patterns button (opens Fill Pattern dialog)
[50%] = Transparency button (opens Transparency slider dialog)
[☐] = Halftone checkbox (direct toggle)
[▼] = Detail Level dropdown (Coarse/Medium/Fine)

Control Types

Control Type Dialog
Projection Lines Button Line Settings Dialog
Projection Patterns Button Fill Pattern Dialog
Transparency Button Transparency Slider Dialog
Cut Lines Button Line Settings Dialog
Cut Patterns Button Fill Pattern Dialog
Halftone Checkbox Inline (no dialog)
Detail Level ComboBox Inline dropdown

14. Override Editing Dialogs

14.1 Line Settings Dialog

A compact modal for configuring line overrides.

┌─────────────────────────────────────────────────┐
│ Line Settings                              [×]  │
├─────────────────────────────────────────────────┤
│                                                 │
│  Preview:  ────────────────────────             │
│            (live preview of current settings)   │
│                                                 │
│  Weight:   [▼ 3                              ]  │
│                                                 │
│  Color:    [■ Blue     ] [🎨]                   │
│                                                 │
│  Pattern:  [▼ Solid                          ]  │
│                                                 │
│            [Reset to Default]                   │
│                                                 │
│                      [Cancel]  [Apply]          │
└─────────────────────────────────────────────────┘

Data Model:

public sealed record VtcLineSettingsData
{
    public int? LineWeight { get; init; }
    public VtcColorData? LineColor { get; init; }
    public long? LinePatternId { get; init; }
    public string? LinePatternName { get; init; }  // For display
}

14.2 Fill Pattern Dialog

A compact modal for configuring foreground/background fill patterns.

┌─────────────────────────────────────────────────┐
│ Fill Pattern Settings                      [×]  │
├─────────────────────────────────────────────────┤
│                                                 │
│  Preview:  ┌──────────────────┐                 │
│            │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│                 │
│            │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒│                 │
│            └──────────────────┘                 │
│                                                 │
│  ─── Foreground ───                             │
│  [☑] Visible                                    │
│  Pattern: [▼ Crosshatch                      ]  │
│  Color:   [■ Gray      ] [🎨]                   │
│                                                 │
│  ─── Background ───                             │
│  [☑] Visible                                    │
│  Pattern: [▼ Solid Fill                      ]  │
│  Color:   [■ White     ] [🎨]                   │
│                                                 │
│            [Reset to Default]                   │
│                                                 │
│                      [Cancel]  [Apply]          │
└─────────────────────────────────────────────────┘

Data Model:

public sealed record VtcFillPatternData
{
    public bool ForegroundVisible { get; init; }
    public long? ForegroundPatternId { get; init; }
    public string? ForegroundPatternName { get; init; }
    public VtcColorData? ForegroundColor { get; init; }

    public bool BackgroundVisible { get; init; }
    public long? BackgroundPatternId { get; init; }
    public string? BackgroundPatternName { get; init; }
    public VtcColorData? BackgroundColor { get; init; }
}

14.3 Transparency Dialog

A minimal modal with just a slider control.

┌─────────────────────────────────────────────────┐
│ Transparency                               [×]  │
├─────────────────────────────────────────────────┤
│                                                 │
│  Preview:  [░░░░░░░░░░░░░░░░]  50%              │
│                                                 │
│  [────────────●────────────] 0    50   100      │
│                                                 │
│                      [Cancel]  [Apply]          │
└─────────────────────────────────────────────────┘

Data Model:

public sealed record VtcTransparencyData(int Transparency);  // 0-100

15. Dialog Implementation with AlertService

Common Dialog Pattern

public sealed class VtcLineSettingsBodyViewModel : ObservableObject, IAlertBody
{
    private int? _lineWeight;
    private VtcColorData? _lineColor;
    private long? _linePatternId;

    public VtcLineSettingsBodyViewModel(
        VtcLineSettingsData initialData,
        IReadOnlyList<VtcLinePatternInfo> availablePatterns,
        IReadOnlyList<int> availableWeights)
    {
        _lineWeight = initialData?.LineWeight;
        _lineColor = initialData?.LineColor;
        _linePatternId = initialData?.LinePatternId;

        AvailablePatterns = availablePatterns;
        AvailableWeights = availableWeights;
    }

    public int? LineWeight
    {
        get => _lineWeight;
        set => SetProperty(ref _lineWeight, value);
    }

    public VtcColorData? LineColor
    {
        get => _lineColor;
        set => SetProperty(ref _lineColor, value);
    }

    public long? LinePatternId
    {
        get => _linePatternId;
        set => SetProperty(ref _linePatternId, value);
    }

    public IReadOnlyList<VtcLinePatternInfo> AvailablePatterns { get; }
    public IReadOnlyList<int> AvailableWeights { get; }

    // IAlertBody implementation
    public bool IsValid => true;  // Always valid, changes are optional
    public event EventHandler? ValidityChanged;

    public object? GetResult() => new VtcLineSettingsData
    {
        LineWeight = LineWeight,
        LineColor = LineColor,
        LinePatternId = LinePatternId
    };
}

Dialog Invocation

public VtcLineSettingsData? ShowLineSettingsDialog(
    VtcLineSettingsData? current,
    string settingName)
{
    var body = new VtcLineSettingsBodyViewModel(
        current,
        GetAvailableLinePatterns(),
        GetAvailableLineWeights());

    var request = new AlertRequest($"Line Settings: {settingName}", body)
    {
        Variant = AlertVariant.Info,
        Options = new AlertWindowOptions
        {
            Width = 350,
            Height = 280,
            MinWidth = 300,
            MinHeight = 250
        },
        Buttons = new[]
        {
            new AlertButtonSpec("cancel", "Cancel") { IsCancel = true },
            new AlertButtonSpec("apply", "Apply") { IsDefault = true, SetDialogResult = true }
        }
    };

    var result = _alerts.Show(request);

    return result.ClickedButtonId == "apply"
        ? result.Payload as VtcLineSettingsData
        : null;
}

16. Visual Preview Components

Line Preview Control

<!-- Preview showing line weight, color, and pattern -->
<Canvas Height="20" Width="200" ClipToBounds="True">
    <Line X1="10" Y1="10" X2="190" Y2="10"
          Stroke="{Binding PreviewBrush}"
          StrokeThickness="{Binding LineWeight}"
          StrokeDashArray="{Binding PatternDashes}" />
</Canvas>

Fill Pattern Preview Control

<!-- Preview showing foreground over background pattern -->
<Grid Width="100" Height="60">
    <!-- Background pattern -->
    <Rectangle Fill="{Binding BackgroundBrush}"
               Visibility="{Binding BackgroundVisible, Converter={StaticResource BoolToVis}}" />
    <!-- Foreground pattern overlay -->
    <Rectangle Fill="{Binding ForegroundBrush}"
               Visibility="{Binding ForegroundVisible, Converter={StaticResource BoolToVis}}" />
</Grid>

Transparency Preview Control

<!-- Checkerboard background with semi-transparent overlay -->
<Grid Width="100" Height="30">
    <Rectangle>
        <Rectangle.Fill>
            <DrawingBrush TileMode="Tile" Viewport="0,0,10,10" ViewportUnits="Absolute">
                <DrawingBrush.Drawing>
                    <GeometryDrawing Brush="LightGray">
                        <GeometryDrawing.Geometry>
                            <GeometryGroup>
                                <RectangleGeometry Rect="0,0,5,5" />
                                <RectangleGeometry Rect="5,5,5,5" />
                            </GeometryGroup>
                        </GeometryDrawing.Geometry>
                    </GeometryDrawing>
                </DrawingBrush.Drawing>
            </DrawingBrush>
        </Rectangle.Fill>
    </Rectangle>
    <Rectangle Fill="{DynamicResource Primary}"
               Opacity="{Binding TransparencyDecimal}" />
</Grid>

17. Updated File Structure

VTC/
├── Documentation/
│   └── VTC_Enhanced_Design.md          # This document
├── Contracts/
│   ├── VtcDataModels.cs                # Enhanced with linked files, worksets, design options
│   ├── VtcEnums.cs                     # Add VtcLinkedFileVisibility
│   ├── IVtcComparisonService.cs
│   └── VtcOverrideEditingModels.cs     # NEW: Line, pattern, transparency data
├── UI/
│   ├── Controls/
│   │   ├── VtcDiffViewControl.xaml     # Enhanced with search and inline controls
│   │   ├── VtcLinePreviewControl.xaml  # NEW: Line preview
│   │   ├── VtcPatternPreviewControl.xaml # NEW: Pattern preview
│   │   └── VtcTransparencyPreviewControl.xaml # NEW: Transparency preview
│   ├── ViewModels/
│   │   ├── VtcWindowViewModel.cs       # Enhanced with search, conditional tabs
│   │   ├── VtcDiffTreeNode.cs          # Enhanced with edit commands
│   │   ├── VtcDiffTreeBuilder.cs       # Enhanced with linked files, worksets
│   │   ├── VtcLinkedFileDiffViewModel.cs
│   │   ├── VtcLineSettingsBodyViewModel.cs    # NEW: Line dialog body
│   │   ├── VtcFillPatternBodyViewModel.cs     # NEW: Pattern dialog body
│   │   └── VtcTransparencyBodyViewModel.cs    # NEW: Transparency dialog body
│   ├── Views/
│   │   ├── VtcWindow.xaml              # Main window with search
│   │   ├── VtcLinkedFileWindow.xaml
│   │   └── VtcCopyLinkedOverridesWindow.xaml  # NEW: Copy dialog
│   ├── Converters/
│   │   ├── VtcDiffBackgroundConverter.cs
│   │   ├── VtcEnumToBoolConverter.cs
│   │   └── VtcPatternToBrushConverter.cs      # NEW: Pattern preview
│   └── Behaviors/
│       └── ScrollSyncBehavior.cs
├── Revit/
│   └── Services/
│       ├── VtcComparisonService.cs     # Enhanced with linked files, worksets, design options
│       └── VtcCategoryClassifier.cs
└── Services/
    └── VtcTemplateStorageService.cs

18. Implementation Priority (Updated)

Priority Feature Complexity Dependencies
1 Search/Filter by Name Low None
2 Linked File Extraction High Revit API integration
3 Worksets & Design Options Medium Conditional tab visibility
4 Copy Linked File Overrides Medium Linked file extraction
5 Inline Override Controls High All dialogs
6 Line Settings Dialog Medium Preview controls
7 Fill Pattern Dialog Medium Preview controls
8 Transparency Dialog Low Preview control

References