mirror of
https://github.com/Micke-K/IntuneManagement.git
synced 2026-09-28 19:05:38 +02:00
431 lines
22 KiB
PowerShell
431 lines
22 KiB
PowerShell
# Avalonia port of the Bulk Compare dialog from
|
|
# UI/WPF/Extensions/CompareUI.ps1 (Show-GraphBulkCompareForm + helpers).
|
|
# New file because Bulk Compare is a self-contained subsystem and the WPF
|
|
# original sits in CompareUI.ps1 alongside the single-policy compare; we
|
|
# already split that into Extensions/IntuneManagerCompareUI.ps1 (architecture
|
|
# rule R9, R12).
|
|
#
|
|
# Slice 4f: full Bulk Compare form for the four "list-driven" providers
|
|
# (export / IntuneWithExport / name / exportedFolders). A fifth, Direct
|
|
# policy-pair provider was removed in 2026-09 - a two-object compare belongs
|
|
# in the single Compare form, which already picks a second policy.
|
|
#
|
|
# Helpers used (all in Internal/Compare.ps1, loaded in both UI backends):
|
|
# Set-CompareRuntimeOptions, Get-BulkCompareOutputProvider,
|
|
# Get-CompareOutputType, Start-BulkCompare.
|
|
# Provider classes (Classes/CompareClasses.ps1) inherit CompareProviderBase
|
|
# and have real CLR `[string]Name` / `[string]Value` properties + Validate /
|
|
# SaveSettings / GetComparePairs methods, so they bind directly to control
|
|
# DataContext for the per-provider option panels' TwoWay TextBox bindings.
|
|
|
|
function Show-GraphBulkCompareForm
|
|
{
|
|
$ui = $script:UIProvider
|
|
$form = $ui.GetXamlObject((Join-Path $script:AppUIRootFolder 'XAML/BulkCompareForm.axaml'))
|
|
if (-not $form) { return }
|
|
|
|
$hostType = Get-AvaloniaHost
|
|
$cbProvider = $hostType::FindByName($form, 'cbCompareProvider')
|
|
$ccProviderOpts = $hostType::FindByName($form, 'ccContentProviderOptions')
|
|
$cbSave = $hostType::FindByName($form, 'cbCompareSave')
|
|
$cbOutFmt = $hostType::FindByName($form, 'cbCompareOutputFormat')
|
|
$cbType = $hostType::FindByName($form, 'cbCompareType')
|
|
$cbDelimiter = $hostType::FindByName($form, 'cbCompareCSVDelimiter')
|
|
$cbMultiValueDelim = $hostType::FindByName($form, 'cbCompareMultiValueDelimiter')
|
|
$chkIgnoreCore = $hostType::FindByName($form, 'chkIgnoreCoreProperties')
|
|
$chkSkipAssignments = $hostType::FindByName($form, 'chkSkipCompareAssignments')
|
|
$chkSkipMissingSrc = $hostType::FindByName($form, 'chkSkipMissingSourcePolicies')
|
|
$chkSkipMissingDst = $hostType::FindByName($form, 'chkSkipMissingDestinationPolicies')
|
|
$grdObjects = $hostType::FindByName($form, 'grdObjectsToCompareSection')
|
|
$dgObjects = $hostType::FindByName($form, 'dgObjectsToCompare')
|
|
$txtStatus = $hostType::FindByName($form, 'txtBulkCompareStatus')
|
|
$btnStart = $hostType::FindByName($form, 'btnStartCompare')
|
|
$btnClose = $hostType::FindByName($form, 'btnClose')
|
|
|
|
# Panel cache + per-provider Value->instance maps + control refs. Event
|
|
# handlers reach ALL of this through $script:_bulkCompareFormState -
|
|
# closure captures of function locals do NOT survive
|
|
# ConvertTo-AvaloniaEventScriptBlock (NewBoundScriptBlock rebinds to the
|
|
# module and drops them; only global/module variables fall through).
|
|
$state = [ordered]@{
|
|
ProviderPanelCache = @{}
|
|
ProviderMap = @{}
|
|
SaveMap = @{}
|
|
OutputProviderMap = @{}
|
|
TypeMap = @{}
|
|
Rows = @()
|
|
Ui = $ui
|
|
HostType = $hostType
|
|
CbProvider = $cbProvider
|
|
CcProviderOpts = $ccProviderOpts
|
|
CbSave = $cbSave
|
|
CbOutFmt = $cbOutFmt
|
|
CbType = $cbType
|
|
CbDelimiter = $cbDelimiter
|
|
CbMultiValueDelim = $cbMultiValueDelim
|
|
ChkIgnoreCore = $chkIgnoreCore
|
|
ChkSkipAssignments = $chkSkipAssignments
|
|
ChkSkipMissingSrc = $chkSkipMissingSrc
|
|
ChkSkipMissingDst = $chkSkipMissingDst
|
|
GrdObjects = $grdObjects
|
|
DgObjects = $dgObjects
|
|
TxtStatus = $txtStatus
|
|
BtnStart = $btnStart
|
|
BtnClose = $btnClose
|
|
}
|
|
$script:_bulkCompareFormState = $state
|
|
|
|
|
|
# --- Combo: Provider ---------------------------------------------------------
|
|
if ($cbProvider) {
|
|
$items = New-Object 'System.Collections.Generic.List[SettingsListItem]'
|
|
foreach ($p in @($script:compareProviders)) {
|
|
if (-not $p) { continue }
|
|
$items.Add([SettingsListItem]@{ Name = [string]$p.Name; Value = [string]$p.Value }) | Out-Null
|
|
$state.ProviderMap[[string]$p.Value] = $p
|
|
}
|
|
$cbProvider.ItemsSource = $items
|
|
$defaultProvider = (Get-SettingStoreValue "Compare" "Provider" "export")
|
|
$sel = $items | Where-Object { "$($_.Value)" -eq "$defaultProvider" } | Select-Object -First 1
|
|
if (-not $sel -and $items.Count -gt 0) { $sel = $items[0] }
|
|
if ($sel) { $cbProvider.SelectedItem = $sel }
|
|
}
|
|
|
|
# --- Combo: Save (one-file-per-type vs all) ---------------------------------
|
|
if ($cbSave) {
|
|
$items = New-Object 'System.Collections.Generic.List[SettingsListItem]'
|
|
foreach ($t in @($script:compareOutputTypes)) {
|
|
if (-not $t) { continue }
|
|
$items.Add([SettingsListItem]@{ Name = [string]$t.Name; Value = [string]$t.Value }) | Out-Null
|
|
$state.SaveMap[[string]$t.Value] = $t
|
|
}
|
|
$cbSave.ItemsSource = $items
|
|
$defaultSave = (Get-SettingStoreValue "Compare" "SaveType" "objectType")
|
|
$sel = $items | Where-Object { "$($_.Value)" -eq "$defaultSave" } | Select-Object -First 1
|
|
if (-not $sel -and $items.Count -gt 0) { $sel = $items[0] }
|
|
if ($sel) { $cbSave.SelectedItem = $sel }
|
|
}
|
|
|
|
# --- Combo: Output Format (CSV / JSON) --------------------------------------
|
|
if ($cbOutFmt) {
|
|
$items = New-Object 'System.Collections.Generic.List[SettingsListItem]'
|
|
foreach ($p in @($script:compareOutputProviders)) {
|
|
if (-not $p) { continue }
|
|
$items.Add([SettingsListItem]@{ Name = [string]$p.Name; Value = [string]$p.Value }) | Out-Null
|
|
$state.OutputProviderMap[[string]$p.Value] = $p
|
|
}
|
|
$cbOutFmt.ItemsSource = $items
|
|
$defaultOut = (Get-SettingStoreValue "Compare" "OutputFormat" "csv")
|
|
$sel = $items | Where-Object { "$($_.Value)" -eq "$defaultOut" } | Select-Object -First 1
|
|
if (-not $sel -and $items.Count -gt 0) { $sel = $items[0] }
|
|
if ($sel) { $cbOutFmt.SelectedItem = $sel }
|
|
}
|
|
|
|
# --- Combo: Comparison Type --------------------------------------------------
|
|
if ($cbType) {
|
|
$items = New-Object 'System.Collections.Generic.List[SettingsListItem]'
|
|
foreach ($t in @($script:comparisonTypes)) {
|
|
if (-not $t) { continue }
|
|
$items.Add([SettingsListItem]@{ Name = [string]$t.Name; Value = [string]$t.Value }) | Out-Null
|
|
$state.TypeMap[[string]$t.Value] = $t
|
|
}
|
|
$cbType.ItemsSource = $items
|
|
$defaultType = (Get-SettingStoreValue "Compare" "Type" "property")
|
|
$sel = $items | Where-Object { "$($_.Value)" -eq "$defaultType" } | Select-Object -First 1
|
|
if (-not $sel -and $items.Count -gt 0) { $sel = $items[0] }
|
|
if ($sel) { $cbType.SelectedItem = $sel }
|
|
}
|
|
|
|
# --- Combo: CSV Delimiter ----------------------------------------------------
|
|
if ($cbDelimiter) {
|
|
$cbDelimiter.ItemsSource = @("", ",", ";", "-", "|")
|
|
$defaultDelim = (Get-SettingStoreValue "Compare" "Delimiter" ";")
|
|
$cbDelimiter.SelectedItem = $defaultDelim
|
|
}
|
|
|
|
# --- Combo: Multi-Value Delimiter (doc-compare ObjectSeparator) ---------------
|
|
if ($cbMultiValueDelim) {
|
|
$items = New-Object 'System.Collections.Generic.List[SettingsListItem]'
|
|
$items.Add([SettingsListItem]@{ Name = 'New line'; Value = [Environment]::NewLine }) | Out-Null
|
|
$items.Add([SettingsListItem]@{ Name = ';'; Value = ';' }) | Out-Null
|
|
$items.Add([SettingsListItem]@{ Name = '|'; Value = '|' }) | Out-Null
|
|
$cbMultiValueDelim.ItemsSource = $items
|
|
$defaultSep = (Get-SettingStoreValue "Compare" "ObjectSeparator" ([Environment]::NewLine))
|
|
$sel = $items | Where-Object { "$($_.Value)" -eq "$defaultSep" } | Select-Object -First 1
|
|
if (-not $sel) { $sel = $items[0] }
|
|
$cbMultiValueDelim.SelectedItem = $sel
|
|
}
|
|
|
|
# --- Checkboxes ----------------------------------------------------------------
|
|
if ($chkIgnoreCore) {
|
|
$chkIgnoreCore.IsChecked = ((Get-SettingStoreValue "Compare" "IgnoreCoreProperties" "true") -eq "true")
|
|
}
|
|
if ($chkSkipAssignments) {
|
|
$chkSkipAssignments.IsChecked = ((Get-SettingStoreValue "Compare" "SkipCompareAssignments" "false") -eq "true")
|
|
}
|
|
if ($chkSkipMissingSrc) {
|
|
$chkSkipMissingSrc.IsChecked = ((Get-SettingStoreValue "Compare" "SkipMissingSourcePolicies" "false") -eq "true")
|
|
}
|
|
if ($chkSkipMissingDst) {
|
|
$chkSkipMissingDst.IsChecked = ((Get-SettingStoreValue "Compare" "SkipMissingDestinationPolicies" "false") -eq "true")
|
|
}
|
|
|
|
# --- Objects-to-compare grid -------------------------------------------------
|
|
$rows = New-Object 'System.Collections.Generic.List[BulkCompareRowItem]'
|
|
foreach ($intuneGroup in @($script:IntuneGroups)) {
|
|
if (-not $intuneGroup -or -not $intuneGroup.Title) { continue }
|
|
$rows.Add([BulkCompareRowItem]@{
|
|
Title = [string]$intuneGroup.Title
|
|
Selected = $true
|
|
ObjectGroup = $intuneGroup
|
|
}) | Out-Null
|
|
}
|
|
$state.Rows = @($rows)
|
|
if ($dgObjects) { $dgObjects.ItemsSource = $state.Rows }
|
|
|
|
# Select/deselect-all moved into the DataGrid column header.
|
|
Initialize-AvaloniaGridSelectAllHeader -Grid $dgObjects -BindingProperty 'Selected' -InitiallyChecked $true | Out-Null
|
|
|
|
# --- Provider panel swap -----------------------------------------------------
|
|
$script:_bulkCompareApplyProvider = {
|
|
param($Provider)
|
|
|
|
$st = $script:_bulkCompareFormState
|
|
if (-not $st -or -not $st.CcProviderOpts) { return }
|
|
if (-not $Provider) {
|
|
$st.CcProviderOpts.Content = $null
|
|
$st.CcProviderOpts.IsVisible = $false
|
|
if ($st.GrdObjects) { $st.GrdObjects.IsVisible = $true }
|
|
return
|
|
}
|
|
|
|
$panel = $null
|
|
if ($Provider.OptionsXaml) {
|
|
if ($st.ProviderPanelCache.ContainsKey($Provider.Value)) {
|
|
$panel = $st.ProviderPanelCache[$Provider.Value]
|
|
} else {
|
|
$panel = $st.Ui.GetXamlObject((Join-Path $script:AppUIRootFolder "XAML/$($Provider.OptionsXaml).axaml"))
|
|
if ($panel) {
|
|
$st.ProviderPanelCache[$Provider.Value] = $panel
|
|
Register-BulkCompareProviderBrowseEvents -Panel $panel -Provider $Provider -HostType $st.HostType
|
|
}
|
|
}
|
|
if ($panel) { $panel.DataContext = $Provider }
|
|
}
|
|
|
|
$st.CcProviderOpts.Content = $panel
|
|
$st.CcProviderOpts.IsVisible = ($null -ne $panel)
|
|
|
|
if ($st.GrdObjects) {
|
|
$st.GrdObjects.IsVisible = -not [bool]$Provider.IgnoreGroups
|
|
}
|
|
}
|
|
|
|
if ($cbProvider) {
|
|
$cbProvider.add_SelectionChanged((ConvertTo-AvaloniaEventScriptBlock {
|
|
param($s, $e)
|
|
$st = $script:_bulkCompareFormState
|
|
if (-not $st) { return }
|
|
$sel = $s.SelectedItem
|
|
$provider = if ($sel) { $st.ProviderMap[[string]$sel.Value] } else { $null }
|
|
& $script:_bulkCompareApplyProvider $provider
|
|
}))
|
|
|
|
# Initial render.
|
|
$initial = $null
|
|
if ($cbProvider.SelectedItem) {
|
|
$initial = $state.ProviderMap[[string]$cbProvider.SelectedItem.Value]
|
|
}
|
|
& $script:_bulkCompareApplyProvider $initial
|
|
}
|
|
|
|
# --- Output format → CSV-delimiter enable toggle ----------------------------
|
|
$script:_bulkCompareApplyDelimiterEnable = {
|
|
$st = $script:_bulkCompareFormState
|
|
if (-not $st -or -not $st.CbDelimiter -or -not $st.CbOutFmt -or -not $st.CbOutFmt.SelectedItem) { return }
|
|
$sel = $st.OutputProviderMap[[string]$st.CbOutFmt.SelectedItem.Value]
|
|
$st.CbDelimiter.IsEnabled = ($sel -is [CompareCSVOutputProvider])
|
|
}
|
|
if ($cbOutFmt) {
|
|
$cbOutFmt.add_SelectionChanged((ConvertTo-AvaloniaEventScriptBlock { param($s, $e) & $script:_bulkCompareApplyDelimiterEnable }))
|
|
& $script:_bulkCompareApplyDelimiterEnable
|
|
}
|
|
|
|
# --- Buttons -----------------------------------------------------------------
|
|
if ($btnClose) {
|
|
$btnClose.add_Click((ConvertTo-AvaloniaEventScriptBlock {
|
|
Show-ModalObject
|
|
$script:_bulkCompareFormState = $null
|
|
}))
|
|
}
|
|
|
|
if ($btnStart) {
|
|
$btnStart.add_Click((ConvertTo-AvaloniaEventScriptBlock {
|
|
$st = $script:_bulkCompareFormState
|
|
if (-not $st) { return }
|
|
$providerSel = if ($st.CbProvider) { $st.CbProvider.SelectedItem } else { $null }
|
|
$provider = if ($providerSel) { $st.ProviderMap[[string]$providerSel.Value] } else { $null }
|
|
if (-not $provider) {
|
|
$st.Ui.ShowMessageBox("No compare provider selected.", "Compare", "OK", "Error")
|
|
return
|
|
}
|
|
|
|
# Persist all five user choices (matches WPF order).
|
|
if ($st.CbProvider -and $st.CbProvider.SelectedItem) { Save-SettingStoreValue "Compare" "Provider" $st.CbProvider.SelectedItem.Value }
|
|
if ($st.CbType -and $st.CbType.SelectedItem) { Save-SettingStoreValue "Compare" "Type" $st.CbType.SelectedItem.Value }
|
|
if ($st.CbDelimiter) { Save-SettingStoreValue "Compare" "Delimiter" ([string]$st.CbDelimiter.SelectedItem) }
|
|
if ($st.CbOutFmt -and $st.CbOutFmt.SelectedItem) { Save-SettingStoreValue "Compare" "OutputFormat" $st.CbOutFmt.SelectedItem.Value }
|
|
if ($st.CbSave -and $st.CbSave.SelectedItem) { Save-SettingStoreValue "Compare" "SaveType" $st.CbSave.SelectedItem.Value }
|
|
if ($st.CbMultiValueDelim -and $st.CbMultiValueDelim.SelectedItem) { Save-SettingStoreValue "Compare" "ObjectSeparator" ([string]$st.CbMultiValueDelim.SelectedItem.Value) }
|
|
if ($st.ChkIgnoreCore) { Save-SettingStoreValue "Compare" "IgnoreCoreProperties" $(if ($st.ChkIgnoreCore.IsChecked) { "true" } else { "false" }) }
|
|
if ($st.ChkSkipAssignments) { Save-SettingStoreValue "Compare" "SkipCompareAssignments" $(if ($st.ChkSkipAssignments.IsChecked) { "true" } else { "false" }) }
|
|
if ($st.ChkSkipMissingSrc) { Save-SettingStoreValue "Compare" "SkipMissingSourcePolicies" $(if ($st.ChkSkipMissingSrc.IsChecked) { "true" } else { "false" }) }
|
|
if ($st.ChkSkipMissingDst) { Save-SettingStoreValue "Compare" "SkipMissingDestinationPolicies" $(if ($st.ChkSkipMissingDst.IsChecked) { "true" } else { "false" }) }
|
|
if ($st.ChkSkipMissingSrc) { $provider.SkipMissingSourcePolicies = [bool]$st.ChkSkipMissingSrc.IsChecked }
|
|
if ($st.ChkSkipMissingDst) { $provider.SkipMissingDestinationPolicies = [bool]$st.ChkSkipMissingDst.IsChecked }
|
|
|
|
if ($st.BtnStart) { $st.BtnStart.IsEnabled = $false }
|
|
if ($st.BtnClose) { $st.BtnClose.IsEnabled = $false }
|
|
if ($st.TxtStatus) { $st.TxtStatus.Text = "Comparing..." }
|
|
Write-Status "Compare objects"
|
|
try {
|
|
# Push runtime options into Internal/Compare.ps1 — mirrors WPF
|
|
# Set-CompareRuntimeOptionsFromUI.
|
|
$runtimeArgs = @{}
|
|
if ($st.CbType -and $st.CbType.SelectedItem) {
|
|
$runtimeArgs.CompareType = [string]$st.CbType.SelectedItem.Value
|
|
$cd = $st.TypeMap[[string]$st.CbType.SelectedItem.Value]
|
|
if ($cd) { $runtimeArgs.CompareDefinition = $cd }
|
|
}
|
|
if ($st.CbOutFmt -and $st.CbOutFmt.SelectedItem) {
|
|
$op = $st.OutputProviderMap[[string]$st.CbOutFmt.SelectedItem.Value]
|
|
if ($op) { $runtimeArgs.OutputProvider = $op }
|
|
}
|
|
if ($st.CbSave -and $st.CbSave.SelectedItem) {
|
|
$runtimeArgs.SaveType = [string]$st.CbSave.SelectedItem.Value
|
|
}
|
|
if ($st.CbDelimiter -and $null -ne $st.CbDelimiter.SelectedItem) {
|
|
$runtimeArgs.CsvDelimiter = [string]$st.CbDelimiter.SelectedItem
|
|
}
|
|
if ($st.CbMultiValueDelim -and $st.CbMultiValueDelim.SelectedItem) {
|
|
$runtimeArgs.ObjectSeparator = [string]$st.CbMultiValueDelim.SelectedItem.Value
|
|
}
|
|
if ($st.ChkIgnoreCore) {
|
|
$runtimeArgs.IgnoreCoreProperties = [bool]$st.ChkIgnoreCore.IsChecked
|
|
}
|
|
if ($st.ChkSkipAssignments) {
|
|
$runtimeArgs.SkipAssignments = [bool]$st.ChkSkipAssignments.IsChecked
|
|
}
|
|
Set-CompareRuntimeOptions @runtimeArgs
|
|
|
|
$selectedGroups = @()
|
|
if (-not $provider.IgnoreGroups) {
|
|
$selectedGroups = @($st.Rows |
|
|
Where-Object { $_ -and $_.Selected -and $_.ObjectGroup } |
|
|
ForEach-Object { $_.ObjectGroup })
|
|
if ($selectedGroups.Count -eq 0) {
|
|
$st.Ui.ShowMessageBox("No object types selected.", "Compare", "OK", "Warning")
|
|
return
|
|
}
|
|
}
|
|
|
|
Start-BulkCompare $provider -SelectedGroups $selectedGroups
|
|
if ($st.TxtStatus) { $st.TxtStatus.Text = "Compare finished." }
|
|
} catch {
|
|
Write-LogError "Bulk compare failed" $_.Exception
|
|
$st.Ui.ShowMessageBox("Compare failed: $($_.Exception.Message)", "Compare", "OK", "Error")
|
|
if ($st.TxtStatus) { $st.TxtStatus.Text = "Compare failed." }
|
|
} finally {
|
|
Write-Status ""
|
|
if ($st.BtnStart) { $st.BtnStart.IsEnabled = $true }
|
|
if ($st.BtnClose) { $st.BtnClose.IsEnabled = $true }
|
|
}
|
|
}))
|
|
}
|
|
|
|
$form.add_KeyDown((ConvertTo-AvaloniaEventScriptBlock {
|
|
param($s, $e)
|
|
if ($e.Key -eq [Avalonia.Input.Key]::Escape) {
|
|
Show-ModalObject
|
|
$e.Handled = $true
|
|
}
|
|
}.GetNewClosure()))
|
|
|
|
$ui.ShowModalForm("Bulk Compare Objects", $form, $true)
|
|
}
|
|
|
|
function Register-BulkCompareProviderBrowseEvents
|
|
{
|
|
# Wires the four Browse buttons that may appear in the per-provider option
|
|
# panels (browseExportPath / browseSavePath / browseExportPathSource /
|
|
# browseExportPathCompare). Handler context rides on each button's Tag
|
|
# (sender-based) - function locals are not available at click time
|
|
# (ConvertTo-AvaloniaEventScriptBlock strips closures).
|
|
# NOTE: param() must stay the FIRST statement - a $ui assignment above it
|
|
# once made 'param' unparseable at call time and broke every panel swap.
|
|
param($Panel, $Provider, $HostType)
|
|
|
|
if (-not $Panel -or -not $Provider) { return }
|
|
|
|
# Text boxes -> provider properties. The panels' XAML has no bindings and
|
|
# nothing synced them, so everything the user typed was discarded - which
|
|
# made the "Named Objects in Intune" provider (whose only inputs are two
|
|
# text boxes) impossible to use at all. Each box pushes on TextChanged;
|
|
# the current provider value seeds the box up front.
|
|
$textPropertyPairs = @(
|
|
@{ Name = 'txtCompareNameFilter'; Property = 'NameFilter' },
|
|
@{ Name = 'txtExportPath'; Property = 'ExportPath' },
|
|
@{ Name = 'txtExportPathSource'; Property = 'SourcePath' },
|
|
@{ Name = 'txtExportPathCompare'; Property = 'ComparePath' },
|
|
@{ Name = 'txtCompareSource'; Property = 'SourcePattern' },
|
|
@{ Name = 'txtCompareWith'; Property = 'ComparePattern' },
|
|
@{ Name = 'txtSavePath'; Property = 'SavePath' }
|
|
)
|
|
foreach ($pair in $textPropertyPairs) {
|
|
$tb = $HostType::FindByName($Panel, $pair.Name)
|
|
if (-not $tb) { continue }
|
|
if (-not $Provider.PSObject.Properties[$pair.Property]) { continue }
|
|
|
|
$tb.Text = [string]$Provider.($pair.Property)
|
|
$tb.Tag = @{ Provider = $Provider; Property = $pair.Property }
|
|
$tb.add_TextChanged((ConvertTo-AvaloniaEventScriptBlock {
|
|
param($S, $E)
|
|
$tag = $S.Tag
|
|
if ($tag -and $tag.Provider) { $tag.Provider.($tag.Property) = [string]$S.Text }
|
|
}))
|
|
}
|
|
|
|
$browsePathPairs = @(
|
|
@{ Name = 'browseExportPath'; Property = 'ExportPath'; TextName = 'txtExportPath'; Title = 'Select root folder for compare' },
|
|
@{ Name = 'browseSavePath'; Property = 'SavePath'; TextName = 'txtSavePath'; Title = 'Select save folder' },
|
|
@{ Name = 'browseExportPathSource'; Property = 'SourcePath'; TextName = 'txtExportPathSource'; Title = 'Select source root folder' },
|
|
@{ Name = 'browseExportPathCompare'; Property = 'ComparePath'; TextName = 'txtExportPathCompare'; Title = 'Select compare root folder' }
|
|
)
|
|
|
|
foreach ($pair in $browsePathPairs) {
|
|
$btn = $HostType::FindByName($Panel, $pair.Name)
|
|
if (-not $btn) { continue }
|
|
# Provider classes don't expose every property; skip wiring for
|
|
# buttons whose target property doesn't exist on this provider.
|
|
if (-not $Provider.PSObject.Properties[$pair.Property]) { continue }
|
|
|
|
$btn.Tag = @{
|
|
Provider = $Provider; Property = $pair.Property; Title = $pair.Title
|
|
TextBox = $HostType::FindByName($Panel, $pair.TextName)
|
|
}
|
|
$btn.add_Click((ConvertTo-AvaloniaEventScriptBlock {
|
|
param($s, $e)
|
|
$tag = $s.Tag
|
|
$current = if ($tag.TextBox) { [string]$tag.TextBox.Text } else { '' }
|
|
$folder = $script:UIProvider.ShowFolderPicker($current, [string]$tag.Title)
|
|
if ($folder) {
|
|
$tag.Provider.($tag.Property) = $folder
|
|
# Write the TextBox directly: these panels have no bindings, so
|
|
# the old DataContext null/restore refresh changed nothing on
|
|
# screen and picking a folder looked like it did nothing.
|
|
if ($tag.TextBox) { $tag.TextBox.Text = $folder }
|
|
}
|
|
}))
|
|
}
|
|
}
|