Skip to content

Access to the underlying .NET chart object#102

Merged
simra merged 5 commits intofslaborg:masterfrom
mtikilay:master
Apr 15, 2016
Merged

Access to the underlying .NET chart object#102
simra merged 5 commits intofslaborg:masterfrom
mtikilay:master

Conversation

@mtikilay
Copy link
Copy Markdown
Contributor

Exposed access to the underlying System.Windows.Forms.DataVisualization.Charting.Chart object via a "visitor"-esque member method ApplyToChart(...), in order to enable programmatic control over the chart form window. This control is already possible to perform manually via the Properties tab of the chart form.

This enables us to use charting with the following example pattern:

let inputData = [1;2;3]
inputData
|> Chart.Line
|> fun c      -> c.WithLegend()  // add legend using the existing method
|> fun c      -> ( c, c.ShowChart() )    // create the chart form
|> fun (c, _) -> (c, c.ApplyToChart( fun c -> c.ChartAreas.[0].CursorX.IsUserSelectionEnabled <- true ) )   // can now modify chart form using all of the available .NET Chart API by chaining lines like this one
|> ignore   // avoid displaying the chart twice

 System.Windows.Forms.DataVisualization.Charting.Chart object via a
 "visitor"-esque member method ApplyToChart(...), in order to enable
 programmatic control over the chart form window (which is already accessible
 to the user manually via the Properties tab of the chart form). This enables
 us to use charting with the following example pattern:

let inputData = [1;2;3]
inputData
|> Chart.Line
|> fun c      -> c.WithLegend()  // add legend using the existing method
|> fun c      -> ( c, c.ShowChart() )    // create the chart form
|> fun (c, _) -> (c, c.ApplyToChart( fun c -> c.ChartAreas.[0].CursorX.IsUserSelectionEnabled <- true ) )   // can now modify chart form using all of the available .NET Chart API by chaining lines like this one
|> ignore   // avoid displaying the chart twice
@tpetricek
Copy link
Copy Markdown
Member

I like the idea!

That said, the way you wrote the sample confuses me - would it be possible to use this with the ordinary . style and write:

let inputData = [1;2;3]
Chart.Line(inputData)
  .ApplyToChart(fun c -> c.ChartAreas.[0].CursorX.IsUserSelectionEnabled <- true )
  .WithLegend()

I guess then the ApplyToChart method would also have to return the modified ch value, but aside from that, I think it should work.

Also, could you add a brief note about this somewhere in the documentation?

@mtikilay
Copy link
Copy Markdown
Contributor Author

Thanks for the feedback!

The way you suggest writing the sample was indeed the way I wanted to make it work too, but failed. The problem I ran into though was that Chart.Line(...) does not end up creating a form with the fully formed .NET Chart object in time for .ApplyToChart(...) to work. In particular, the list c.ChartAreas is not populated in time if you chain the calls as you suggested -- hence the rigmarole in my code sample with running ShowChart() first. And in turn, the necessity of running ShowChart() first necessitates running .WithLegend() before ShowChart(), as otherwise the request to show the legend is too late to get rendered. Effectively, there are things that seemingly need to happen before the chart is rendered in a window, and then some things that need to happen after.

I hope this explains why the sample is as it is (also, I'm not yet hugely proficient in F#, so please bear with it). If I make ApplyToChart return the chart object again, a perhaps clearer piece of sample code would be:

inputData
|> Chart.Line
// do things necessary before chart is rendered
|> fun c -> c.WithLegend( FontSize = 5.0 )  
             .WithStyling( Color = System.Drawing.Color.Red )
// create the chart form and render the chart
|> fun c -> ( c, c.ShowChart() )    
// do things that can only be done after the chart is rendered
|> fun ( c, _ ) -> c.ApplyToChart( fun c -> c.ChartAreas.[0].CursorX.IsUserSelectionEnabled <- true ) 
                    .ApplyToChart( fun c -> c.ChartAreas.[0].CursorY.IsUserSelectionEnabled <- true )
|> ignore  // avoid displaying the chart twice

Please let me know if you agree with the above and if you find the code sample above acceptable (also, if you want to name ApplyToChart(...) some other name!). I'm very happy to tidy up this up, document it and resubmit the pull request if it's all agreed.

…ls, like in the example below:

inputData
|> Chart.Line
// do things necessary before chart is rendered
|> fun c -> c.WithLegend( FontSize = 5.0 )
             .WithStyling( Color = System.Drawing.Color.Red )
// create the chart form and render the chart
|> fun c -> ( c, c.ShowChart() )
// do things that can only be done after the chart is rendered
|> fun ( c, _ ) -> c.ApplyToChart( fun c -> c.ChartAreas.[0].CursorX.IsUserSelectionEnabled <- true )
                    .ApplyToChart( fun c -> c.ChartAreas.[0].CursorY.IsUserSelectionEnabled <- true )
|> ignore  // avoid displaying the chart twice
@tpetricek
Copy link
Copy Markdown
Member

Hmm, I think requiring a user who does not expect this to write code like the one in your sample is a bit too complicated - it requires pretty good understanding of the internals, which we would ideally like to hide. But I really like the feature you're suggesting :-) we should find some way to make it work!

I see why it's not working though. Another way to implement this would be to just store the function somewhere (perhaps create a new mutable property of GenericChart named CustomizationFunctions?) and then run it immediately after the chart is created in ShowChart?

This should work, but it will let us keep the simple notation.

@mtikilay
Copy link
Copy Markdown
Contributor Author

Great to hear you like the idea. I have also thought of "caching" calls until after ShowChart, like you suggest with CustomizationFunctions -- let me give it a go and I'll get back to you on this thread/PR.

mt99 added 2 commits February 17, 2016 17:52
…til the point of drawing of the actual chart, as discussed in #102 (comment). ApplyToChart() is now usable with the existing simple dot-notation.
@mtikilay
Copy link
Copy Markdown
Contributor Author

Hello again. Apologies for not doing this sooner. I have now pushed a couple more commits that implement the idea of exposing the underlying chart object for modification using the simple existing dot notation. The last commit has a couple of example uses of ApplyToChart(). I hope it's easier to use now.

@tpetricek
Copy link
Copy Markdown
Member

Thanks - and sorry for the long delay. The latest version looks good to me!

@simra - Are you doing a new release? Can we include this?

@simra
Copy link
Copy Markdown
Contributor

simra commented Apr 12, 2016

Seems like a good plan but I'm not up to speed on how to do this. Let's merge the PR and between you and I we can generate the release.

@simra
Copy link
Copy Markdown
Contributor

simra commented Apr 12, 2016

One quick comment about the PR which is that the customization functions will be executed in reverse order from the order in which ApplyToChart is called. I don't see any issues with this but we might want to make a note of it in the docs so as not to lead to unexpected behaviors. If it's not too much trouble could you add this note to the sample code? Alternatively we could accept the PR and I'll add a quick additional one to cover this note- let us know your preference.

…d, if only for the sake of clarity. This was noticed by @simra.
@mtikilay
Copy link
Copy Markdown
Contributor Author

Oh man, apologies. Silly error, now corrected. This behaviour was at best not intuitive.

@simra simra merged commit b70b4a6 into fslaborg:master Apr 15, 2016
@simra
Copy link
Copy Markdown
Contributor

simra commented Apr 15, 2016

Looks good. Thanks for the fix!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants