【问题标题】:WPF DataGrid Cell SelectionWPF DataGrid 单元格选择
【发布时间】:2018-08-17 18:25:51
【问题描述】:

我正在尝试通过 PowerShell 搜索 WPF DataGrid,依次选择并突出显示包含搜索关键字的每个单元格。目标是 1) 搜索 DataGrid,2) 选择并突出显示包含搜索关键字的单元格,同时将焦点/滚动到包含列/单元格的行,以及 3) 对可能包含的任何其他列/单元格重复相同的操作相同的关键字。

我整理了一个示例脚本(见下文)来演示我到目前为止所做的工作。在下面显示的示例脚本中,我能够搜索并找到包含关键字的行并滚动到该行。但是,我不知道如何突出显示该行中包含关键字的单元格。

下面显示的示例数据具有用于演示目的的预定义列。但是,从后端返回的实际数据是动态的,因此列数、列标题会有所不同,并且任何列都可能包含关键字。我们如何选择包含关键字的单元格?有没有更好的方法来实现这个总体目标?提前感谢您的帮助。

[xml]$xaml=@"
<Window
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:Test"
    Title="MainWindow" Height="175" Width="550">
<Grid>
    <TextBox x:Name="tb_Search" HorizontalAlignment="Left" Height="23" Margin="10,10,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="149"/>
    <Button x:Name="bt_Search" Content="Search" HorizontalAlignment="Left" VerticalAlignment="Top" Width="100" IsDefault="True" Height="22" Margin="165,10,0,0" />        
    <DataGrid x:Name="dg" Margin="10,45,0,0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" MinHeight="100" Height="Auto" Width="Auto" ColumnWidth="Auto" AlternationCount="1" IsReadOnly="True" SelectionMode="Extended" SelectionUnit="Cell" Background="White" /> 
</Grid>
</Window>
"@

$reader=(New-Object System.Xml.XmlNodeReader $xaml)
$Window=[Windows.Markup.XamlReader]::Load($reader)

#Turn XAML into PowerShell objects
$xaml.SelectNodes("//*[@*[contains(translate(name(.),'n','N'),'x:Name')]]") | ForEach-Object{
Set-Variable -Name ($_.Name) -Value $Window.FindName($_.Name)
}

#sample data
$DataSet = New-Object System.Data.DataSet
$Table = $DataSet.Tables.Add("Table")
$Properties = @("Country","Capital","Population")
$Properties | foreach {
 $Column = New-Object System.Data.DataColumn($_)
 $Table.Columns.Add($Column)
 }
$Null=$Table.Rows.Add("China PR","Beijing","20,693,000")
$Null=$Table.Rows.Add("India","New Delhi","16,787,949") 
$Null=$Table.Rows.Add("Japan","Tokyo","13,189,000")
$Null=$Table.Rows.Add("Philippines","Manila","12,877,253")
$Null=$Table.Rows.Add("Russia","Moscow","11,541,000")
$Null=$Table.Rows.Add("Egypt","Cairo","10,230,350")
$Null=$Table.Rows.Add("USA","Washington, D.C","658,893")
$Null=$Table.Rows.Add("China PR","Beijing","20,693,000")
$Null=$Table.Rows.Add("India","New Delhi","16,787,949") 
$Null=$Table.Rows.Add("Japan","Tokyo","13,189,000")
$Null=$Table.Rows.Add("Philippines","Manila","12,877,253")
$Null=$Table.Rows.Add("Russia","Moscow","11,541,000")
$Null=$Table.Rows.Add("Egypt","Cairo","10,230,350")
$Null=$Table.Rows.Add("USA","Washington, D.C","658,893")

#populate datagrid
$DataView = New-Object System.Data.DataView($Table)
$array = New-Object System.Collections.ArrayList
[void] $array.AddRange($DataView)       
$dg.clear()
$dg.ItemsSource = $array
$dg.IsReadOnly = $true

$bt_Search.Add_Click({
$SearchValue = $tb_Search.text
for ($i = 0; $i -lt $dg.Items.Count; $i++)
{
    if ($dg.Items[$i].Row[$dg.Columns.DisplayIndex] -eq "$SearchValue")
    {
        [System.Windows.Forms.MessageBox]::Show("Keyword Found")
        $dg.ScrollIntoView($dg.Items[$i]) #scroll to the row that contains the keyword searched
    }
}
})

#Display Form
$Window.ShowDialog() | Out-Null

【问题讨论】:

    标签: wpf powershell datagrid


    【解决方案1】:

    使用与您正在执行的操作非常相似的方法,我能够突出显示包含搜索短语的所有单元格(因为我起初误解了这个问题)。我必须做的是遍历列,而不是为每一行一次搜索所有列。这样我们就知道所需信息在哪一列,然后我们可以在将正确的行滚动到视图中后选择该列中的单元格。

    $bt_Search.Add_Click({
    $SearchValue = $tb_Search.text
    $dg.SelectedCells.Clear()
    for ($i = 0; $i -lt $dg.Items.Count; $i++)
    {
        0..($dg.Columns.Count-1)|?{$dg.Items[$i].Row[$_] -eq "$SearchValue"}|%{
            $dg.ScrollIntoView($dg.Items[$i],$dg.Columns[$_])
            $DGCell = $dg.Columns[$_].GetCellContent($dg.Items[$i]).Parent
            $DGCellInfo = New-Object System.Windows.Controls.DataGridCellInfo($DGCell)
            $dg.SelectedCells.add($DGCellInfo)
        }
    }
    })
    

    这至少为您提供了选择单元格的基础,您只需要弄清楚如何跟踪当前单元格以便能够移动到下一个单元格,因为听起来您希望能够移动一遍又一遍地点击“搜索”按钮,逐个单元格地点击。

    编辑:关于将其设置为查找/查找下一步,您可以设置一个全局变量,将 $i 设置为该变量,然后在每个循环中设置该全局变量时间。然后,任何时候你得到一个匹配运行一个break 来终止循环,它应该从它停止的地方开始。可能还想在循环之前添加一行来重置全局变量,以便在循环结束时重新开始。应该这样做:

    $global:SearchIndex = 0
    $bt_Search.Add_Click({
    $SearchValue = $tb_Search.text
    $dg.SelectedCells.Clear()
    for ($i = $global:SearchIndex; $i -lt $dg.Items.Count; $i++)
    {
        0..($dg.Columns.Count-1)|?{$dg.Items[$i].Row[$_] -eq "$SearchValue"}|%{
            $dg.ScrollIntoView($dg.Items[$i],$dg.Columns[$_])
            $DGCell = $dg.Columns[$_].GetCellContent($dg.Items[$i]).Parent
            $DGCellInfo = New-Object System.Windows.Controls.DataGridCellInfo($DGCell)
            $dg.SelectedCells.add($DGCellInfo)
            $global:SearchIndex = $i
            break
        }
    }
    #check if we hit the end of the table. If we did display a notice and reset the search index.
    If($global:SearchIndex -ge $dg.Items.Count){
        [System.Windows.Forms.MessageBox]::Show("No more matches found, resetting search to begining of table.")
        $global:SearchIndex=0
    }
    })
    

    Edit2:好吧,你是对的,break 停止递增,所以每次都会找到相同的结果。为了解决这个问题,我们还需要跟踪该列。如果我们也将内部循环更改为For 循环,我们可以在每次跟踪时增加内部循环。我们还需要跟踪中断,因此如果内部循环中断,外部循环也会中断。所以,这样的事情应该这样做:

    $global:SearchIndex = 0
    $global:SearchIndex2 = 0
    $bt_Search.Add_Click({
    $SearchValue = $tb_Search.text
    $dg.SelectedCells.Clear()
    for ($i = $global:SearchIndex; $i -lt $dg.Items.Count; $i++)
    {
        $global:BreakPoint = $false
        For($j=$global:SearchIndex2;$j -le 2;$j++){
            If($dg.Items[$i].Row[$j] -eq "$SearchValue"){
                $dg.ScrollIntoView($dg.Items[$i],$dg.Columns[$j])
                $DGCell = $dg.Columns[$j].GetCellContent($dg.Items[$i]).Parent
                $DGCellInfo = New-Object System.Windows.Controls.DataGridCellInfo($DGCell)
                $dg.SelectedCells.add($DGCellInfo)
                $global:SearchIndex = $i
                $global:SearchIndex2 = $j+1
                $global:BreakPoint = $true
                break
            }
        }
    
        If($global:BreakPoint){break}
        $global:SearchIndex2=0
    }
    #check if we hit the end of the table. If we did display a notice and reset the search index.
    If($global:SearchIndex -ge $dg.Items.Count){
        [System.Windows.Forms.MessageBox]::Show("No more matches found, resetting search to begining of table.")
        $global:SearchIndex=0
        $global:SearchIndex2=0
    }
    })
    

    【讨论】:

    • 这非常适合选择单元格。谢谢你。如果您对一次移动到后续单元格有任何建议,请告诉我。把它想象成“查找下一个”方法。暂时,我在循环中引入了一个消息框,因此它会在显示消息框时暂停当前单元格选择。但是,此消息框将根据找到的匹配数显示多次,因此不是理想的解决方案。我正在尝试找到一种方法来使用同一窗口中的按钮,以便每个按钮单击都会遍历找到的后续匹配项,但仍然可以找到方法。
    • 我更新了我的答案,提出了关于尝试循环的建议。希望对您有所帮助!
    • 感谢您的想法。但是,如果我在找到匹配项并中断后将 $i 设置在循环中,并在下次再次运行它时,它会从它停止的同一行开始,并找到它之前找到的相同匹配项。如果我增加 $i + 1 然后设置全局变量,那么它在下一次搜索期间从下一行开始,依此类推。但是,不利的一面是,如果关键字在同一行中的多个列上,那么它只会找到每行的第一个匹配项和中断。有没有想过从它离开的同一行开始,但在找到上一个匹配项的那一行旁边的列?
    • 跟踪专栏的绝妙想法。再做一些调整,我就能完成完整的查找功能。感谢您的时间和专业知识。
    猜你喜欢
    • 2014-11-01
    • 1970-01-01
    • 2021-03-29
    • 1970-01-01
    • 2013-06-08
    • 2017-08-07
    • 2015-04-12
    • 2017-07-14
    • 1970-01-01
    相关资源
    最近更新 更多