Управление типом, возвращаемым функцией в PowerShell - PullRequest
2 голосов
/ 06 марта 2020

Итак, я пытаюсь создать GUI для поискового приложения, используя форму WPF. Моя форма имеет TextBox и ComboBox для ввода, и содержимое применяется в качестве фильтра каждый раз, когда происходят события изменения. $var_SearchRegion - мой ComboBox, $var_SearchFor - мой TextBox, а $var_DGDisplay - моя DataGrid для возвращаемых данных.

Чтобы установить фильтр, я создал базовую c функцию. Я знаю, что есть более чистые способы сделать это, чем дерево If Then ElseIf, но это было быстро, и это работает. Функция выглядит следующим образом:

Function GetFilteredItems
{
    $RSelect = $var_SearchRegion.SelectedValue.Content
    $PF = $var_SearchFor.Text
    $RF = If ($RSelect -eq 'All Regions') {''} Else {$RSelect}
    If ($PF -eq '' -and $RF -eq '')
    {
        $DPST
    }
    ElseIf ($PF -eq '')
    {
        $DPST | ? {$_.Region -eq $RF}
    }
    ElseIf ($RF -eq '')
    {
        $DPST | ? {$_.FilePath -like "*$PF*"}
    }
    Else
    {
        $DPST | ? {$_.Region -eq $RF -and $_.FilePath -like "*$PF*"}
    }
}

Проблема возникает, когда фильтр сокращает набор данных ровно до одной записи и возвращает ее. Раньше у меня была эта проблема с PowerShell, использующим методы. NET, и я предполагаю, что тип меняется с [Array] для «thing »на« что-то », и это вызывает проблемы с объектами WPF.

Вот пример кода обработчика событий, который вызывает проблемы, когда фильтр возвращает мое возвращение, установленное в 1 элемент:

$var_SearchFor.Add_TextChanged({
    $var_DGDisplay.ItemsSource = GetFilteredItems
    $var_DGDisplay.Refresh
})

Когда GetFilteredItems возвращает 1 элемент, я получаю эту ошибку:

Exception setting "ItemsSource": "Cannot convert the "@{Region=SW; 
FilePath=\\server\share\folder\file.ext}" value of type 
"Deserialized.System.Management.Automation.PSCustomObject" to type "System.Collections.IEnumerable"."
At C:\Users\Matthew\GUITest.ps1:90 char:5
+     $var_DGDisplay.ItemsSource = GetFilteredItems
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], SetValueInvocationException
    + FullyQualifiedErrorId : ExceptionWhenSetting

Я могу легко «исправить» эту проблему, обернув вызов функции и явно преобразовав в тип [array], например:

$var_SearchFor.Add_TextChanged({
    $var_DGDisplay.ItemsSource = [array](GetFilteredItems)
    $var_DGDisplay.Refresh
})

Но кажется, что нужно сделай это. Я должен быть в состоянии исправить это как-то внутри функции. Однако обтекание строк вывода функции (в 4 местах) одним и тем же [array]() не сработало. Я нашел некоторую информацию о расширенных функциях PowerShell, где я мог бы использовать синтаксис [OutputType([Array])] для обозначения типа вывода, но хотя я и подтвердил, что получаю System.Array в качестве типа вывода, используя (Get-Command GetFilteredItems).OutputType, он все равно завершится с та же ошибка.

TL; DR - есть ли способ избежать необходимости оборачивать мой вызов функции с [array]() и все же заставить его работать со свойством ItemSource элемента управления моего WPF DataGrid?

1 Ответ

4 голосов
/ 07 марта 2020
  • PowerShell по умолчанию перечисляет коллекций, которые вы выводите из функции (независимо от того, выводите ли вы их неявно или с помощью оператора return)

    • Кому для предотвращения этого используйте Write-Output -NoEnumerate или, более просто и эффективно (но более неясно), оберните выходной набор в одноэлементный aux. массив (, $collection) - см. этот ответ для получения дополнительной информации.
  • В вашем случае вам также необходимо убедиться, что вы упаковка * сама всегда коллекция, для которой вы можете использовать @() или приведение к [array].

Function GetFilteredItems
{
    $RSelect = $var_SearchRegion.SelectedValue.Content
    $PF = $var_SearchFor.Text
    $RF = If ($RSelect -eq 'All Regions') {''} Else {$RSelect}

    # Collect your command's output in an array-typed variable ([object[]])
    [array] $result = 
      If ($PF -eq '' -and $RF -eq '')
      {
        $DPST
      }
      ElseIf ($PF -eq '')
      {
        $DPST | ? {$_.Region -eq $RF}
      }
      ElseIf ($RF -eq '')
      {
        $DPST | ? {$_.FilePath -like "*$PF*"}
      }
      Else
      {
        $DPST | ? {$_.Region -eq $RF -and $_.FilePath -like "*$PF*"}
      }

    # Output the array $result as-is, via an aux. wrapper array.
    , $result

}

Примечание: Вы не строго нужна немедленная переменная $result; Вы можете использовать , @(if ...) напрямую.

Однако обратите внимание, что ни в одном из случаев команда не будет выводить stream (создавать объекты по мере их создания), поскольку весь вывод должен быть собрал сначала , перед доп. массив может быть построен вокруг него.

...