I'm using RadCartesianChart3d to show a surface. I've written some background code to intelligently choose the Maximum and Minimum values for my Z axis to be on nice even numbers which exceed the boundaries of the data. I then bind the LinearAxis3D for Z to these values.
The chart honors my Minimum value. Unfortunately, it sometimes ignores my Maximum value and chooses its own.
To be sure I wasn't wasting your time, before I posted this report, I tried to reproduce the problem using the sample application that Martin Ivanov attached in a reply in this other forum thread from just 2 weeks ago. It's the attachment "WpfApp17"
https://www.telerik.com/forums/dynamic-colorizer-for-surfaceseries3d
What I did was change the Z axis in the sample to use some arbitrary Minimum and Maximum values:
<
telerik:RadCartesianChart3D.ZAxis
>
<
telerik:LinearAxis3D
Minimum
=
"-800"
Maximum
=
"900"
/>
</
telerik:RadCartesianChart3D.ZAxis
>
At first, I could not reproduce the problem. But then I tried updating the sample to use the very latest version of UI for WPF that I have on my machine. It is R2 2019 SP1 which was literally released today, June 19, 2019.
Once I changed the assembly references to use the this new version of UI for WPF, the problem did occur. When I run the sample app with the changes above, my maximum axis value only shows 700, not 900.
(I realize the data does not go that high but I still want the chart to show the maximum I give to it.)
That's not all. It gets even stranger. If you then change the Maximum value from 900 to 1200, suddenly, it *is* honored.
The forum software does not seem to want to let me attach my modified version of the sample to this thread, even though it's a ZIP file and it's only 350k so you'll need to make the changes on your own version to reproduce this.
8 Answers, 1 is accepted
This is quite curious. I am not aware of the reported behavior. Would it be possible for you to send some runnable code snippets which I can test on my side.
As for the forum attachments, indeed, it allows only pictures. If you want to share a project, you can do it via the support ticketing system.
Regards,
Martin Ivanov
Progress Telerik
Hi Martin,
(I must have missed the notification that you replied or I would have replied back sooner myself)
As for some usable code snippets, well all you need to do is make a minor modification to that very same sample from the other forum thread that I linked to. However if you would like to see the modified version I am using I have pasted the XAML and code-behind of the MainWindow from that project as I modified it.
First, here is the MainWindow.Xaml (remember this is your sample, just slightly modified by me)
<
Window
x:Class
=
"WpfApp17.MainWindow"
xmlns:local
=
"clr-namespace:WpfApp17"
xmlns:telerik
=
"http://schemas.telerik.com/2008/xaml/presentation"
mc:Ignorable
=
"d"
Title
=
"MainWindow"
Height
=
"450"
Width
=
"800"
>
<
Grid
>
<
telerik:RadCartesianChart3D
>
<
telerik:RadCartesianChart3D.XAxis
>
<
telerik:LinearAxis3D
/>
</
telerik:RadCartesianChart3D.XAxis
>
<
telerik:RadCartesianChart3D.YAxis
>
<
telerik:LinearAxis3D
/>
</
telerik:RadCartesianChart3D.YAxis
>
<
telerik:RadCartesianChart3D.ZAxis
>
<!-- In my testing, the maximum value of 900 is NOT honored but a max of 1200 IS honored -->
<
telerik:LinearAxis3D
Minimum
=
"-800"
Maximum
=
"1200"
/>
</
telerik:RadCartesianChart3D.ZAxis
>
<
telerik:RadCartesianChart3D.Series
>
<
telerik:SurfaceSeries3D
ItemsSource
=
"{Binding Points}"
XValueBinding
=
"X"
YValueBinding
=
"Y"
ZValueBinding
=
"Z"
x:Name
=
"Series"
>
<
telerik:SurfaceSeries3D.Colorizer
>
<
telerik:SurfaceSeries3DValueGradientColorizer
x:Name
=
"Colorizer"
IsAbsolute
=
"False"
/>
</
telerik:SurfaceSeries3D.Colorizer
>
</
telerik:SurfaceSeries3D
>
</
telerik:RadCartesianChart3D.Series
>
<
telerik:RadCartesianChart3D.Grid
>
<
telerik:CartesianChart3DGrid
/>
</
telerik:RadCartesianChart3D.Grid
>
<
telerik:RadCartesianChart3D.Behaviors
>
<
telerik:Chart3DCameraBehavior
/>
</
telerik:RadCartesianChart3D.Behaviors
>
</
telerik:RadCartesianChart3D
>
<
telerik:RadSlider
Minimum
=
"0"
Maximum
=
"63"
SmallChange
=
"1"
LargeChange
=
"1"
VerticalAlignment
=
"Bottom"
Margin
=
"10"
x:Name
=
"border"
IsSelectionRangeEnabled
=
"True"
SelectionChanged
=
"Border_SelectionChanged"
/>
</
Grid
>
</
Window
>
Here is the code-behind MainWindow.xaml.cs
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Windows;
using
System.Windows.Media;
namespace
WpfApp17
{
public
partial
class
MainWindow : Window
{
private
static
Random r =
new
Random();
private
List<Color> ColorsCollection =
new
List<Color>();
public
MainWindow()
{
InitializeComponent();
int
rMax = Colors.Red.R;
int
rMin = Colors.Blue.R;
int
bMax = Colors.Red.B;
int
bMin = Colors.Blue.B;
int
gMax = Colors.Red.G;
int
gMin = Colors.Blue.G;
int
size = 64;
for
(
int
i = 0; i < size; i++)
{
var rAverage = rMin + (
int
)((rMax - rMin) * i / size);
var gAverage = gMin + (
int
)((gMax - gMin) * i / size);
var bAverage = bMin + (
int
)((bMax - bMin) * i / size);
ColorsCollection.Add(Color.FromRgb((
byte
)rAverage, (
byte
)gAverage, (
byte
)bAverage));
}
this
.Colorizer.GradientStops = GetGradientStops(0, ColorsCollection.Count - 1);
this
.border.Background =
new
LinearGradientBrush(
this
.Colorizer.GradientStops);
List<PlotInfo> data =
new
List<PlotInfo>();
double
maxX = 100;
double
maxY = 80;
for
(
int
x = 0; x < maxX; x++)
{
for
(
int
y = 0; y < maxY; y++)
{
double
xValue = Math.Sin(x * Math.PI / (0.25 * maxX));
double
yValue = Math.Cos(y * Math.PI / (0.50 * maxY));
double
z = 200 * xValue * yValue;
PlotInfo pi =
new
PlotInfo { X = x, Y = y, Z = z, };
data.Add(pi);
}
}
this
.Series.ItemsSource = data;
}
private
GradientStopCollection GetGradientStops(
int
startIndex,
int
endIndex)
{
var colors = ColorsCollection.Skip(startIndex).Take(endIndex - startIndex);
var step = 1.0 / colors.Count();
var stops = colors.Select((color, index) =>
new
GradientStop(color, index * step));
return
new
GradientStopCollection(stops);
}
private
void
Border_SelectionChanged(
object
sender, Telerik.Windows.RadRoutedEventArgs e)
{
this
.Colorizer.GradientStops = GetGradientStops((
int
)
this
.border.SelectionStart, (
int
)
this
.border.SelectionEnd);
}
}
public
class
PlotInfo
{
public
double
X {
get
;
set
; }
public
double
Y {
get
;
set
; }
public
double
Z {
get
;
set
; }
}
}
This is causing me problems in my production application. The Surface I draw renders well and the chart shows the X and Y axes fully. But in my case, I cannot make it show any label on the Z axis besides the minimum label
I've attached an image showing what I see.
Below is the xaml I'm using in my production app for the chart you see in the attached image. Note that I've stripped the Z axis down to its minimum settings (just SmartLabelsMode) in the hopes to make it show *something* more than just the one label. But believe me, I have tried every combination of LinearAxis3D properties I can find for the labels, including "SmartLabelsMode", TickOrigin, MajorStep, Minimum and Maximum properties. I've tried setting them and leaving them to their defaults. I've even tried hard-coding them to the values I *know* are in the sample surface I'm looking at. It makes no difference; The data looks great but the axis never shows me anything but the minimum value. Again, this is what produces the attached image.
<
tk:RadCartesianChart3D
x:Name
=
"Chart"
FontSize
=
"{Binding Settings.FontSizeMed}"
MouseDoubleClick
=
"Chart_OnMouseDoubleClick"
>
<
tk:RadCartesianChart3D.Resources
>
<
Style
TargetType
=
"{x:Type tk:LinearAxis3D}"
BasedOn
=
"{StaticResource {x:Type tk:LinearAxis3D}}"
>
<
Setter
Property
=
"Visibility"
Value
=
"{Binding LocalSettings.SurfaceShowAxes, Converter={StaticResource BoolToVis}}"
/>
</
Style
>
</
tk:RadCartesianChart3D.Resources
>
<
tk:RadCartesianChart3D.XAxis
>
<
tk:LinearAxis3D
Title
=
"{Binding XAxisTitle}"
Minimum
=
"{Binding XAxisMinLocal}"
LabelFormat
=
"0.0"
SmartLabelsMode
=
"SmartStep"
Maximum
=
"{Binding XAxisMaxLocal}"
Foreground
=
"GreenYellow"
>
<
tk:LinearAxis3D.LabelStyle
>
<
Style
TargetType
=
"TextBlock"
>
<
Setter
Property
=
"Foreground"
Value
=
"GreenYellow"
/>
<
Setter
Property
=
"FontWeight"
Value
=
"Bold"
/>
</
Style
>
</
tk:LinearAxis3D.LabelStyle
>
</
tk:LinearAxis3D
>
</
tk:RadCartesianChart3D.XAxis
>
<
tk:RadCartesianChart3D.YAxis
>
<
tk:LinearAxis3D
Title
=
"{Binding YAxisTitle}"
Minimum
=
"{Binding YAxisMinLocal}"
LabelFormat
=
"0.0"
SmartLabelsMode
=
"SmartStep"
Maximum
=
"{Binding YAxisMaxLocal}"
Foreground
=
"Orange"
>
<
tk:LinearAxis3D.LabelStyle
>
<
Style
TargetType
=
"TextBlock"
>
<
Setter
Property
=
"Foreground"
Value
=
"Orange"
/>
<
Setter
Property
=
"FontWeight"
Value
=
"Bold"
/>
</
Style
>
</
tk:LinearAxis3D.LabelStyle
>
</
tk:LinearAxis3D
>
</
tk:RadCartesianChart3D.YAxis
>
<
tk:RadCartesianChart3D.ZAxis
>
<
tk:LinearAxis3D
Title
=
"{Binding ZAxisTitle}"
LabelFormat
=
"0.00"
SmartLabelsMode
=
"SmartStep"
>
<
tk:LinearAxis3D.LabelStyle
>
<
Style
TargetType
=
"TextBlock"
>
<
Setter
Property
=
"Foreground"
Value
=
"White"
/>
<
Setter
Property
=
"FontWeight"
Value
=
"Bold"
/>
</
Style
>
</
tk:LinearAxis3D.LabelStyle
>
</
tk:LinearAxis3D
>
</
tk:RadCartesianChart3D.ZAxis
>
<
tk:RadCartesianChart3D.Series
>
<
tk:SurfaceSeries3D
ItemsSource
=
"{Binding Points}"
XValueBinding
=
"X"
YValueBinding
=
"Y"
ZValueBinding
=
"Z"
x:Name
=
"Series"
>
<
tk:SurfaceSeries3D.Colorizer
>
<
tk:SurfaceSeries3DValueGradientColorizer
x:Name
=
"Colorizer"
IsAbsolute
=
"True"
/>
</
tk:SurfaceSeries3D.Colorizer
>
</
tk:SurfaceSeries3D
>
<!--
First cut at showing the profile line on the Surface display
Still need to colorize it and to ensure that the points we get
are NOT leveled because the Surface isn't...
-->
<!--
<
tk:LineSeries3D
ItemsSource
=
"{Binding ProfileLine, IsAsync=True}"
XValueBinding
=
"X"
YValueBinding
=
"Y"
ZValueBinding
=
"Z"
x:Name
=
"ProfileLine"
DefaultLineVisualDiameter
=
"8"
Foreground
=
"GreenYellow"
>
</
tk:LineSeries3D
>
-->
</
tk:RadCartesianChart3D.Series
>
<
tk:RadCartesianChart3D.Grid
>
<
tk:CartesianChart3DGrid
XZGridLinesVisibility
=
"All"
YZGridLinesVisibility
=
"All"
XYGridLinesVisibility
=
"All"
Visibility
=
"{Binding LocalSettings.SurfaceShowGridLines, Converter={StaticResource BoolToVis}}"
/>
</
tk:RadCartesianChart3D.Grid
>
<!--
JMO: Setting IsRotationEnabled to False would keep the 3d image
fully illuminated no matter how you rotated it.
-->
<
tk:RadCartesianChart3D.Lights
>
<
DirectionalLight
tk:Chart3DCameraBehavior.IsRotationEnabled
=
"True"
Direction
=
"0.0, 0.0 1.0"
/>
</
tk:RadCartesianChart3D.Lights
>
<
tk:RadCartesianChart3D.Behaviors
>
<
tk:Chart3DCameraBehavior
/>
</
tk:RadCartesianChart3D.Behaviors
>
</
tk:RadCartesianChart3D
>
Thank you for the provided information. I've tried your code, but I still cannot reproduce the issue. Can you please check the attached project and let me know if I am missing something?
Regards,
Martin Ivanov
Progress Telerik
Hi Martin,
I can produce the bug about it not showing the Maximum and minimum I want fairly easily. Just go to the Z axis declaration in your example (the XAML element whose "x:Name" is set to "zAxis" and add attributes for Minimum and Maximum. Here I've set it to -800 and positive 900. So those are the number I expect to see on the top and bottom, right?
<
telerik:LinearAxis3D
Title
=
"{Binding ZAxisTitle}"
x:Name
=
"zAxis"
LabelFormat
=
"0.00"
Minimum
=
"-800"
Maximum
=
"900"
SmartLabelsMode
=
"SmartStep"
>
If you do that, you won't see "900" at the top. You'll see "700". And it doesn't matter if you take out the "SmartLabelsMode" attribute. It will still be only 700.
So how do I force it to use the number I specify for the max Z label?
Having said that, I will admit I unable to reproduce the second problem I see which only shows up in my production app. The symptom (which ou can see it in the previous image I attached) is that I cannot make the Maximum label appear at all, no matter what I do. I just get a Minimum label. I'm not sure I can reproduce that one but I will try.
My hope is that whatever is preventing me from showing the label I want in *this* sample is causing my problems in my production app
Thank you for the clarification. Now I see what is the issue. The last tick and label are not shown because of the automatic tick step calculated by the axis. This step is calculated based on the applied minimum and maximum values. In your case, it is 500. And because the next number after 700 should be 1200 (700+500), but the maximum is 900, the last tick is not drawn. To resolve this, you can set the choose a custom step and assign it to the MajorStep property of the axis.
<
telerik:LinearAxis3D
Title
=
"{Binding ZAxisTitle}"
x:Name
=
"zAxis"
LabelFormat
=
"0.00"
MajorStep
=
"100"
Minimum
=
"-800"
Maximum
=
"900"
SmartLabelsMode
=
"SmartStep"
>
As for the second problem, if you succeed in reproducing it, you can post some steps here and I will investigate what could be happening.
Regards,
Martin Ivanov
Progress Telerik
Hi Martin,
I have managed to reproduce my issue with the axes refusing to show any Z values other than the bottom one. However the only way I was able to reproduce it was to rewrite your sample along with some simplifed, file-reading code and use the actual data file that I use for my surface.
Unfortunately, that data file is 19 mb large.
Is there some way I can get a file of that size to you for you to test with my modified sample? I am guessing it's not going to let me attach something that large to the forum
The work of reproducing my sample and comparing it to the original line-by-line finally made me stumble across the cause of the labels not showing at all. It was all hidden in just one line in a code-behind handler (dating back to the first time I asked you a question about Surface Series, months ago).
I was doing work in a DataContextChanged handler and for some reason I can no-longer recall, I had a line in there which set the LabelInterval of the Z axis.
private
void
SurfaceDisplay_DataContextChanged(
object
sender, System.Windows.DependencyPropertyChangedEventArgs e)
{
// Some other stuff that isn't relevant...
// ... etc etc.
// Now THIS LINE. XAML bindings no longer matter...
Chart.ZAxis.LabelInterval = 100;
}
This local, immediate value of course completely overwrote my XAML binding statement so it would not have mattered *what* I bound it to. Hours and hours of frustration all due to a single line of code. Once I removed that one line, it worked perfectly.
Thank you for all your time.
I am glad to hear that you managed to resolve the issue.
Regards,
Martin Ivanov
Progress Telerik