首页 > 解决方案 > 自定义 LegendItem 时 ChartInvalidate 期间出现 NullReferenceException

问题描述

我在powershell中为IIS日志构建了一个图形引擎,点的大小由绘制的点数(50-50k)缩放。当尺寸很小时,图例会同时调整,所以我正在尝试调整图例中标记的大小。

我创建了一个事件函数:

$chart1_CustomizeLegend = { 
        Param([Object]$sender , [System.Windows.Forms.DataVisualization.Charting.CustomizeLegendEventArgs]$e ) 
        try
        {
        if ($e.LegendItems.Count -gt 0)
        {
            Write-Host found $e.LegendItems.Count legend items

            for ($i = 0 ; $i -lt $e.LegendItems.Count ; $i++)
            {
                write-host processing $i

                if ($e.LegendItems[$i] -eq $null)
                {
                    write-host item is null
                }
                else
                {
                    write-host item is not null
                }
                #update marker size
                $e.LegendItems[$i].MarkerSize=10
            }
         }
         } catch [System.Exception] {
            Write-Error "failed to customize legend-item" 
            $_ | Format-List -Force | Out-String | Write-Error
         }
        }#CustomizeLegend

并注册:

$custjob = Register-ObjectEvent -InputObject $chart1 -EventName CustomizeLegend -Action $chart1_CustomizeLegend

我可以看到它被调用了,但是它碰到了 catch 块:

未能自定义图例项

异常:System.NullReferenceException:对象引用未设置为对象的实例。在 System.Windows.Forms.DataVisualization.Charting.Chart.Invalidate() 在 System.Windows.Forms.DataVisualization.Charting.ChartPicture.Invalidate() 在 System.Windows.Forms.DataVisualization.Charting.ChartElementCollection`1.Invalidate()在 System.Windows.Forms.DataVisualization.Charting.ChartElement.Invalidate() 在 System.Windows.Forms.DataVisualization.Charting.Legend.Invalidate(Boolean invalidateLegendOnly) 在 CallSite.Target(Closure , CallSite , Object , Int32 ) TargetObject : CategoryInfo : 未指定: (:) [],

但在此之前它写出该项目不是空的,所以我知道我没有做任何空引用异常。这似乎是一个 .NET 问题,但很难确定。

它似乎在图表的 SaveImage 期间发生。

 $ChartName = "IIS Response Time {0}-{1}, Series By Call Type, time-response scatter" -f $data.Folder,$data.File
        Write-progress -status "Preparing $ChartName containing $($data.data.count) points" -activity "Preparing $ChartName, getting data"
        $DataArray  = $data.data  |where {$_["time"]}| where $FilterSB | where $CoreFilterSB

    $chart1 = New-object System.Windows.Forms.DataVisualization.Charting.Chart
    $chart1.Width = 1200*$GraphScale
    $chart1.Height = 800*$GraphScale
    $chart1.BackColor = [System.Drawing.Color]::White

    # title
       $chart1.Titles.Add(("{0} ({1} of {2} points) " -f $ChartName, @($DataArray).Count, $data.data.count)) > $null
       $chart1.Titles[0].Font = "Arial,{0}pt" -f (8*$GraphScale)
       $chart1.Titles[0].Alignment = "topLeft"

    # chart area
       $chartarea = New-Object System.Windows.Forms.DataVisualization.Charting.ChartArea
       $chartarea.Name = "ChartArea1"
       $chartarea.AxisY.Title = "Response time (MS)"
       $chartarea.AxisX.Title = "Time"
         $chartarea.AxisY.LogarithmBase = 2
       #$chartarea.axisy.IntervalAutoMode = [System.Windows.Forms.DataVisualization.Charting.IntervalAutoMode]::FixedCount
       $chartarea.AxisX.Interval = 60
       $chartarea.AxisX.IntervalType = [System.Windows.Forms.DataVisualization.Charting.DateTimeIntervalType]::Minutes
       $chartarea.AxisX.LabelStyle.Font = "Arial,24pt"
       $chartarea.AxisX.LabelStyle.Format="HH:mm"
       #$chartarea.AxisX.Minimum = $StartTime.TotalMinutes
       #$chartarea.AxisX.Maximum = $EndTime.TotalMinutes
       #$chartarea.AxisX.Minimum = 0*60 #17:30
       #$chartarea.AxisX.Maximum = 24.0*60 #20:00
       $chartarea.AxisY.LabelStyle.Font = "Arial,24pt"
       $chartarea.AxisY.Minimum = 256
        if ($SetExceedingToMaxCallTime)
        {
            $chartarea.AxisY.Maximum = [Math]::Pow([Math]::Round([Math]::Sqrt($MaxCallTimeToChart)+0.5,0),2)*1024

        }
        else
        {
            $chartarea.AxisY.Maximum = 131072
        }
       $chartarea.AxisY.Interval = 1
       $chartarea.AxisY.MinorGrid.Enabled = $true
       $chartarea.AxisY.IsLogarithmic=$true
       $chartarea.AxisY.MinorGrid.LineColor = [System.drawing.color]::LightGray

       $chart1.ChartAreas.Add($chartarea)

        # legend
        if ($ShowLegend)
        {
write-host add event to scatter chart
       $custjob = Register-ObjectEvent -InputObject $chart1 -EventName CustomizeLegend -Action $chart1_CustomizeLegend
       write-host 'create legend scatter chart'
       $legend = New-Object system.Windows.Forms.DataVisualization.Charting.Legend
       $legend.name = "Legend1"
       $chart1.Legends.Add($legend)
        write-host 'format legend scatter chart'
       $chart1.Legends["Legend1"].Font = "Arial,{0}pt" -f (6 * $GraphScale)

       $chart1.Legends["Legend1"].Docking =[System.Windows.Forms.DataVisualization.Charting.Docking]::Right
       #$chart1.Legends["Legend1"].LegendItems
       }
    # data series
       #$chart1.Series["wGLN"].Legend = "Legend1"
       #$chart1.Series["wGLN"].color = "#62B5CC"



       #dev
       $labelNum = 0


       $series = @()
       $pointsadded = 0
       $DataArray | ForEach-Object {
            if ($pointsadded % 100 -eq 0) {  write-host ("Adding data Series [{0}] Points [{1}]" -f $series.Count,$pointsadded)       Write-progress -status ("Adding data Series [{0}] Points [{1}]" -f $series.Count,$pointsadded) -activity "Preparing $ChartName" }

            $seriesname = Invoke-Command $seriesSB -ArgumentList $_
            if (!($series -contains $seriesname))
            {
                write-host 'create a series'
              $theSeries = $chart1.Series.Add($seriesname)
               $theSeries.ChartType = "Point"
               $theSeries.BorderWidth  = 2
               $theSeries.IsVisibleInLegend = $true
               $theSeries.chartarea = "ChartArea1"
               $theSeries.MarkerSize = 6/[Math]::Sqrt([Math]::Sqrt([Math]::Max($DataArray.Count/1000,1)))*$GraphScale
               $theSeries.BorderColor=$theSeries.Color
               $theSeries.BorderWidth = 2
               write-host 'add series'
               $series += $seriesname

               $theSeries.IsVisibleInLegend=$true
               $theseries.Legend = "Legend1"
           }
           else
           {
                #series dictionary is case sensitive however IIS logs are not, 
                #use whereclause instead of dictionary lookup to ensure case insensitive match!
                $theSeries = $chart1.Series | where {$_.Name -eq $seriesname}
           }

           if ($_['time-taken'] -gt 0)
           {
               if ($SetExceedingToMaxCallTime)
               {
                     $theSeries.Points.addxy( [DateTime]($_["time"]) , [Math]::Min($MaxCallTimeToChart*1000,($_['time-taken']))) > $null; 
               }
               else
               {
                   $theSeries.Points.addxy( [DateTime]($_["time"]) , (($_['time-taken']))) > $null; 
               }
           }
           $pointsadded++
        }#each dataarray

        foreach ($item in $chart1.Legends["Legend1"].CustomItems) {$item.MarkerSize = 8}
       #$chart1.Show()
       write-host "save charte $chartname"
       $filename = "$DestinationPath\$ChartName.png"
       $chart1.SaveImage($filename,"png")

标签: .netpowershellexceptionchartsnullreferenceexception

解决方案


我最初的想法是以下内容不正确:

$e.LegendItems[$i].MarkerSize=10

从我用我的 Google-foo 读到的一点点来看,你不能markersize直接改变。您必须更改SeriesSymbolSize. 我可能错了(我以前从未合作Datavisualization.Charting过)

尝试更换:

for ($i = 0 ; $i -lt $e.LegendItems.Count ; $i++)
{
    write-host processing $i

    if ($e.LegendItems[$i] -eq $null)
    {
        write-host item is null
    }
    else
    {
        write-host item is not null
    }
    #update marker size
    $e.LegendItems[$i].MarkerSize=10
}

和:

foreach($item in $e.LegendItems)
{
    item.Cells(0).SeriesSymbolSize = 10
}

推荐阅读