Jump to content
Software FX Community

JuanC

Staff
  • Posts

    863
  • Joined

  • Last visited

  • Days Won

    1

Everything posted by JuanC

  1. The problem is that at the time you are "copying" the brushes used in the first 5 series for the second set of 5 series, the palette is still glass so you are copying the glass colors, after that you are setting the Palette which will result in the palette being used in the first set of series (because you never modified their properties) but NOT on the second set of series because your settings override the palette. The solution is to move the setting of the Style before both series loops. JuanC
  2. Can you describe how you populate the chart when the combobox is changed? Are you passing data manually? Are you using ObservableCollection? JuanC
  3. We have fixed most of these issues in build 3671 available as a hotfix. I noticed in your sample app you are adding a point and removing another one, in ChartFX 8.1 we will support a real time API that would make this scenario perform better, note that in the current build we will "re-render" all the points when a point is added/removed. In 8.1 if you use the realtime API we will only render the new value. Also note that we are more efficient when processing changes in an ObservableCollection that is bound to the chart instead of a specific series. The reclaiming space issue you mentioned in your last post was also fixed. JuanC
  4. Can you expand on what do you want to achieve in Q1? Feel free to post a modified chart image if needed. JuanC
  5. Unfortunately this is "by design". I can see that when the zoom mode is Stripe we could easily enable AutoMargin on the Y axis so I would expect we will support this in future versions (likely the 8.1 branch) JuanC
  6. We are actively working on this issue, I apologize for the lack of confirmation. As soon as we have a hotfix available we will respond to this thread. I noticed in your code you are binding each series to your ObservableCollection even though both series point to the same collection (different fields), how many collections do you have in your real-world app? I ask this because we think we have already fixed the scenario when you use Chart.ItemsSource instead of Series.ItemsSource (which obviously only work if your app only uses 1 collection). Also will your points be equally spaced in the X axis? JuanC
  7. We are not aware of any particular settings (including styling or using AdvanvedSettings) that would prevent this from working. Can you duplicate this issue in a dummy app? JuanC
  8. When you set Mode to Selection we start the selection process, so at that point the user will be able to select the area and zoom will happen when the mouse is up. No additional code is required. JuanC
  9. Yes, we were able to reproduce the issue on our latest build, there is a workaround which involves using binding instead of passing data manually, e.g. in RandomizeChart // Set the data for the two series /* for (int i = 0; i < 10; i++) { chart1.Data[0, i] = random.Next(1, 25); chart1.Data[1, i] = random.Next(1, 25); } */ List<MyData> data = new List<MyData>(); for (int i = 0; i < 10; i++) { int n0 = random.Next(1, 25); int n1 = random.Next(1, 25); data.Add(new MyData() { V0 = n0, V1 = n1 }); } sa1.BindingPath = "V0"; sa2.BindingPath = "V1"; chart1.ItemsSource = data; And this class for your data internal class MyData { public double V0 { get; set; } public double V1 { get; set; } } I am still unsure of what scenario you are trying to test, one thing about this workaround, because of the way we process the data, even though you are setting the ItemsSource collection in a loop that executes one thousand times, we will only process the data once as we delay this for performance reasons JuanC
  10. Note that not only you want your lines to be offset but there are 4 values plotted for the bars but 5 for the lines. To achieve this you need something like this Data Layer public class PercentChange { public string Period { get; set; } public double YourPortfolio { get; set; } public double DJIA { get; set; } } public class Growth { public double YourPortfolio { get; set; } public double DJIA { get; set; } public static List<Growth> BuildGrowthTable (List<PercentChange> percentChanges, double startingValue) { List<Growth> growth = new List<Growth>(); double yourPortfolio = startingValue; double djia = startingValue; growth.Add(new Growth() { YourPortfolio = yourPortfolio, DJIA = djia }); foreach (PercentChange percentChange in percentChanges) { yourPortfolio += (percentChange.YourPortfolio * yourPortfolio); djia += (percentChange.DJIA * djia); growth.Add(new Growth() { YourPortfolio = yourPortfolio, DJIA = djia }); } return growth; } } Chart Code List<PercentChange> percentChanges = new List<PercentChange>(); percentChanges.Add(new PercentChange() { Period = "1Q08", YourPortfolio = 0.034, DJIA = 0.05 }); percentChanges.Add(new PercentChange() { Period = "2Q09", YourPortfolio = 0.062, DJIA = 0.035 }); percentChanges.Add(new PercentChange() { Period = "3Q09", YourPortfolio = -0.021, DJIA = 0.03 }); percentChanges.Add(new PercentChange() { Period = "4Q09", YourPortfolio = 0.095, DJIA = 0.07 }); chart1.Series.Clear(); chart1.Series.Add(new SeriesAttributes("YourPortfolio")); chart1.Series.Add(new SeriesAttributes("DJIA")); chart1.AxisX.Labels.BindingPath = "Period"; chart1.AxisY.Labels.Format = AxisFormat.Percentage; chart1.AxisY.Position = AxisPosition.Far; chart1.AxisY.Grids.Major.Visibility = Visibility.Collapsed; chart1.ItemsSource = percentChanges; // Growth List<Growth> growth = Growth.BuildGrowthTable(percentChanges, 10000); Axis axisXGrowth = new Axis(); axisXGrowth.Visibility = Visibility.Collapsed; axisXGrowth.AxisStyle |= AxisStyles.StartAtAxis; chart1.AxesX.Add(axisXGrowth); Axis axisYGrowth = new Axis(); axisYGrowth.ForceZero = false; axisYGrowth.Min = 5000; axisYGrowth.Max = 20000; axisYGrowth.Step = 5000; axisYGrowth.Labels.Format = AxisFormat.Currency; axisYGrowth.Position = AxisPosition.Near; axisYGrowth.Grids.Major.Visibility = Visibility.Collapsed; axisYGrowth.Grids.Minor.TickMark = TickMark.None; chart1.AxesY.Add(axisYGrowth); BuildSeriesGrowth("YourPortfolio", axisXGrowth, axisYGrowth, growth, chart1.Series[0]); BuildSeriesGrowth("DJIA", axisXGrowth, axisYGrowth, growth, chart1.Series[1]); chart1.Style = ChartFX.WPF.Motifs.Basic.Style; } private SeriesAttributes BuildSeriesGrowth (string name, Axis axisX, Axis axisY, IEnumerable itemsSource, SeriesAttributes seriesModel) { SeriesAttributes seriesGrowth = new SeriesAttributes(name); seriesGrowth.AxisX = axisX; seriesGrowth.AxisY = axisY; seriesGrowth.ItemsSource = itemsSource; seriesGrowth.Gallery = Gallery.Line; seriesGrowth.StrokeThickness = 2; seriesGrowth.Marker.Size = 10; // Make sure we use the same attributes as percent series seriesGrowth.Fill = seriesModel.Fill; seriesGrowth.Stroke = seriesModel.Stroke; seriesGrowth.Marker.Fill = seriesModel.Fill; seriesGrowth.Marker.Stroke = seriesModel.Stroke; // Hide this series from the legend box int seriesGrowthIndex = chart1.Series.Count; chart1.LegendBox.ItemAttributes[chart1.Series, seriesGrowthIndex].Visibility = Visibility.Collapsed; chart1.Series.Add(seriesGrowth); return seriesGrowth; } The only major difference between the chart generated by this code and your screenshot is that the 0 value for both axes will not be aligned, I will post later sample code on how to achieve this JuanC
  11. Can you post an image of the chart you are getting now and annotate where would you want to see the additional information? I am afraid there might not be enough space. JuanC
  12. I meant to say I get a chart with 10 points (all of them visible) and only 1 series. JuanC
  13. JuanC

    Multiple Y Axes

    There are 2 ways of achieving what you are looking for, first let's initialize our chart int numberOfSeries = 3; chart1.Series.Clear(); for (int i = 0; i < numberOfSeries; i++) chart1.Series.Add(new SeriesAttributes() { Gallery = Gallery.Bar }); for (int i = 0; i < numberOfSeries; i++) chart1.Series.Add(new SeriesAttributes() { Gallery = Gallery.Line }); The first approach uses PointAttributes which allow you to set visual attributes on a per-point basis, our highlighting code will then try to highlight all elements sharing the same point attributes // Aproach 1: Using Point Attributes // This works well if you do not need additional per-point attributes and the number of points is not too big int numberOfPoints = 5; for (int i = 0; i < numberOfSeries; i++) { SeriesAttributes seriesSource = chart1.Series; PointAttributes pointAttr = new PointAttributes(); pointAttr.Fill = seriesSource.Fill; pointAttr.Stroke = seriesSource.Stroke; pointAttr.Marker.Fill = seriesSource.Fill; pointAttr.Marker.Stroke = seriesSource.Stroke; // Replace this with your series description // if you do not set the PointAttributes content it will not appear in the legend box pointAttr.Content = string.Format("Series {0}", i + 1); for (int j = 0; j < numberOfPoints; j++) { chart1.Points[i, j] = pointAttr; chart1.Points[i + numberOfSeries, j] = pointAttr; } } chart1.LegendBox.ItemAttributes[chart1.Series].Visibility = Visibility.Collapsed; The drawback is that you have to assign the point attributes for all points on each series although we are sharing the same instance. When we are drawing there will be a small performance hit as we will "search" a dictionary to see if you have set attributes for a specific series/point combination. For a small data set this should not be noticeable. The second approach is based on building your own Highlight Resolver that decides what gets highlighted/dimmed, Note that we still have to make sure (if desired) that visual attributes are shared between the line and the bar for (int i = 0; i < numberOfSeries; i++) { SeriesAttributes seriesSource = chart1.Series; SeriesAttributes seriesTarget = chart1.Series[i + numberOfSeries]; seriesTarget.Fill = seriesSource.Fill; seriesTarget.Stroke = seriesSource.Stroke; seriesTarget.Marker.Fill = seriesSource.Fill; seriesTarget.Marker.Stroke = seriesSource.Stroke; chart1.LegendBox.ItemAttributes[chart1.Series, i + numberOfSeries].Visibility = Visibility.Collapsed; } chart1.Highlight.CreatingResolver += new HighlightResolverEventHandler(OnCreatingResolver); private void OnCreatingResolver (object sender, HighlightingEventArgs args) { IRequiresIndex requiresIndex = null; LogicalVisualItem visualItem = args.Object as LogicalVisualItem; if (visualItem != null) requiresIndex = visualItem.Series; if (requiresIndex != null) { int seriesIndex = requiresIndex.Index; args.Resolver = new MyResolver(seriesIndex, 3); } } private class MyResolver : IHighlightResolver { private int m_targetSeries; private int m_groupCount; public MyResolver(int targetSeries, int groupCount) { m_targetSeries = targetSeries; m_groupCount = groupCount; } void IHighlightResolver.ApplyRemove (Chart chart, bool apply) { } object IHighlightResolver.HighlightData { get { return null; } } HighlightState IHighlightResolver.IsItemHighlighted (ILogicalHighlightableItem item, bool apply) { IRequiresIndex requiresIndex = item.Series; if (requiresIndex == null) return HighlightState.Normal; int series = requiresIndex.Index; if ((series % m_groupCount) == (m_targetSeries % m_groupCount)) return HighlightState.Highlighted; else return HighlightState.Dimmed; } HighlightState IHighlightResolver.IsObjectHighlighted (object item, IPlotArea plotArea) { return HighlightState.Normal; } bool IHighlightResolver.RequiresRemoval (IHighlightResolver resolver) { return true; } ResolverStyles IHighlightResolver.Style { get { return ResolverStyles.PerSeries; } } } For this code to compile you have to add using clauses for ChartFX.WPF.Internal and ChartFX.WPF.Internal.Highlight JuanC
  14. Your code works fine (only 1 series and 9 points) using our current build, can you check the version of the ChartFX.WPF.dll file? You can download our most recent hotfix here, this page uses a control that will try to update your ChartFX files so if you have UAC enabled you must run IE as an Administrator. JuanC
  15. JuanC

    Zoom Problem

    We expect any axis used in the chart to be in either the AxesY or AxesX collection, please add this line of code after you customize your new axis chart1.AxesY.Add(a); e.g. Axis a = new Axis(); a.Min = 0; a.Max = 50; chart1.AxesY.Add(a); ... I Apologize for our delay answering this question. JuanC
  16. JuanC

    Multiple Y Axes

    I think we have the appropriate hooks for this to work but I would like to understand more about your scenario, can you describe (or post a sample) what your data look like and what the series legend items represent? JuanC
  17. JuanC

    Line Chart Colors

    Because we paint our lines using a WPF polyline you cannot have a border but you could use the following template for your line series <DataTemplate x:Key="Line"> <DataTemplate.Resources> <sys:Double x:Key="cfxDefMarkerSize">10</sys:Double> <sys:Double x:Key="cfxDefMarkerStrokeThickness">1</sys:Double> </DataTemplate.Resources> <Canvas Opacity="{Binding Path=Opacity}"> <Polyline Points="{Binding Path=Points}" Stroke="Black" StrokeThickness="8" StrokeDashArray="{Binding Path=StrokeDashArray}" StrokeMiterLimit="1"/> <Polyline Points="{Binding Path=Points}" Stroke="{Binding Path=Stroke}" StrokeThickness="6" StrokeDashArray="{Binding Path=StrokeDashArray}" StrokeMiterLimit="1"/> </Canvas> <DataTemplate.Triggers> <DataTrigger Binding="{Binding Path=Dimmed}"> <DataTrigger.Value> <sys:Boolean>True</sys:Boolean> </DataTrigger.Value> <Setter Property="Opacity" Value="0.3" /> </DataTrigger> </DataTemplate.Triggers> </DataTemplate> For simplicity I am hardcoding the line thickness to be 6 so that we can paint a thicker line that will be become the border, if you wanted to be more correct you could bind the first line thickness to a converter that adds 2 to the provided value and then bind the second line thickness to StrokeThickness. To use this template in a chart with only lines you could say chart1.AllSeries.Template = (DataTemplate) FindResource("Line"); In your case you will probably want to assign this template to the series that are painted by line. Hope this gets you on the right track JuanC
  18. We do not have a UI to trigger the start of the selection but if you add a button that executes this code chart1.Zoom.Mode = ZoomMode.Selection We will then allow the user to select the interesting area and zoom it. There is also a property in Zoom called Style that controls different zoom strategies. JuanC
  19. This would be the best place to post a sample. It would also be helpful if you could describe the final behavior you are looking for related to the chart. JuanC
  20. This is a tough one, although you can in fact use a template to control the brush used in the pie slices as follows <DataTemplate> <Path Data="{Binding Path=Geometry}" Fill="{Binding Path=DataItem.@CustomColor}" Stroke="{Binding Path=Stroke}" StrokeThickness="{Binding Path=StrokeThickness}" StrokeDashArray="{Binding Path=StrokeDashArray}" Opacity="{Binding Path=Opacity}"/> <DataTemplate.Triggers> <DataTrigger Binding="{Binding Path=Dimmed}"> <DataTrigger.Value> <sys:Boolean>True</sys:Boolean> </DataTrigger.Value> <Setter Property="Opacity" Value="0.25" /> </DataTrigger> </DataTemplate.Triggers> </DataTemplate> The truth is that the brush of the series/point is used elsewhere, including a darker version of it as the stroke, in the legend box, dataview, etc. I think a better solution although it requires to write some code would be to loop through the data and use the Points property to change the fill brush, e.g. chart1.Points[0].Fill = Brushes.Red; chart1.Points[1].Fill = Brushes.Blue; JuanC
  21. This issue does occur when the chart is inside a user control (it does not seem to happen when the chart is directly inside the tabitem), it has been fixed on build 3629 which can be downloaded as a hotfix. JuanC
  22. To control the frame/chrome of the tooltip you should remove the Black Rounded border from the DataTemplate and add the following XAML <cfx:Chart.ToolTipArea> <cfx:ToolTipArea> <cfx:ToolTipArea.Template> <ControlTemplate TargetType="{x:Type cfx:ToolTipArea}"> <Border Background="Black" BorderThickness="1" CornerRadius="5" Padding="5"> <ContentPresenter/> </Border> </ControlTemplate> </cfx:ToolTipArea.Template> </cfx:ToolTipArea> </cfx:Chart.ToolTipArea> JuanC
  23. We have found an issue when you combine AffectedSeries and BindingPath in a condition, it has been fixed on any build marked 3624 or later. JuanC
  24. You can get our most recent service pack here, we will soon be uploading a new service pack build but even though the XAML approach will work on that build we recently discovered and fixed an issue related to ConditionalAttributes when using BindingPath and AffectedSeries simultaneously (because of your post on this thread). To fix this issue you can download our most recent hotfix here, note that service pack builds go through more testing than hotfixes but we can release hotfixes as soon as issues are corrected. In both cases you need to run IE as administrator if your machine has UAC enabled. JuanC
  25. Real time support is one of those features that had to be cut from 8.0 because of the schedule, but customer feedback in both our forums and email was loud and clear, there are some of you that need to update the chart often and need a high performance redraw strategy. Before we go into the real time API and behavior in Chart FX for WPF 8.1 let’s discuss how the chart control responds to non-realtime changes in the data Binding to objects that support INotifyPropertyChanged When Chart FX receives a collection of CLR objects that support INotifyPropertyChanged, it will attach to the PropertyChanged event, then as soon as the event is fired because of a property change the chart will process the new value and mark itself as dirty. Note that this means that if you change more than one of your CLR objects quickly the chart will only “repaint” once. The quotes around repaint are because it is a little more complex than that, we will run our paint code but then detect that the changes do not need a full WPF redraw and instead animate the data changes. Binding to a collection that support INotifyCollectionChanged If the collection passed to Chart FX supports this interface we will also track changes to the collection, when the collection fires its Collection changed event the chart will process the new data and then mark itself as dirty, normally you will not get animations in this scenario. We will process the data changes immediately but delay the painting so it is also possible that if you add two objects to the collection quickly we will only repaint the chart once. Real Time Support In both these scenarios, Chart FX is going through a full repaint process which works fine for casual use but if the data changes often (e.g. once per second or quicker) it will probably generate high CPU usage, enter the real time API, when using this API you will first configure the maximum number of points the chart will hold, once this number is reached, each additional value will bump the oldest value out of the chart to keep memory usage steady. chart1.RealTime.BufferSize = 120; chart1.Data.Series = 1; chart1.Data.Points = 120; You will then run a code similar to this every time you want to add one or more points to the chart chart1.RealTime.BeginAddData(1, RealTimeAction.Append); chart1.Data[0, 0] = newValue; //chart1.Data[0, 1] = newValue2; chart1.RealTime.EndAddData(false, false); The first parameter to BeginAddData indicates the number of points you plan to add, note that when adding the points you will use 0 based index for the points regardless of how many points have been added to the chart before this. The first boolean parameter to EndData specifies whether labels should be scrolled, the second specifies whether the chart should scroll to the end if scrolling is enabled. Once we receive the data we do not a run a standard paint process but instead process the new data and move the visual objects accordingly, because of this we only support realtime in conjunction with certain gallery types (Bar, Line and Area), also several features such as stacked are currently unsupported in realtime. Performance is still being tweaked but a chart holding hundreds of points and being updated 1 – 5 times per second will only use a small percentage of the CPU (1 – 10%) even on a laptop. JuanC
×
×
  • Create New...