xpie.ctl

来自「多种图表的绘制及其运用」· CTL 代码 · 共 1,957 行 · 第 1/5 页

CTL
1,957
字号
Private uSaveAsCaption    As String       'the SaveAs dialog picBox caption
Private uOldSelection     As Long
Private uGroupSegment     As Boolean      'indicates if the pie's segments must be grouped
                                          '(Group property of PieSegment type) or not
Private uGroupExplode           As Boolean 'indicates if the group must be exploded (meaningful only if uGroupSegment=True)
Private uGroupExplodeBackColor  As Long   'the group explosion form's backcolor
Private uGroupExplodeForeColor  As Long   'the group explosion form's forecolor
Private uGroupExplodeTitleColor As Long   'the group explosion form's title color
Private uGroupExplodeMenuItems  As String 'the menu's items in the group explosion form
Private uGroupExplodeAllowCommands As Boolean 'indicates if the command buttons in the group form are allowed (meaningful only if uGroupExplode=True)
Private Const GROUP_EXPLODE_MENU_ITEMS = "&OK|&Print"

Private uInfoItems        As String       'the information items (to be displayed in the info picBox)
Private Const INFO_ITEMS = "Value|Description"

Private uInfoPieBackColor       As Long    'the group information picBox background color
Private uInfoPieForeColor       As Long    'the group information picBox foreground color

Public Enum ChartMenuConstants             'the enumerated for menu type
    xcPopUpMenu = 0
    xcButtonMenu
End Enum

Private uMenuType         As ChartMenuConstants 'the menu type.
Private uMenuItems        As String       'the menu's items.
Private Const MENU_ITEMS = "&Save as...|&Print|&Copy|Selection &information|&Legend|&Hide"

Private uCustomMenuItems  As String       'the custom menu's items.
Private Const CUSTOM_MENU_ITEMS = Empty

Private uLegendCaption    As String       'the legend's tooltip string
Private Const LEGEND_CAPTION = "Display legend"

Private Const IDX_SAVE = 0                'the command buttons' indexs
Private Const IDX_PRINT = 1
Private Const IDX_COPY = 2
Private Const IDX_INFO = 3
Private Const IDX_LEGEND = 4

Private uTopMargin        As Single       '--------------------------------------
Private uBottomMargin     As Single       'margins used around the pie content
Private uLeftMargin       As Single       '
Private uRightMargin      As Single
Private uRightMarginOrg   As Single       '--------------------------------------
Private uDisplayDescript  As Boolean      'display description when selectable
Private uChartTitle       As String       'chart title
Private uChartSubTitle    As String       'chart sub title
Private uHotTracking      As Boolean      'marker indicating use of hot tracking

Private offsetX           As Long
Private offsetY           As Long
Private sngRadius         As Single
Private sngPieXCenter     As Single
Private sngPieYCenter     As Single
Private dblPieTotal       As Double

Private bLegendAdded      As Boolean
Private bLegendClicked    As Boolean
Private bDisplayLegend    As Boolean
Private bResize           As Boolean
Private bResizeLegend     As Boolean

Private bProcessingOver   As Boolean      'marker to speed up mouse over effects

Public Type CoordConv
    X As Single
    Y As Single
    Angle As Double
    Radius As Double
End Type

Public Type PieSegment
    ItemID As String
    Group As String
    GroupColor As Long
    SelectedDescription As String
    LegendDescription As String
    Description As String
    Value As Double
    Color As Long
End Type
Private cItems As Collection   'collection of chart items

Public Type PieSegmentAttributes
    AngleFrom As Single
    AngleTo As Single
    Percentage As Double
End Type
Private cItemsAttributes As Collection

Public Type PieGroup
    Value As Double
    Name As String
    Color As Long
End Type
Private cGroups As Collection   'collection of pie groups

Public Type PieGroupAttributes
    AngleFrom As Single
    AngleTo As Single
    Percentage As Double
End Type
Private cGroupsAttributes As Collection

Public Event ItemClick(cItem As PieSegment)
Public Event ItemGroupClick(cItem As PieGroup)
Public Event MenuItemClick(intMenuItemIndex As Integer, stgMenuItemCaption As String)
Public Event GroupMenuItemClick(intMenuItemIndex As Integer, stgMenuItemCaption As String)
Public Event MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
Public Event MouseUp(Button As Integer, Shift As Integer, X As Single, Y As Single)

'-----------------------------------------------
' for Ballon ToolTip
'-----------------------------------------------
Private ttpBalloon As New Tooltip

Private Sub HandleSegments()
    
    Dim sngAngle As Single
    Dim intCount As Integer
    Dim dblPercentage As Double
    Dim dblSegmentTotal As Double
    Dim dblPercentageTotal As Double
    Dim dblPercentageRemainder As Double
    Dim oSAttr As PieSegmentAttributes
    
    On Error GoTo errHandle
    
    'define segments' attributes
    dblSegmentTotal = 100
    dblPercentageTotal = 0
    dblPercentageRemainder = 0
    Set cItemsAttributes = Nothing
    Set cItemsAttributes = New Collection
    With cItems
        For intCount = 1 To .Count
            'total segment percentage is decreased only if we are after the first segment
            If (intCount > 1) Then
                oSAttr.AngleFrom = cItemsAttributes(intCount - 1).AngleTo
                dblSegmentTotal = dblSegmentTotal - cItemsAttributes(intCount - 1).Percentage
            Else
                oSAttr.AngleFrom = 0
            End If
            'calculate percentage/angle and add to the proper collection
            If intCount = .Count Then
                'last item's percentage corresponds to the remainder (100-assigned)
                dblPercentage = 100 - dblPercentageTotal
            Else
                'item by item, add the previous remainder value
                '(difference between double value and rounded value)
                dblPercentage = ((.Item(intCount).Value / dblPieTotal) * 100) + dblPercentageRemainder
                If (dblPercentage - Round(dblPercentage, 2)) > 0 Then
                    'the remainder is saved only in case the value was not rounded by excess
                    dblPercentageRemainder = dblPercentage - Round(dblPercentage, 2)
                Else
                    dblPercentageRemainder = 0
                End If
                'increment total percentage
                dblPercentageTotal = dblPercentageTotal + dblPercentage
            End If
            sngAngle = 360 * dblPercentage / 100
            oSAttr.AngleTo = oSAttr.AngleFrom + sngAngle
            oSAttr.Percentage = Round(dblPercentage, 2)
            cItemsAttributes.Add oSAttr
        Next intCount
    End With
    Exit Sub

errHandle:
    Exit Sub
End Sub

Private Function Radians(ByVal Degrees As Single) As Double
    Radians = CDbl(Degrees) * PI / 180
End Function

Private Function ConvertCoordinates(X As Single, Y As Single) As CoordConv
    
    Dim sngY1 As Single
    Dim sngX1 As Single
    Dim sngAngle As Single
    Dim oConv As CoordConv
    
    sngX1 = X / Screen.TwipsPerPixelX
    sngY1 = Y / Screen.TwipsPerPixelY
    sngX1 = (sngPieXCenter - sngX1)     'calculate in respect to center
    sngY1 = (sngPieYCenter - sngY1)     'calculate in respect to center
    sngAngle = CSng(Atn(sngY1 / IIf(sngX1 = 0, 0.00000001, sngX1))) * 180 / PI
    'these calculations are valid in case the graduation is as follows:
    '
    '              0
    '              |
    '              |
    ' 90-----------+----------270
    '              |
    '              |
    '             180
    '
    If (sngX1 < 0) And (sngY1 >= 0) Then
        sngAngle = 270 + Abs(sngAngle)
    ElseIf (sngX1 >= 0) And (sngY1 >= 0) Then
        sngAngle = 90 - sngAngle
    ElseIf (sngX1 >= 0) And (sngY1 < 0) Then
        sngAngle = 90 + Abs(sngAngle)
    ElseIf (sngX1 < 0) And (sngY1 < 0) Then
        sngAngle = 270 - Abs(sngAngle)
    End If
        
'    'these calculations are valid in case the graduation is as follows:
'    '
'    '              90
'    '              |
'    '              |
'    ' 180----------+----------0
'    '              |
'    '              |
'    '             270
'    '
'    If (sngX1 < 0) And (sngY1 >= 0) Then
'        sngAngle = Abs(sngAngle)
'    ElseIf (sngX1 >= 0) And (sngY1 >= 0) Then
'        sngAngle = 180 - sngAngle
'    ElseIf (sngX1 >= 0) And (sngY1 < 0) Then
'        sngAngle = 180 - sngAngle
'    ElseIf (sngX1 < 0) And (sngY1 < 0) Then
'        sngAngle = 360 - sngAngle
'    End If

    With oConv
        .X = sngX1
        .Y = sngY1
        .Angle = sngAngle
        .Radius = Sqr(sngX1 * sngX1 + sngY1 * sngY1)
    End With
    
    ConvertCoordinates = oConv

End Function

Private Sub DrawSegmentText(sngAngleFrom As Single, sngAngleTo As Single, intItemIdx As Integer)
    
    On Error GoTo errHandle

    Dim lngW As Long
    Dim lngH As Long
    Dim sngX1 As Single
    Dim sngY1 As Single
    Dim lngColor As Long
    Dim stgDesc As String
    Dim sngXoff As Single
    Dim sngYoff As Single
    Dim sngAngle As Single
    Dim sngAngleRad As Single

    sngAngle = sngAngleFrom + (sngAngleTo - sngAngleFrom) / 2
    sngAngleRad = Radians(sngAngle)
    sngXoff = Sin(sngAngleRad) * sngRadius
    sngYoff = Cos(sngAngleRad) * sngRadius

    With UserControl
        lngColor = .ForeColor
        If uGroupSegment = False Then
            stgDesc = cItems(intItemIdx).Description
            .ForeColor = IIf(((intItemIdx - 1) = uSelected And uSelectable), uSelectedColor, uMarkerColor)
        Else
            stgDesc = cGroups(intItemIdx).Name
            .ForeColor = IIf(((intItemIdx - 1) = uSelected And uSelectable), uSelectedColor, uMarkerColor)
        End If
    
        sngX1 = sngPieXCenter - sngXoff
        sngY1 = sngPieYCenter - sngYoff
        '
        '              0
        '              |
        '              |
        ' 90-----------+----------270
        '              |
        '              |
        '             180
        '
        lngW = .TextWidth(stgDesc) / Screen.TwipsPerPixelX / 2
        lngH = .TextHeight(stgDesc) / Screen.TwipsPerPixelY / 2
        If sngAngle >= 0 And sngAngle < 90 Then
            sngX1 = sngX1 - lngW
            sngY1 = sngY1 - lngH
        ElseIf sngAngle >= 90 And sngAngle < 180 Then
            sngX1 = sngX1 - lngW
            sngY1 = sngY1 + lngH
        ElseIf sngAngle >= 180 And sngAngle < 270 Then
            sngX1 = sngX1 + lngW
            sngY1 = sngY1 + lngH
        Else
            sngX1 = sngX1 + lngW
            sngY1 = sngY1 - lngH
        End If
        PrintRotText .hDC, stgDesc, sngX1, sngY1, 0
        .ForeColor = lngColor
    End With
    Exit Sub
    
errHandle:
    Exit Sub
    

End Sub

Private Sub GroupSegments()
    
    'important: before grouping segments, it has to be defined
    'segments' attributes!!!

    Dim intIdx As Integer
    Dim intCount As Integer
    Dim oGroup As PieGroup
    Dim stgGroup As String
    Dim stgGroups As String
    Dim stgColor As String
    Dim stgGroupsColor As String
    Dim dblSegmentTotal As Double
    Dim oGAttr As PieGroupAttributes
    Dim dblPercentage As Double
    Dim sngAngle As Single
    Dim dblPercentageTotal As Double
    Dim dblPercentageRemainder As Double
    
    'verify how may groups there are in segments
    dblSegmentTotal = 0
    stgGroups = Empty
    stgGroupsColor = Empty
    For intCount = 1 To cItems.Count
        stgGroup = cItems(intCount).Group
        If InStr(stgGroups, stgGroup) = 0 Then
            'add group name to the list
            stgGroups = stgGroups & stgGroup & Chr$(0)
            'and group color
            stgGroupsColor = stgGroupsColor & CStr(cItems(intCount).GroupColor) & Chr$(0)
        End If
    Next
    
    'set new groups' collection
    Set cGroups = Nothing
    Set cGroups = New Collection
    intIdx = 1
    While intIdx > 0
        stgGroup = TokenByPos(stgGroups, intIdx, Chr$(0))
        If stgGroup = "" Then
            'no more items in the groups' list
            intIdx = -1
        Else
            dblSegmentTotal = 0
            stgColor = TokenByPos(stgGroupsColor, intIdx, Chr$(0))
            For intCount = 1 To cItems.Count
                If cItems(intCount).Group = stgGroup Then
                    dblSegmentTotal = dblSegmentTotal + cItems(intCount).Value
                End If
            Next
            With oGroup
                .Value = dblSegmentTotal
                .Name = stgGroup
                .Color = CLng(stgColor)
            End With
            cGroups.Add oGroup
            intIdx = intIdx + 1
        End If
    Wend
    
    'groups' attributes
    dblSegmentTotal = 100
    Set cGroupsAttributes = Nothing
    Set cGroupsAttributes = New Collection
    dblPercentageTotal = 0
    dblPercentageRemainder = 0
    With cGroups
        For intCount = 1 To .Count
            'total segment percentage is decreased only if we are after the first segment
            If intCount > 1 Then
                oGAttr.AngleFrom = cGroupsAttributes(intCount - 1).AngleTo
                dblSegmentTotal = dblSegmentTotal - cGroupsAttributes(intCount - 1).Percentage
            Else
                oGAttr.AngleFrom = 0
            End If
            'calculate percentage, basing on percentage of single segments, belonging to the group
            dblPercentage = 0
            For intIdx = 1 To cItems.Count
                If cItems(intIdx).Group = .Item(intCount).Name Then
                    dblPercentage = dblPercentage + cItemsAttributes(intIdx).Percentage
                End If
            Next
            sngAngle = 360 * dblPercentage / 100
            oGAttr.AngleTo = oGAttr.AngleFrom + sngAngle
            oGAttr.Percentage = dblPercentage
            cGroupsAttributes.Add oGAttr

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?