WPF 算盘






4.79/5 (13投票s)
一个 WPF 算盘

引言
WPF Suanpan 只是一个以 XAML 形式存在的算盘。本文的目标不是让你成为珠算的支持者,尽管你可以自由转换,而是让你了解我是如何创建这个应用程序的。
算盘
算盘是一种中国算盘,可用于十进制和十六进制计算。使用算盘,您可以执行加法、减法、乘法、除法、平方根和立方根运算。
算盘分为两部分。上半部分包含称为天珠的珠子,而下半部分,在分隔梁下方,包含称为地珠的珠子。算盘的每一列包含两个天珠和五个地珠。天珠的价值为5,地珠的价值为1。
算盘最右边的列代表个位,紧邻其左边的列代表十位,然后是百位、千位... 以下截图代表数字 804,
如果您想了解更多关于算盘的信息,以下资源可能会有所帮助
要求
要运行从上面的下载链接提供的项目,你需要以下之一
- Visual Studio 2010
- Expression Blend
如果您使用的是 VS 2008,可以从此处下载源文件。
注意:如果您使用的是 Visual Studio 的 Express 版本,请确保使用Visual Basic Express打开解决方案。
WPF 算盘
工作原理
要移动珠子,请将光标放在珠子上,按住鼠标左键并向上或向下移动珠子。要将所有珠子重置为远离分隔梁,请双击分隔梁。
要拖动WPF Suanpan,请使用“木质”框架。
设计和布局
WPF Suanpan 的框架由抛光玫瑰木制成,天珠由橡木制成...好的,不是这样的。我在 Expression Design 中设计了WPF Suanpan,并在 Expression Blend 中添加了一些额外的元素。
有两个值得关注的内容控件:HeavenGrid
和 EarthGrid
。
HeavenGrid
包含十个 Canvas
,EarthGrid
也包含十个 Canvas
。
有两个 UserControl
:EarthBead
和 HeavenBead
。两者中值得关注的元素是 Thumb
控件,它在两者中都被裁剪,并且 Opacity
设置为零。下图显示了 EarthBead
控件中的 Thumb
,其 Opacity
属性为 100%。
代码
在 MainWindow
Loaded
事件期间,我们将珠子添加到各自的 Canvas
中,
Private Sub MainWindow_Loaded(ByVal sender As Object, _
ByVal e As System.Windows.RoutedEventArgs) _
Handles Me.Loaded
LoadEarthBeads()
LoadHeavenBeads()
End Sub
在上面的事件处理程序中,我们调用 LoadEarthBeads
方法,该方法将 EarthBead
添加到 EarthGrid
的 Canvas
中
Private Sub LoadEarthBeads()
For Each eCanvas As Canvas In EarthGrid.Children
Dim y As Double = eCanvas.ActualHeight
Dim i As Integer = 1
Do
Dim eB As New EarthBead()
y -= eB.Height
eB.EarthNumber = i
Canvas.SetLeft(eB, 2)
Canvas.SetTop(eB, y)
eCanvas.Children.Add(eB)
i += 1
Loop While i < 6
i = 0
Next
End Sub
EarthBead
有一个名为 EarthNumber
的变量,在将珠子添加到 Canvas
之前会为其分配一个值。EarthBead
的值将如下所示
LoadHeavenBeads
方法也在 MainWindow
Loaded
事件处理程序中调用,该方法将 HeavenBeads
添加到 HeavenGrid
的相应 Canvas
中
Private Sub LoadHeavenBeads()
For Each hCanvas As Canvas In HeavenGrid.Children
Dim i As Integer
Dim y As Double = 0
Do
Dim hB As New HeavenBead()
Canvas.SetLeft(hB, 2)
Canvas.SetTop(hB, y)
hCanvas.Children.Add(hB)
y += hB.Height
i += 1
hB.HeavenNumber = i
Loop While i < 2
i = 0
Next
End Sub
每个 HeavenBead
的 HeavenNumber
也被分配了一个值
EarthBead
要移动 EarthBead
,我们会处理其 Thumb
控件的 DragDelta
事件
Private Sub EarthThumb_DragDelta(ByVal sender As Object, _
ByVal e As DragDeltaEventArgs) _
Handles EarthThumb.DragDelta
' Check whether movement is vertical.
If Math.Abs(e.VerticalChange) > Math.Abs(e.HorizontalChange) Then
If e.VerticalChange < 0 Then
MoveEarthBeadUp(e)
Else
MoveEarthBeadDown(e)
End If
Else
Exit Sub
End If
End Sub
在上面的事件处理程序中,调用 MoveEarthBeadUp
来向上移动珠子
Private Sub MoveEarthBeadUp(ByVal e As DragDeltaEventArgs)
Dim y As Double = Canvas.GetTop(Me)
If (Me.EarthNumber = 5) Then
If (y > 0) Then
Canvas.SetTop(Me, (y - shift))
Else
Canvas.SetTop(Me, 0)
End If
ElseIf (Me.EarthNumber = 4) Then
If (y > Me.Height) Then
Canvas.SetTop(Me, (y - shift))
Else
Canvas.SetTop(Me, Me.Height)
End If
ElseIf (Me.EarthNumber = 3) Then
If (y > (Me.Height * 2)) Then
Canvas.SetTop(Me, (y - shift))
Else
Canvas.SetTop(Me, (Me.Height * 2))
End If
ElseIf (Me.EarthNumber = 2) Then
If (y > (Me.Height * 3)) Then
Canvas.SetTop(Me, (y - shift))
Else
Canvas.SetTop(Me, (Me.Height * 3))
End If
ElseIf (Me.EarthNumber = 1) Then
If (y > (Me.Height * 4)) Then
Canvas.SetTop(Me, (y - shift))
Else
Canvas.SetTop(Me, (Me.Height * 4))
End If
End If
MoveBeadsAbove(e)
End Sub
MoveBeadsAbove
方法在上面的方法中被调用,它会将选定珠子在向上移动时与之碰撞的任何珠子向上移动
' Move beads/bead above the bead being moved
' when this bead collides with bead above.
Private Sub MoveBeadsAbove(ByVal e As DragDeltaEventArgs)
Dim earthCanvas As Canvas = CType(Me.Parent, Canvas)
For Each eB As EarthBead In earthCanvas.Children
If (eB.EarthNumber <> Me.EarthNumber) Then
Dim My_Y As Double = Canvas.GetTop(Me)
Dim eB_Y As Double = Canvas.GetTop(eB)
If (eB_Y < My_Y) And (My_Y < (eB_Y + eB.Height)) Then
eB.EarthThumb_DragDelta(Nothing, e)
End If
' Ensure bead above has stopped at its limit.
If (eB.EarthNumber = 5) And (eB_Y < 0) Then
Canvas.SetTop(eB, 0)
ElseIf (eB.EarthNumber = 4) And (eB_Y < eB.Height) Then
Canvas.SetTop(eB, eB.Height)
ElseIf (eB.EarthNumber = 3) And (eB_Y < (eB.Height * 2)) Then
Canvas.SetTop(eB, (eB.Height * 2))
ElseIf (eB.EarthNumber = 2) And (eB_Y < (eB.Height * 3)) Then
Canvas.SetTop(eB, (eB.Height * 3))
ElseIf (eB.EarthNumber = 1) And (eB_Y < (eB.Height * 4)) Then
Canvas.SetTop(eB, (eB.Height * 4))
End If
End If
Next
End Sub
在 EarthThumb
DragDelta
事件处理程序中,我们还调用 MoveEarthBeadDown
来向下移动珠子
Private Sub MoveEarthBeadDown(ByVal e As DragDeltaEventArgs)
Dim y As Double = Canvas.GetTop(Me)
Dim earthCanvas As Canvas = CType(Me.Parent, Canvas)
Dim parentHeight As Double = earthCanvas.ActualHeight
If (Me.EarthNumber = 5) Then
If (y < (parentHeight - (Me.Height * 5))) Then
Canvas.SetTop(Me, (y + shift))
Else
Canvas.SetTop(Me, (parentHeight - (Me.Height * 5)))
End If
ElseIf (Me.EarthNumber = 4) Then
If (y < (parentHeight - (Me.Height * 4))) Then
Canvas.SetTop(Me, (y + shift))
Else
Canvas.SetTop(Me, (parentHeight - (Me.Height * 4)))
End If
ElseIf (Me.EarthNumber = 3) Then
If (y < (parentHeight - (Me.Height * 3))) Then
Canvas.SetTop(Me, (y + shift))
Else
Canvas.SetTop(Me, (parentHeight - (Me.Height * 3)))
End If
ElseIf (Me.EarthNumber = 2) Then
If (y < (parentHeight - (Me.Height * 2))) Then
Canvas.SetTop(Me, (y + shift))
Else
Canvas.SetTop(Me, (parentHeight - (Me.Height * 2)))
End If
ElseIf (Me.EarthNumber = 1) Then
If (y < (parentHeight - Me.Height)) Then
Canvas.SetTop(Me, (y + shift))
Else
Canvas.SetTop(Me, (parentHeight - Me.Height))
End If
End If
MoveBeadsBelow(e)
End Sub
MoveBeadsBelow
方法会将选定珠子在向下移动时与之碰撞的任何珠子向下移动
Private Sub MoveBeadsBelow(ByVal e As DragDeltaEventArgs)
Dim earthCanvas As Canvas = CType(Me.Parent, Canvas)
Dim parentHeight As Double = earthCanvas.ActualHeight
For Each eB As EarthBead In earthCanvas.Children
If (eB.EarthNumber <> Me.EarthNumber) Then
Dim My_Y As Double = Canvas.GetTop(Me)
Dim eB_Y As Double = Canvas.GetTop(eB)
If (eB_Y > My_Y) And ((My_Y + Me.Height) > eB_Y) Then
eB.EarthThumb_DragDelta(Nothing, e)
End If
' Ensure bead below has stopped at its limit.
If (eB.EarthNumber = 5) And _
(eB_Y > (parentHeight - (Me.Height * 5))) Then
Canvas.SetTop(eB, (parentHeight - (Me.Height * 5)))
ElseIf (eB.EarthNumber = 4) And _
(eB_Y > (parentHeight - (Me.Height * 4))) Then
Canvas.SetTop(eB, (parentHeight - (Me.Height * 4)))
ElseIf (eB.EarthNumber = 3) And _
(eB_Y > (parentHeight - (Me.Height * 3))) Then
Canvas.SetTop(eB, (parentHeight - (Me.Height * 3)))
ElseIf (eB.EarthNumber = 2) And _
(eB_Y > (parentHeight - (Me.Height * 2))) Then
Canvas.SetTop(eB, (parentHeight - (Me.Height * 2)))
ElseIf (eB.EarthNumber = 1) And _
(eB_Y > (parentHeight - Me.Height)) Then
Canvas.SetTop(eB, (parentHeight - Me.Height))
End If
End If
Next
End Sub
HeavenBead
要移动 HeavenBead
,我们会处理其 Thumb
控件的 DragDelta
事件
Private Sub HeavenThumb_DragDelta(ByVal sender As Object, _
ByVal e As DragDeltaEventArgs) _
Handles HeavenThumb.DragDelta
' Check whether movement is vertical.
If Math.Abs(e.VerticalChange) > Math.Abs(e.HorizontalChange) Then
If e.VerticalChange < 0 Then
MoveHeavenBeadUp(e)
Else
MoveHeavenBeadDown(e)
End If
Else
Exit Sub
End If
End Sub
调用 MoveHeavenBeadUp
方法来向上移动珠子
Private Sub MoveHeavenBeadUp(ByVal e As DragDeltaEventArgs)
Dim y As Double = Canvas.GetTop(Me)
If (Me.HeavenNumber = 1) Then
If (y > 0) Then
Canvas.SetTop(Me, (y - shift))
Else
Canvas.SetTop(Me, 0)
End If
ElseIf (Me.HeavenNumber = 2) Then
If (y > Me.Height) Then
Canvas.SetTop(Me, (y - shift))
Else
Canvas.SetTop(Me, Me.Height)
End If
End If
MoveBeadAbove(e)
End Sub
调用 MoveBeadAbove
方法来移动选定珠子在向上移动时与之碰撞的珠子
Private Sub MoveBeadAbove(ByVal e As DragDeltaEventArgs)
Dim heavenCanvas As Canvas = CType(Me.Parent, Canvas)
For Each hB As HeavenBead In heavenCanvas.Children
If (hB.HeavenNumber <> Me.HeavenNumber) Then
Dim My_Y As Double = Canvas.GetTop(Me)
Dim hB_Y As Double = Canvas.GetTop(hB)
If (hB_Y < My_Y) And (My_Y < (hB_Y + hB.Height)) Then
hB.HeavenThumb_DragDelta(Nothing, e)
End If
' Ensure bead above has stopped at its limit.
If (hB_Y < 0) Then
Canvas.SetTop(hB, 0)
End If
End If
Next
End Sub
MoveHeavenBeadDown
方法在 HeavenThumb
的 DragDelta
事件处理程序中被调用,它会将珠子向下移动
Private Sub MoveHeavenBeadDown(ByVal e As DragDeltaEventArgs)
Dim y As Double = Canvas.GetTop(Me)
Dim heavenCanvas As Canvas = CType(Me.Parent, Canvas)
Dim parentHeight As Double = heavenCanvas.Height
If (Me.HeavenNumber = 2) Then
If (y < (parentHeight - Me.Height)) Then
Canvas.SetTop(Me, (y + shift))
Else
Canvas.SetTop(Me, (parentHeight - Me.Height))
End If
ElseIf (Me.HeavenNumber = 1) Then
If (y < (parentHeight - (Me.Height * 2))) Then
Canvas.SetTop(Me, (y + shift))
Else
Canvas.SetTop(Me, (parentHeight - (Me.Height * 2)))
End If
End If
MoveBeadBelow(e)
End Sub
调用 MoveBeadBelow
方法来移动选定珠子在向下移动时与之碰撞的珠子
Private Sub MoveBeadBelow(ByVal e As DragDeltaEventArgs)
Dim heavenCanvas As Canvas = CType(Me.Parent, Canvas)
Dim parentHeight As Double = heavenCanvas.ActualHeight
For Each hB As HeavenBead In heavenCanvas.Children
If (hB.HeavenNumber <> Me.HeavenNumber) Then
Dim My_Y As Double = Canvas.GetTop(Me)
Dim hB_Y As Double = Canvas.GetTop(hB)
If (hB_Y > My_Y) And ((My_Y + Me.Height) > hB_Y) Then
hB.HeavenThumb_DragDelta(Nothing, e)
End If
' Ensure bead below has stopped at its limit.
If (hB_Y > (parentHeight - hB.Height)) Then
Canvas.SetTop(hB, (parentHeight - Me.Height))
End If
End If
Next
End Sub
重置 WPF Suanpan
还记得我在工作原理部分解释过,要重置珠子,你需要双击分隔梁。你实际双击的是一个 Opacity
设置为零的按钮
Private Sub ResetButton_MouseDoubleClick(ByVal sender As Object, _
ByVal e As System.Windows.Input.MouseButtonEventArgs) _
Handles ResetButton.MouseDoubleClick
' Clear heaven beads.
For Each hCanvas As Canvas In HeavenGrid.Children
hCanvas.Children.Clear()
Next
' Clear earth beads.
For Each eCanvas As Canvas In EarthGrid.Children
eCanvas.Children.Clear()
Next
' Reload beads.
LoadEarthBeads()
LoadHeavenBeads()
End Sub
结论
就这样。希望您喜欢阅读这篇文章,并且它在某种程度上有所帮助。如果您已被影响,成为珠算的实践者,那么恭喜您。我个人更喜欢按钮算术。
历史
- 2011 年 4 月 1 日:首次发布