I'm using the WinRTXamlToolkit.Controls.DataVisualization.Charting.Chart object, with a dependent axis integer of 0/1, and an independent axis of time.
I'd like to suppress or perhaps rotate the labels at the top of the chart.
Are the styles found on the Axis (chart.Axes) or series (LineSeries)?
My chart is completely configured through code, snippets below:
EDIT 1/30/2017-3: added hosting XAML page.
<Page
x:Class="HomeControl.Views.Historical"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:Charting="using:WinRTXamlToolkit.Controls.DataVisualization.Charting"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:HomeControl.Views"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Charting:Chart x:Name="LineChart" HorizontalAlignment="Stretch" HorizontalContentAlignment="Stretch" VerticalAlignment="Top" Height="500">
</Charting:Chart>
</Grid>
</Page>
EDIT 1/30/2017-2: added remaining code...
var lowDate = records.First().taken.DateTime;
var highDate = records.Last().taken.DateTime;
var allDeviceTelemetry = records.GroupBy(t => t.sensorid).OrderBy(g => g.Key);
var axisTaken = new DateTimeAxis()
{
Title = "Taken",
Orientation = AxisOrientation.X,
IntervalType = DateTimeIntervalType.Minutes,
Interval = 5,
Minimum = lowDate,
Maximum = highDate,
};
LineChart.Axes.Add(axisTaken);
LineChart.Axes.Add(new LinearAxis()
{
Title = "State",
Orientation = AxisOrientation.Y
});
foreach (var deviceTelemetry in allDeviceTelemetry)
{
var series = new LineSeries()
{
Title = deviceTelemetry.Key, // sensorid
IndependentValuePath = "taken",
DependentValuePath = "sensorvalueint",
ItemsSource = deviceTelemetry
};
LineChart.Series.Add(series);
}
The area I'm trying to control is in green:
I've played around with some of the other styles, such as the interval and axis titles, I just can't figure out where the data point label styles are?
EDIT 1/30/2017:
Here is the tree, with the highlighted object (TextBlock at bottom). I need to figure out how to style this "AxisLabel", "Panel", "AxisGrid" or "CategoryAxis" - through code.
Any hints would be appreciated!
-John
Not a complete answer, since I can't tell how you made labels to show up at the top, but these seem to me like they are more of data point labels rather than axis labels. Running the toolkit's sample app might help you browse the UI tree a bit and make it easier to explore things. Just get the toolkit's source in your VS, hit F5, open the Chart control sample and hit Ctrl+Shift while pointing at a label. Here's what I get when pointing at the category axis:
I would poke around the data points, the series and search for a property called style. Otherwise - browse the source code to find how it gets set up.
I've solved my issue, but not with an outcome I expected - much better.
After experimenting quite a bit, I've learned a few things about the WinRTXAML charting; these observations are from purely a coding perspective, since I am not using static XAML in my page. I am new to the control, so if anyone knows these learnings to be incomplete or misguided please chime in:
Axes are created dynamically, based on the Series being added, if an
appropriate Axis is not predefined (in the Axes property). This
creation of axes does not occur until data binding takes place
(understandably because the chart needs to reflect on the datatypes
of the incoming data).
The explicit assignment of a series to an axis
is achieved through the IndependentAxis and DependentRangeAxis of the
Series object. If this is omitted, and assignment will be made
implicitly based on the data type of the series bound Value.
Conversely, if an axis explicitly assigned to a series it need not be
added to the Axes property on the Chart (but it also can be).
If an axis is explicitly assigned to a series which conflicts with the
datatype of that series, a horribly misleading exception is
generated. "Assigned independent axis cannot be used. This may be due
to an unset Orientation property for the axis."
A CategoryAxis is
automatically generated for a series value of type string, but also
when the datatype does not match an existing axis. (I.e. the datatype
is cast to a string, then a CategoryAxis is produced/used)
Applying these learnings to my original issue, here's what happened. Although I had a DateTime axis predefined, my incoming datatype for the independent axis was DateTimeOffset. This value was interpreted as a string value, since it was not DateTime (i.e. no implicit conversion). This caused the Chart to generated a CategoryAxis, assign it to the series, and it placed it at the top of the chart.
Not understanding this was happening, I didn't want the labels on this top axis, so I was trying to suppress them, but I could not find the axis, since it was not created until AFTER data binding took place.
SOLUTION: make the "taken" value datatype DateTime, this caused the chart to align [explicitly or implicitly] to the DateTimeAxis. Optimization: assign the axis directly to the series, don't bother adding them to the Chart.Axes property.
Thank you #jstreet and #FilipSkakun for taking the time to look at this, I appreciate your attention and patience.
-John
Related
I have a chart with a legend whose symbol I replaced per the example in the docs. It looks like this:
var marker = chart.legend.markers.template;
marker.disposeChildren();
let dollar = marker.createChild(am4core.Image);
dollar.width = 40;
dollar.height = 40;
dollar.verticalCenter = "top";
dollar.horizontalCenter = "left";
dollar.strokeWidth = 2;
dollar.strokeOpacity = 1;
dollar.adapter.add("href", function (href: any, target: any) {
return `http://host.com?id=${target.dataItem.dataContext.dummyData.value`;
});
And this works, my images are displayed, little faces :) - I would like to add a border around the image of the same color of the series so that you can identify the marker in the legend with the series. But I can't find the right set of settings to make this a thing.
Is this possible?
EDIT -
So, I tried the following chage to the above and got a decent result. It's a bit hacky, so there might be a better way. If not, I guess this works.
//marker.disposeChildren(); <= don't do this
marker.width = "50px";
marker.height = "50px";
Basically the original marker remains and is behind the image. The marker has to be made larger so that it sticks out and creates a pseudo border.
I'm going to answer this one myself, since I have a working solution and no one lese answered :)
The edit above does what it is needed. Doesn't see like a great solution, a border around an image should be doable. But this gets us what we want.
Solution:
Make the marker bigger than the image
Place the image above the marker
In this case, we do not remove child elements of the marker, like the sample code on amchart4 shows, since you need it.
I'm drawing a canvas programmatically, given a bunch of path data from somewhere else and adding it to the canvas as
// This is actually done more elaborately, but will do for now
PathFigureCollection figures = GetPathFigureCollection();
var path = new Path
{
Data = new PathGeometry { Figures = figures },
Fill = GetFill(),
Stroke = GetStroke(),
StrokeThickness = GetThickness()
};
MyCanvas.Children.Add(path);
Now, I have the canvas in a ScrollViewer, so I want to make sure that I can scroll all the way to reveal the entire path (actually paths - I have several, generated the same way) but no further. I tried this:
var drawingWidth = MyCanvas.Children
.OfType<FrameworkElement>()
.Max(e => Canvas.GetLeft(e) + e.ActualWidth);
MyCanvas.Width = drawingWidth;
This works well for some other elements (the drawing also has a few text blocks and ellipses), but for the paths both Canvas.GetLeft(e) and e.ActualWith (as well as some other things I tried like e.RenderSize.Width and e.DesiredSize.With) all return 0. Since the element that extends farthest to the right is a path, this results in a canvas that is too small.
How do I get the width of the Path elements too?
Ha, found it!
Rewriting the LINQ query as a loop, I could cast paths to Path, and use path.Data.Bounds.Right as the right edge of that element.** I might be able to convert the code back to a LINQ query now that I know what I want to do (I always find them more readable than stateful loops...).
I found this when I, after having perused the link provided by markE where, as a side note, it was stated that
If your design requirements allow more rough approximates, then you will find that cubic Bezier curves are always contained within their control points.
So, if I could find the right-most control point of all the path figures in my path, I would be home. Intellisense did the rest of the job for me :)
Is there a way to explicitly position/orient an axis using dimple.js? I know that the first x-axis is added to the bottom and the second is added to the top, so I can get the desired outcome like this:
var xAxis = myChart.addMeasureAxis("x", "dur");
xAxis.hidden = true;
xAxis = myChart.addMeasureAxis("x", "dur");
...but that seems a little hacked.
I don't think there's any way around that. Here's the relevant code : https://github.com/PMSI-AlignAlytics/dimple/blob/master/src/objects/chart/methods/draw.js#L123
Because firstX is local to the draw function there isn't a way you could override it within the context of the loop.
You could possibly duplicate the logic used to position the top axis : https://github.com/PMSI-AlignAlytics/dimple/blob/master/src/objects/chart/methods/draw.js#L226
And then only add one axis (which would be positioned at the bottom), then manually move your axis after the drawing, like :
chart.draw();
chart.axes[0].shapes.attr('transform', ...);
But you would need to do this after every draw or else they'll be repositioned. The hack you have seems easiest.
You could open an enhancement ticket for this though : https://github.com/PMSI-AlignAlytics/dimple/issues?q=is%3Aissue+label%3Aenhancement+is%3Aopen
I'm using Builder v1.80.06
I can vary the position of a polygon every repeat easily enough
e.g. I have a Positions list
positions=[[1,1],[1.1,0.9],...]
and in the 'Position field' have :
$positions[0]
and then change it's value in a code block on each repeat.
BUT I want to vary the size in a similar manner with a $sizes list but get an error.
Looking at the generated code, the problem is at the object creation stage. the code generated is:
for a hard coded polygon (ie ok)
polygon_1 = visual.Rect(win=win, name='polygon_1',
width=[1.5, .2][0], height=[1.5, .2][1],
ori=0, pos=[0, -0.6],
lineWidth=1, lineColor=[1,1,1], lineColorSpace=u'rgb',
fillColor=[0,1,0], fillColorSpace=u'rgb',
opacity=1,interpolate=True)
for one populated by a variable (not working):
polygon_2= visual.Rect(win=win, name='polygon_2',
width=1.0[0], height=1.0[1],
ori=0, pos=[0,0],
lineWidth=1, lineColor=[1,1,1], lineColorSpace=u'rgb',
fillColor=[1,0,0], fillColorSpace=u'rgb',
opacity=1,interpolate=True)
It complains (rightly) that 1.0[0] makes no sense on the width and height parameters
Even though I have my sizes list instantiated in a code block right at the beginning of the experiment instead of reading $sizes[0] a default float value of 1.0 is used.
Any other suggestions for how to vary the polygon size dynamically at runtime using builder?
I could just take the generated code and drop it into coder I suppose and fix the problem but I want to hand this over to a researcher so would like for them to be able to maintain it.
thanks,
If you set size to be a tuple/list with a pair values [1.2,1.5] or [1,1] does that not fix it?
When you change attributes at runtime, just change the attribute of an existing stimulus instead of instantiating a full new stimulus. The latter is quite heavy on ressources, causing unreliable timing. So do
stim = visual.Rect(win) # instantiation, ressource heavy
stim.attribute = newValue # change attribute. lighter.
I can think of two ways you could do it in a pretty neat way. The first is to set width and height explicitly instead of the size attribute, but using a size-like value. So (removing all parameters not of interest):
polygon_2 = visual.Rect(win)
# Unpack the x,y-sizes to the stimulus .width and .height attributes
newSize = (1.5, 0.2)
polygon_2.width, polygon_2.height = newSize
The second, if the size attribute is really important to use, is to use the Polygin with edges=4 to make it a rectangle:
polygon_2 = visual.Polygon(win=win, edges=4, size=(1.5, 0.2))
# Setting size
polygon_2.size = (0.8, 0.4)
Do try Jon's suggestion first. But the idea with visual.Rect and visual.Circleis to use substitute Polygon's size and vertices for something more relevant. So size can do unexpected things if width/height etc. are not 1.
I have a Zedgraph textobj which I want to place always in the same x, y position (ASP.NET image). I noticed that the text doesn't always show in the same starting x position. It shifts depending on the text's length. I tried to have the text to have the same length by padding it with spaces. It helped a little but the result is not always consistent. I am using PaneFraction for coordType.
What's the proper method to have a piece of text to always show in the same x position. I am using textobj as a title because the native title property always shows up centered and I need my title be left aligned to the graph.
No, it does not depend on text lenght, however...
It depends on various other things:
Horizontal and vertical align of the text box (see: Location )
Current size of the pane. The font size is scaled dynamically to fit the changing size of the chart.
Counting proper positions to have TextObj (or any other object) always at the same place is quite hard. So you need avoid as much as you can any numbers/fractions in your location coordinates. ZedGraph sometimes calculates the true position in quite odd way then.
You haven't provided any code, so it's hard to tell if and where you made the mistake (if any). But, if I were you, I would do something like that:
TextObj fakeTitle = new TextObj("some title\n ", 0.0, 0.0); // I'm using \n to have additional line - this would give me some space, margin.
fakeTitle.Location.CoordinateFrame = CoordType.ChartFraction;
fakeTitle.Location.AlignH = AlignH.Left; // Left align - that's what you need
fakeTitle.Location.AlignV = AlignV.Bottom; // Bottom - it means, that left bottom corner of your object would be located at the left top corner of the chart (point (0,0))
fakeTitle.FontSpec.Border.IsVisible = false; // Disable the border
fakeTitle.FontSpec.Fill.IsVisible = false; // ... and the fill. You don't need it.
zg1.MasterPane[0].GraphObjList.Add(fakeTitle);
I'm using ChartFraction coordinates instead of PaneFraction (as drharris suggests) coordinates to have the title nicely aligned with the left border of the chart. Otherwise it would be flushed totally to the left side (no margin etc...) - it looks better this way.
But make sure you didn't set too big font size - it could be clipped at the top
Are you using this constructor?
TextObj(text, x, y, coordType, alignH, alignV)
If not, then be sure you're setting alignH to AlignH.Left and alignV to AlignV.Top. Then X and Y should be 0, 0. PaneFraction for the coordType should be the correct option here, unless I'm missing your intent.
Alternatively, you can simply download Zedgraph code, edit it to Left-align the title (or even better, provide an option for this, which should have been done originally), and then use it in production. Beauty of open source.