我一直在为 Windows Power Shell 制作 ezXAML 库(简单的 XAML)。基本上你可以像使用 jQuery 一样创建 DOM:创建元素并添加子元素,这会构建一个虚拟 DOM。 ToString 函数从我的元素类中生成 XAML 字符串,包括所有子元素及其子元素。您将元素发送到加载窗口:创建一个窗口元素,将 DOM 添加为子元素,将字符串发送到 xamlReader 以加载到窗口对象,然后在其上调用 ShowDialog。我使用模态窗口,因此虚拟 DOM 和真实 DOM 保持一致:我可以导航虚拟 DOM,轻松找到要在真实 DOM 中查找的元素名称。
然后我从 AngularJS 获取了一个页面,添加了我自己的 ezClick 和 ezKeyDown。我什至添加了 ezBind 用于数据绑定。我什至编写了一堆单元测试来测试我的库,包括一个使用 ezClick 和 ezBind 的简单增量单元测试。
现在,我想做Suduko。因此,其中有 81 个用于解决方案的文本块,以及 729 个用于注释的文本块。我正在进行的步骤是使用用户按下的数字设置当前单元格值。数据绑定用于设置初始“”(单个空格)空值。但尝试更改任何单元格都会使程序崩溃。请记住,这在简单的增量单元测试中效果很好。
我有调试写出,数据值被正确评估为按下的数字(过滤掉错误输入的逻辑),找到正确的当前单元格,元素是真实的,存在的,并且具有文本属性,并且具有唯一的姓名。无论我执行 .Text = $value 还是获取 TextProperty 并对其执行 SetValue,单元测试总是通过,但游戏总是在这里崩溃,并分别出现相同的异常(但 Text 或 SetValue):“异常设置”Text(或 SetValue) ":"索引操作失败;数组索引评估为 null。""
没有足够的空间容纳所有 3 个文件,我不得不删除一些文档。这是库和游戏,抱歉您必须尝试并进行自己的单元测试。如前所述,带有 ezClick 和 ezBind 的简单增量器可以正常工作。
我希望有人能有一个更可靠的方法来解决这个问题
ezXAML.ps1:
# ezxaml.ps1
# By: Crawford Computing
# This facilitates creating a window using WFP and XAML, and wiring up events.
# This draws heavily on concepts from AngularJs, and includes a virtual DOM.
# You can use ezClick, ezKeyDown and ezBind attributes for your XAML elements
# Events and Data are bound on window open, and data is bebound during every event.
# in your click handle, your params are: "param ($sender, $eventArgs, $data)"
# $sender.Name is the button name, $data is the scope data object. this is the data you can bind and otherwise use in your app.
# Usage:
# $element = CreateElement -tag [String] -attr [Dictionary [String,String] -selfClosing [Bool, default = false]
# $element.AddAttribute([Name],[Value])
# $element.AddChild([XamlElement], $place = "last")
# $clone = $element.Clone()
# PrettyPrint -element [XamlElement]
# LoadWindow-XAML -content [XamlElement], -title [String], -data [Dictionary [String, Object] default = $null,
# -height [int, optional], -width [int, optional], -top [int, optional], -left [int, optional],
# -debug [bool, default = $false]
# This Loads the window. As stated, <Window></Window> is created here, and the content is added as a child.
# Before showing the window, ezEvents are wired and the data is bound to any ezBind elements.
# Add the necessary assemblies
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
Add-Type -AssemblyName PresentationCore
Add-Type -AssemblyName PresentationFramework
# XAML element class. This describes a hierarchical element system like the HTML DOM, and then returns a XAML string for WPF.
class XamlElement {
[string]$TagName
[System.Collections.Generic.Dictionary[String, String]]$Attributes
[System.Collections.Generic.List[object]]$Children
[XamlElement]$Parent
[bool]$IsSelfClosing
# Constructor to initialize the tag name and optionally attributes
XamlElement([string]$tagName) {
$this.TagName = $tagName
$this.Attributes = @{}
$this.Children = @()
$this.Parent = $null
}
#initialize
[void] initialize([System.Collections.Generic.Dictionary[String, String]]$attributes = @{}, [bool]$isSelfClosing = $false) {
$this.IsSelfClosing = $isSelfClosing
# Convert the attributes hashtable into a list of key-value pairs to preserve order
foreach ($key in $attributes.Keys) {
$this.Attributes.Add($key, $attributes[$key])
}
}
# Clone method
[XamlElement] Clone() {
# Create a new instance of XamlElement with the same tag name
$clone = [XamlElement]::new($this.TagName)
# Clone attributes
foreach ($key in $this.Attributes.Keys) {
$clone.Attributes[$key] = $this.Attributes[$key]
}
# Clone children recursively
foreach ($child in $this.Children) {
if ($child -is [XamlElement]) {
$clone.Children.Add($child.Clone())
}
}
# Set the self-closing status
$clone.IsSelfClosing = $this.IsSelfClosing
return $clone
}
# Add an attribute to this element
AddAttribute([string]$key, [string]$value) {
$this.Attributes[$key] = $value
}
# Convert the element and its children into a valid XAML string
[string] ToXamlString() {
return $this.ToXamlString($false) # Call the main method with default debug value
}
[string] ToXamlString([bool]$debug = $false) {
$xaml = "<$($this.TagName)"
# Add attributes
foreach ($key in $this.Attributes.Keys) {
# Include custom attributes only if debug mode is enabled
if ($debug -or (-not $key.StartsWith("ez"))) {
$xaml += " $($key)='$($this.Attributes[$key])'"
}
}
# Check if the element is self-closing
if ($this.IsSelfClosing) {
$xaml += " />"
} else {
$xaml += ">"
# Add children elements
foreach ($child in $this.Children) {
$xaml += $child.ToXamlString($debug)
}
# Close the tag
$xaml += "</$($this.TagName)>"
}
return $xaml
}
# Pretty print the element with proper indentation
[string] PrettyPrint([int]$depth = 0) {
$indentation = " " * $depth
$xaml = "$indentation<$($this.TagName)"
# Add attributes
foreach ($key in $this.Attributes.Keys) {
$xaml += " $($key)='$($this.Attributes[$key])'"
}
# Handle self-closing or nested elements
if ($this.IsSelfClosing) {
$xaml += " />"
} else {
$xaml += ">"
if ($this.Children.Count -gt 0) {
$xaml += "`n"
foreach ($child in $this.Children) {
$xaml += $child.PrettyPrint($depth + 1) + "`n"
}
$xaml += "$indentation</$($this.TagName)>"
} else {
$xaml += "</$($this.TagName)>"
}
}
return $xaml
}
}
# Add-ChildElement Function
function AddChild {
param (
[XamlElement]$parent, # The parent XamlElement
[XamlElement]$child, # The child XamlElement to add
[int]$place = -1 # The position to insert the child (-1 means add to the end)
)
if (-not $parent) {
throw "Parent element cannot be null."
}
if (-not $child) {
throw "Child element cannot be null."
}
# Add the child at the specified position or at the end
if ($place -ge 0 -and $place -lt $parent.Children.Count) {
$parent.Children.Insert($Place, $Child)
} else {
$parent.Children.Add($Child)
}
# Set the parent of the child
$child.Parent = $parent
}
# Pretty Print
function PrettyPrint {
param (
[XamlElement]$element # Accept the XamlElement
)
$result = $element.PrettyPrint(0)
return $result
}
# open a window
function LoadWindow-XAML {
param (
[XamlElement]$content,
[string]$title,
[System.Collections.Generic.Dictionary[String, Object]]$data = $null,
[int]$height = -1,
[int]$width = -1,
[int]$top = -1,
[int]$left = -1,
[bool]$debug = $false
)
# Ensure $data is a valid dictionary
if (-not $data) {
$data = [System.Collections.Generic.Dictionary[String, Object]]::new()
}
# Assign to global variable
$global:Data = $data
# Define base attributes for the Window
$attributes = New-Object 'System.Collections.Generic.Dictionary[String, String]'
$attributes.Add("Title", $title)
$attributes.Add("xmlns", "http://schemas.microsoft.com/winfx/2006/xaml/presentation")
$attributes.Add("xmlns:x", "http://schemas.microsoft.com/winfx/2006/xaml")
if ($height -gt -1) { $attributes.Add("Height", $height) }
if ($width -gt -1) { $attributes.Add("Width", $width) }
if ($top -gt -1) { $attributes.Add("Top", $top) }
if ($left -gt -1) { $attributes.Add("Left", $left) }
# Create the Window XamlElement
$windowXaml = CreateElement -tag "Window" -attr $attributes
$global:DOM = $windowXaml
AddChild -parent $windowXaml -child $content
# Generate the XAML string
$windowXamlString = $windowXaml.ToXamlString()
# If in debug mode, return the XAML string
if ($debug) {
return $windowXamlString
}
try {
# Parse the XAML string and create the Window
[xml]$xml = $windowXamlString
$reader = (New-Object System.Xml.XmlNodeReader $xml)
$global:Window = [Windows.Markup.XamlReader]::Load($reader)
# Bind events
BindEvents -dom $global:DOM
# Bind data
BindData -dom $global:DOM
# Show the window as a modal dialog
$global:Window.ShowDialog()
# Return the Window object if needed for further processing
return $global:Window
} catch {
Write-Error "Error rendering the XAML window: $_"
Write-Host $windowXamlString
}
}
function BindEvents {
param (
[XamlElement]$dom
)
$global:handlers = @{}
# Recursive function to bind events based on the virtual DOM
function TraverseAndBind {
param (
[XamlElement]$virtualElement
)
foreach ($key in $virtualElement.Attributes.Keys) {
if ($key.StartsWith("ez")) {
# Extract event name (e.g., "Click" from "ezClick")
$eventName = $key.Substring(2) # Remove "ez" from the key to get event name
# Make sure there is no case mismatch by converting to proper case
$eventName = $eventName.Trim() # Remove any extra spaces
$eventName = $eventName.Substring(0, 1).ToUpper() + $eventName.Substring(1).ToLower() # Standardize case
$handlerName = $virtualElement.Attributes[$key] # Function name as string
if ($eventName -ne "Bind"){
# Resolve the function by name
$handler = Get-Command -Name $handlerName -ErrorAction SilentlyContinue
if ($handler) {
$handler = [scriptblock]::Create($handler.ScriptBlock.ToString())
# Find the real element by matching the name
$name = $virtualElement.Attributes["Name"]
$realElement = $global:Window.FindName($name)
if ($realElement) {
# Store the handler in the global list (associating the element with the handler)
$global:handlers[$name] = $handler
switch ($eventname) {
"Click" {
# Attach the event handler to the Click event
$realElement.Add_Click({
param ($sender, $e)
$elementName = $sender.Name
# Look up the handler based on the sender's name
$handlerFromList = $global:handlers[$elementName]
if ($handlerFromList) {
$handlerFromList.Invoke($sender, $e, $global:Data)
BindData -dom $global:DOM
} else {
Write-Warning "Handler not found for $elementName"
}
})
}
"KeyDown" {
# Attach the event handler to the KeyDown event
$realElement.Add_KeyDown({
param ($sender, $e)
$elementName = $sender.Name
# Look up the handler based on the sender's name
$handlerFromList = $global:handlers[$elementName]
if ($handlerFromList) {
$handlerFromList.Invoke($sender, $e, $global:Data)
BindData -dom $global:DOM
} else {
Write-Warning "Handler not found for $elementName"
}
})
}
}
} else {
Write-Warning "Real element not found for: $($virtualElement.Attributes["Name"])"
}
} else {
Write-Warning "Handler function not found for: $handlerName"
}
} else {
if ($virtualElement.TagName -eq "TextBox") { # only elements that can have a TextChanged event
# Find the real element by matching the name
$name = $virtualElement.Attributes["Name"]
$realElement = $global:Window.FindName($name)
if ($realElement) {
#add a text changed event for bining data the other way
$realElement.Add_TextChanged({
param ($sender, $e)
$elementName = $sender.Name
$element = $global:Window.FindName($elementName)
# update the data
$global:Data[$handlerName] = $element.text
})
} else {
#Write-Warning "Real element not found for: $($virtualElement.Attributes["Name"])"
}
}
}
}
}
# Recurse for child elements
foreach ($child in $virtualElement.Children) {
TraverseAndBind -virtualElement $child
}
}
# Start traversal with the root virtual element
TraverseAndBind -virtualElement $dom
}
function BindData {
param (
[XamlElement]$dom
)
# Recursive function to bind data based on the virtual DOM
function TraverseAndBind {
param (
[XamlElement]$virtualElement
)
foreach ($key in $virtualElement.Attributes.Keys) {
if ($key.StartsWith("ez")) {
# Extract event name (e.g., "Click" from "ezClick")
$eventName = $key.Substring(2) # Remove "ez" from the key to get event name
if($eventname -eq "Bind") {
# Make sure there is no case mismatch by converting to proper case
$eventName = $eventName.Trim() # Remove any extra spaces
$eventName = $eventName.Substring(0, 1).ToUpper() + $eventName.Substring(1).ToLower() # Standardize case
$dataKey = $virtualElement.Attributes[$key] # dataKey as string
$expression = "`$global:data.$dataKey"
Write-Host $expression
# Evaluate the string using Invoke-Expression
$dataValue = Invoke-Expression -Command $expression
if ($dataValue -ne $null -and $dataValue.ToString().Trim() -ne "") {
Write-Host "The string is valid: '$dataValue'"
# Find the real element by matching the name
$name = $virtualElement.Attributes["Name"]
Write-Host $name
$realElement = $global:Window.FindName($name)
if ($realElement -and $realElement -is [System.Windows.Controls.TextBox] -or $realElement -is [System.Windows.Controls.TextBlock]) {
Write-Host "Real element is a TextBox"
if ($dataValue -eq " ") {$dataValue = " " }
$dataValue = [String]$dataValue.ToString()
$realElement.Text = [String]$dataValue.ToString()
Write-Host "set success"
} else {
Write-Warning "Real element not found for: $($virtualElement.Attributes["Name"])"
}
} else {
Write-Warning "Data Value for Data Key $dataKey is null"
}
}
}
}
# Recurse for child elements
foreach ($child in $virtualElement.Children) {
TraverseAndBind -virtualElement $child
}
}
# Start traversal with the root virtual element
TraverseAndBind -virtualElement $dom
}
function CreateElement{
param (
[string]$tag,
[System.Collections.Generic.Dictionary[String, String]]$attr = @{},
[bool]$selfClosing = $false
)
$el = [XamlElement]::new($tag)
$el.initialize($attr,$selfClosing)
return $el
}
Suduko.ps1:
. "C:\CommonScripts\EZXaml\0.0.3\ezxaml.ps1"
# Function to create a Suduko Cell. This should be a textbox (free border), a 9 square grid of textblocks on top for notes, and a trasparent button on top of that.
# The button will tell the logic the current cell. Then next number typed is then either place in the textbox or the corresponding textblck based on the notes toggle, elswhere in main logic code.
function Create-SudukoCell {
param (
[string]$name
)
$size = 30
$fontSize = 20
$margin = "2,2,2,2"
# Attributes for the TextBox
$textboxAttributes = New-Object 'System.Collections.Generic.Dictionary[String, String]'
$textboxAttributes.Add("Name", $name)
$textboxAttributes.Add("Width", $size)
$textboxAttributes.Add("Height", $size)
$textboxAttributes.Add("Margin", $margin)
$textboxAttributes.Add("Text", " ")
$textboxAttributes.Add("HorizontalAlignment", "Center")
$textboxAttributes.Add("VerticalAlignment", "Center")
$textboxAttributes.Add("TextAlignment", "Center")
$textboxAttributes.Add("FontSize", $fontSize)
$textboxAttributes.Add("ezBind", "celldata.$name")
$global:celldata[$name] = " "
$box = CreateElement -tag "TextBox" -attr $textboxAttributes
# Attributes for the outer Grid (parent)
$gridParentAttributes = New-Object 'System.Collections.Generic.Dictionary[String, String]'
$gridParentAttributes.Add("Width", $size)
$gridParentAttributes.Add("Height", $size)
$gridParentAttributes.Add("Margin", $margin)
$gridParent = CreateElement -tag "Grid" -attr $gridParentAttributes
# Create the inner Grid for notes
$gridChildAttributes = New-Object 'System.Collections.Generic.Dictionary[String, String]'
$gridChild = CreateElement -tag "Grid" -attr $gridChildAttributes
# RowDefinitions
$rowDefinitions = CreateElement -tag "Grid.RowDefinitions"
for ($i = 1; $i -le 3; $i++) {
$rowDefAttributes = New-Object 'System.Collections.Generic.Dictionary[String, String]'
$rowDefAttributes.Add("Height", "*")
$rowDef = CreateElement -tag "RowDefinition" -attr $rowDefAttributes -selfClosing $true
AddChild -parent $rowDefinitions -child $rowDef
}
AddChild -parent $gridChild -child $rowDefinitions
# ColumnDefinitions
$columnDefinitions = CreateElement -tag "Grid.ColumnDefinitions"
for ($i = 1; $i -le 3; $i++) {
$colDefAttributes = New-Object 'System.Collections.Generic.Dictionary[String, String]'
$colDefAttributes.Add("Width", "*")
$colDef = CreateElement -tag "ColumnDefinition" -attr $colDefAttributes -selfClosing $true
AddChild -parent $columnDefinitions -child $colDef
}
AddChild -parent $gridChild -child $columnDefinitions
# Add TextBlocks for notes
$noteFontSize = [math]::Floor($size / 4)
for ($i = 1; $i -le 9; $i++) {
$row = [math]::Floor(($i - 1) / 3)
$col = ($i - 1) % 3
$noteAttributes = New-Object 'System.Collections.Generic.Dictionary[String, String]'
$noteAttributes.Add("Name", "$name`_Note$i")
$noteAttributes.Add("FontSize", $noteFontSize)
$noteAttributes.Add("Grid.Row", $row)
$noteAttributes.Add("Grid.Column", $col)
$noteAttributes.Add("HorizontalAlignment", "Center")
$noteAttributes.Add("VerticalAlignment", "Center")
$noteAttributes.Add("TextAlignment", "Center")
$noteAttributes.Add("Background", "Transparent") # Transparent background
$noteAttributes.Add("BorderBrush", "Transparent") # Transparent border
$noteAttributes.Add("BorderThickness", "0") # No border
$noteBlock = CreateElement -tag "TextBox" -attr $noteAttributes
AddChild -parent $gridChild -child $noteBlock
}
# Create the cell selector Button
$buttonAttributes = New-Object 'System.Collections.Generic.Dictionary[String, String]'
$buttonAttributes.Add("Name", "$name`Selector")
$buttonAttributes.Add("Width", $size)
$buttonAttributes.Add("Height", $size)
$buttonAttributes.Add("HorizontalAlignment", "Stretch")
$buttonAttributes.Add("VerticalAlignment", "Stretch")
$buttonAttributes.Add("ezClick", "HandleClicks")
#$buttonAttributes.Add("ezClick", "SetActiveCell")
$buttonAttributes.Add("Background", "Transparent") # Transparent background
$button = CreateElement -tag "Button" -attr $buttonAttributes
# Assemble the Sudoku cell
AddChild -parent $gridParent -child $box
AddChild -parent $gridParent -child $gridChild
AddChild -parent $gridParent -child $button
return $gridParent
}
# Function to generate the 9x9 Sudoku board
function Create-SudukoBoard {
# Board
$attributes = New-Object 'System.Collections.Generic.Dictionary[String, String]'
$attributes.Add("Orientation", "Vertical")
$attributes.Add("Name", "Board")
$attributes.Add("ezKeyDown", "HandleKeyDown")
$boardXAML = CreateElement -tag "StackPanel" -attr $attributes
for ($i = 0; $i -lt 9; $i += 3) {
# Box row
$attributes = New-Object 'System.Collections.Generic.Dictionary[String, String]'
$attributes.Add("Orientation", "Horizontal")
$boxRow = CreateElement -tag "StackPanel" -attr $attributes
for ($j = 0; $j -lt 9; $j += 3) {
# Cell box
$attributes = New-Object 'System.Collections.Generic.Dictionary[String, String]'
$attributes.Add("Orientation", "Vertical")
$attributes.Add("Margin", "5")
$cellBox = CreateElement -tag "StackPanel" -attr $attributes
for ($k = 0; $k -lt 3; $k++) {
# Cell row
$attributes = New-Object 'System.Collections.Generic.Dictionary[String, String]'
$attributes.Add("Orientation", "Horizontal")
$cellRow = CreateElement -tag "StackPanel" -attr $attributes
for ($l = 0; $l -lt 3; $l++) {
$row = $i + $k
$col = $j + $l
$name = "R$row`C$col"
# Create the cell
$cell = Create-SudukoCell -name $name
AddChild -parent $cellRow -child $cell
}
AddChild -parent $cellBox -child $cellRow
}
AddChild -parent $boxRow -child $cellBox
}
AddChild -parent $boardXAML -child $boxRow
}
return $boardXAML
}
# function to build the game GUI
function Create-SudukoGUI {
#Game Panel (full content)
$gamePanel = CreateElement -tag "StackPanel"
# Generate the board XAML
$boardPanel = Create-SudukoBoard
# Button Panel
$attributes = New-Object 'System.Collections.Generic.Dictionary[String, String]'
$attributes.Add("Orientation", "Horizontal")
$attributes.Add("HorizontalAlignment", "Center")
$buttonPanel = CreateElement -tag "StackPanel" -attr $attributes
# button base
$attributes = New-Object 'System.Collections.Generic.Dictionary[String, String]'
$attributes.Add("Width", "55")
$attributes.Add("Height", "20")
$attributes.Add("Margin", "4,4,4,4")
$attributes.Add("ezClick", "HandleClicks")
$buttonBase = CreateElement -tag "Button" -attr $attributes -selfClosing $true
$newGame = $buttonBase.Clone()
$restartGame = $buttonBase.Clone()
$undoMove = $buttonBase.Clone()
$newGame = $buttonBase.Clone()
$toggleNotes = $buttonBase.Clone()
$clearCell = $buttonBase.Clone()
$newGame.AddAttribute("Name","NewGameButton")
$newGame.AddAttribute("Content","New")
$restartGame.AddAttribute("Name","RestartButton")
$restartGame.AddAttribute("Content", "Restart")
$undoMove.AddAttribute("Name", "UndoMoveButton")
$undoMove.AddAttribute("Content", "Undo")
$toggleNotes.AddAttribute("Name", "ToggleNotesButton")
$toggleNotes.AddAttribute("Content", "Notes")
$clearCell.AddAttribute("Name", "ClearCellButton")
$clearCell.AddAttribute("Content", "Erase")
AddChild -parent $buttonPanel -child $newGame
AddChild -parent $buttonPanel -child $restartGame
AddChild -parent $buttonPanel -child $undoMove
AddChild -parent $buttonPanel -child $clearCell
AddChild -parent $buttonPanel -child $toggleNotes
AddChild -parent $gamePanel -child $boardPanel
AddChild -parent $gamePanel -child $buttonPanel
return $gamePanel
}
function HandleClicks{
param ($sender, $eventArgs, $data)
switch ($sender.Name) {
"ToggleNotesButton" {
$data.notesMode = -not $data.notesMode
}
Default {
$data.cell = $sender.Name.Substring(0,4)
}
}
Write-Host $data.notesmode
Write-Host $data.cell
}
function HandleKeyDown{
param ($sender, $eventArgs, $data)
if ($data.notesmode) {
} else {
$key = $eventArgs.Key.ToString()
$key = $key.Substring(1)
if([int]::TryParse($key, [ref]$null)) {
$integerValue = [int]::Parse($key)
if ($integerValue -ge 1 -and $integerValue -le 9) {
$data.celldata[$data.cell] = $integerValue
} elseif ($integerValue -eq 0) {
$data.celldata[$data.cell] = " "
}
}
}
Write-Host $data.notesmode
Write-Host $data.cell
Write-Host $data.celldata[$data.cell]
}
function InitGame {
# Set game varables for the "scope" global:Data object
$data = New-Object 'System.Collections.Generic.Dictionary[String, Object]'
$history = New-Object 'System.Collections.Generic.Dictionary[String, Object]'
$notes = New-Object 'System.Collections.Generic.Dictionary[String, Object]'
$global:celldata = New-Object 'System.Collections.Generic.Dictionary[String, String]' #we populate this dictionary with initial values in the GUI creation
$data.Add("cell", "R1C1")
$data.Add("notesmode", $false)
$data.Add("history", $history)
$data.Add("notes", $notes)
#create the GUI
$sudukoGUI = Create-SudukoGUI
# add in the initial celldata
$data.Add("celldata", $global:celldata)
#open the window
$window = LoadWindow-XAML -title "Suduko by Crawford Computing" -content $sudukoGUI -height 400 -width 350 -data $data
}
InitGame
我的猜测是错误来自函数中的这一行
HandleKeyDown
:
$key = $key.Substring(1)
获取从位置 1 到末尾的子字符串很可能会得到空字符串。
这里你真正想要的是 first 字符,没有别的,所以使用
.Substring(0,1)
(位置 0,长度 1)
您也不必
TryParse()
和下一个 Parse()
将字符串转换为整数值,这只需一个 TryParse()
即可完成
尝试:
function HandleKeyDown{
param ($sender, $eventArgs, $data)
if (-not $data.notesmode) {
$key = $eventArgs.Key.ToString().Substring(0,1)
$integerValue = 0
if([int]::TryParse($key, [ref]$integerValue)) {
if ($integerValue -ge 1 -and $integerValue -le 9) {
$data.celldata[$data.cell] = $integerValue
}
elseif ($integerValue -eq 0) {
$data.celldata[$data.cell] = " "
}
}
}
Write-Host $data.notesmode
Write-Host $data.cell
Write-Host $data.celldata[$data.cell]
}