Text scaling problem with itext7, hidden margins? - c#

I'm trying to get this c# function from within a asp.netcore razor project using itext7 (7.1.7) to output a div containing text that scales up to within the constraints given with height and width. However, for some reason it seems there's some kind of hidden margin that will not scale the text to the boundaries of the given width and height, even though the margins are set to 0.
At the moment the maximum text i get is only half of what it should be.
Also, the paragraph, nor the div is actually set to the min width and min height.
Any help how to fix this is appreciated
public Div MakeScaledTxtDiv(string _displayTxt ,PdfFont _Pdffont, float _maxHeight,
float _maxWidth, Canvas _canvas, bool _showBorder)
{
Div nameDiv = new Div();
iText.Kernel.Geom.Rectangle posRect = new iText.Kernel.Geom.Rectangle(_maxWidth,_maxHeight);
Paragraph txtPara = new Paragraph()
.SetVerticalAlignment(VerticalAlignment.MIDDLE)
.SetTextAlignment(iText.Layout.Properties.TextAlignment.CENTER);
if (_showBorder)
txtPara.SetBorder(new DashedBorder(1));
if (_maxHeight > 0)
txtPara.SetMaxHeight(_maxHeight);
if (_maxWidth > 0)
txtPara.SetMaxWidth(_maxWidth);
txtPara
.SetMargin(0f)
.SetPadding(0f)
.SetFont(_Pdffont)
.Add(_displayTxt);
float fontSizeToSet = 1;
float fontSizeMax = 42;
float curFontSize = fontSizeMax;
bool m_bGoodfit = false;
while (!m_bGoodfit)
{
txtPara.SetFontSize(curFontSize);
ParagraphRenderer renderer = (ParagraphRenderer)txtPara.CreateRendererSubTree().SetParent(_canvas.GetRenderer());
LayoutContext context = new LayoutContext(new LayoutArea(1, posRect));
var fits = renderer.Layout(context).GetStatus();
if (fits == LayoutResult.FULL)
{
fontSizeToSet = curFontSize;
m_bGoodfit = true;
}
else
{
curFontSize -= 1;
if (curFontSize == 1)
{
fontSizeToSet = curFontSize;
m_bGoodfit = true;
}
}
}
txtPara.SetFontSize(fontSizeToSet);
if (_maxHeight > 0)
{
nameDiv.SetMinHeight(_maxHeight)
.SetMaxHeight(_maxHeight)
.SetHeight(_maxHeight);
}
if (_maxWidth > 0)
{
nameDiv.SetMinWidth(_maxWidth)
.SetMaxWidth(_maxWidth)
.SetWidth(_maxWidth);
}
nameDiv
.SetMargin(0f)
.SetPadding(0f)
.SetVerticalAlignment(VerticalAlignment.MIDDLE)
.SetTextAlignment(iText.Layout.Properties.TextAlignment.CENTER);
if (_showBorder)
nameDiv.SetBorder(new DottedBorder(1));
nameDiv.Add(txtPara).SetVerticalAlignment(VerticalAlignment.MIDDLE);
return nameDiv;
}

What worked for me was to change the multiplier number 0.7f slightly bigger in the code below (code referenced from https://stackoverflow.com/a/57929067/5490413).
It seems as that multiplier gets bigger/smaller, the space between the text and field bottom border grows/shrinks accordingly. Maybe start with 1.2f and find one that fits.
Paragraph linePara = new Paragraph().Add(lineTxt)
.SetTextAlignment(iText.Layout.Properties.TextAlignment.CENTER).SetBorder(new DottedBorder(1))
.SetMultipliedLeading(0.7f);
lineDiv.Add(linePara);
FYI I came across the exact issue you were facing: How to scale text within a fixed rectangle with itext7?
I tried with the answer posted there https://stackoverflow.com/a/57929067/5490413 and faced the same hidden bottom margin issue here.

Related

iText 7 ImageRenderInfo Matrix contains negative height on Even number Pages

I have a PDF with four pages. Two images on the first page, one on the second, and one on the third. When I retrieve the value of the image on the second page or fourth,, I get a negative height. I tried setting it to Absolute as a quick fix but the Y position of the image was still slightly off. Also, the height and positioning on page three was fine.
Update: So far, this only seems to be a problem with PDF's created in Google Docs.
My code to extract the PDF images was taken from this thread Using iText 7, what's the proper way to export a Flate encoded image?.
This is how I access the height
var currentPDFImageInfo = extractedImages[i];
var currentPDFImageMatrix = currentPDFImageInfo.RenderInfo.GetImageCtm();
float pdfImageWidth = currentPDFImageMatrix.Get(iText.Kernel.Geom.Matrix.I11);
How I retrieve the PDF image data
public static List<PDFImageInfo> ExtractImagesFromPDF(string filePath)
{
Reader = new PdfReader(filePath);
Document = new PdfDocument(Reader);
var strategy = new ImageRenderListener();
PdfCanvasProcessor parser = new PdfCanvasProcessor(strategy);
for (int pageNumber = 1; pageNumber <= Document.GetNumberOfPages(); pageNumber++)
{
strategy.CurrentPageNumber = pageNumber;
parser.ProcessPageContent(Document.GetPage(pageNumber));
}
return strategy.ImageInfoList;
}
And of course the Strategy class
public class ImageRenderListener : IEventListener
{
public void EventOccurred(IEventData data, EventType type)
{
if (data is ImageRenderInfo imageData)
{
try
{
if (imageData.GetImage() == null)
{
Console.WriteLine("Image could not be read.");
}
else
{
var pdfImageInfo = new PDFImageInfo(CurrentPageNumber, imageData);
ImageInfoList.Add(pdfImageInfo);
}
}
catch (Exception ex)
{
Console.WriteLine("Image could not be read: {0}.", ex.Message);
}
}
}
public ICollection<EventType> GetSupportedEvents()
{
return null;
}
public int CurrentPageNumber { get; set; }
public List<PDFImageInfo> ImageInfoList { get; set; } = new List<PDFImageInfo>();
}
This is how I access the height
var currentPDFImageInfo = extractedImages[i];
var currentPDFImageMatrix = currentPDFImageInfo.RenderInfo.GetImageCtm();
float pdfImageWidth = currentPDFImageMatrix.Get(iText.Kernel.Geom.Matrix.I11);
This value is the height only under certain circumstances.
Some backgrounds: The contents of a PDF page are drawn by a sequence of instructions in some content stream. Some of these instructions can manipulate the so called current transformation matrix (CTM) which represents an affine transformation, i.e. some combination of a rotation, translation, mirroring, and skewing. Everything other instructions draw is manipulated by the CTM value at the time that instruction is executed.
When a bitmap image is drawn, it is conceptually first reduced to a 1×1 square which then is transformed by the CTM to the final form of the image on the page.
If the image is displayed upright, no rotation or anything else involved, then indeed the I11 value is the width of the displayed image and the I22 value is the height. The I12 and I21 values are 0 then
But often bitmaps are displayed at 90° clockwise or counterclockwise (e.g. because someone held the camera at an 90° angle while shooting). In these cases I11 and I22 are 0 while I12 and I21 are the height and width respectively, with one or the other having a negative sign depending on the direction of the rotation.
If the bitmap is rotated by 180°, I11 and I22 again contain width and height, but both with a negative sign. If it's mirrored along the x axis or the y axis, one of them is negative.
And if the transformation is something else, e.g. a rotation by an angle that's not a multiple of 90°, finding the height and width becomes more complicated.
Actually then it is not even clear what height and width of the skewed, rotated, and mirrored form shall mean.
Thus, as a start please define which values you exactly are after; based on that you can try and determine them from arbitrary transformation matrices.
Another possible cause for unexplainable coordinate data for pages after the first one is that your code re-uses the PdfCanvasProcessor for each page without resetting:
var strategy = new ImageRenderListener();
PdfCanvasProcessor parser = new PdfCanvasProcessor(strategy);
for (int pageNumber = 1; pageNumber <= Document.GetNumberOfPages(); pageNumber++)
{
strategy.CurrentPageNumber = pageNumber;
parser.ProcessPageContent(Document.GetPage(pageNumber));
}
This causes the graphics state at the end of one page incorrectly to be used as starting graphics state of the next one. Instead you should either use a new PdfCanvasProcessor instance for each page or call parser.Reset() at the start of each page.

How can I see added last candle in candlestick chart with WinForms Chart?

Sorry, I'm not good at writing with English.
I've tried to make real time candle stick chart and almost completed.
But, there are a few problems.
When new data comes in, my program works, but it only shows half the width of the last candle.
I can then move the scroll bar to the end to see the finished bar. I don't know where to touch.
When the chart area is zoomed, the length of the x-axis changes slightly when data is received.
I want to fix the size of the area where the candle is displayed.
The following are some of the sources:
private void RealChart(Chart chart)
{
int a = chtMain.Series["BaseCandle"].Points.Count;
double yMinValue = double.MaxValue;
double yMaxValue = double.MinValue;
for (int i = 0; i < a; i++)
{
Series s = chtMain.Series["BaseCandle"];
if (i < s.Points.Count)
{
yMaxValue = Math.Max(yMaxValue, s.Points[i].YValues[0]);
yMinValue = Math.Min(yMinValue, s.Points[i].YValues[1]);
}
}
chtMain.ChartAreas["ChartArea1"].AxisY.Maximum = yMaxValue + Math.Abs(yMaxValue * 0.05);
chtMain.ChartAreas["ChartArea1"].AxisY.Minimum = yMinValue - Math.Abs(yMinValue * 0.05);
chtMain.ChartAreas["ChartArea1"].AxisX.ScaleView.Scroll(a);
}

Trouble with UIScrollView mixed layouts approach

I'm trying to follow the UIScrollView And Autolayout Mixed approach.
I've seen other similar questions but with no accepted answer.
I'm not sure that this is what I need.
Please understand that I'm neither working with Storyboards nor XIBs.
All my views are created programmatically using c# in Xamarin.iOS. This is very similar to objective C code.
Like many others I can't find a way to have my scroll view to actually scroll.
So in my main View Controller I have the following in ViewDidLoad():
public override void ViewDidLoad()
{
base.ViewDidLoad();
_scrollView = new UIScrollView
{
ShowsHorizontalScrollIndicator = false,
TranslatesAutoresizingMaskIntoConstraints = false,
AlwaysBounceVertical = true,
Bounces = true
};
_refreshControl = new UIRefreshControl { TranslatesAutoresizingMaskIntoConstraints = false };
_refreshControl.Enabled = true;
_refreshControl.ValueChanged -= RefreshControl_ValueChanged;
_refreshControl.ValueChanged += RefreshControl_ValueChanged;
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
_scrollView.RefreshControl = _refreshControl;
}
else
{
_scrollView.AddSubview(_refreshControl);
}
this.View.AddSubview(_scrollView);
_scrollViewContainer = new UIView { TranslatesAutoresizingMaskIntoConstraints = true };
_scrollView.AddSubview(_scrollViewContainer);
_scrollView.Anchor(top: this.View.SaferAreaLayoutGuide().TopAnchor, leading: this.View.LeadingAnchor, bottom: this.View.SaferAreaLayoutGuide().BottomAnchor, trailing: this.View.TrailingAnchor);
}
Nothing really fancy, I create my UIScrollView add it as a sub view of the main View. Create a UIRefreshControl and add it to the scroll view. Create and add a plain UIView to the UIScrollView in order to put all my subviews.
Because I'm using AutoLayout the ScrollView is anchored within constraints of the main view.
Now later in ViewWillAppear(), I'm creating Card Views that are added to the plain UIView. These card views are using autoloayout to be stacked on top of each other, the first card is anchored to the container view's top, leading and trailing anchors.
Everything in these cards is using autolayout, to basically stack everything.
public UIView BuildQOLCard()
{
CardView qolCard = new CardView();
_scrollViewContainer.AddSubview(qolCard);
qolCard.TranslatesAutoresizingMaskIntoConstraints = false;
qolCard.CornerRadius = 5f;
qolCard.ShadowOffsetHeight = 0;
qolCard.ShadowOffsetWidth = 0;
qolCard.BackgroundColor = UIColor.White;
...
qolCard.Anchor(leading: _scrollViewContainer.LeadingAnchor, trailing: _scrollViewContainer.TrailingAnchor, top: _scrollViewContainer.TopAnchor, padding: new UIEdgeInsets(10f, 10f, 10f, 10f));
return qolCard;
}
Of course the container view won't magically know how to size itself. So I'll have to give it a size otherwise I won't see anything.
So I mage a helper like this:
public void SizeScrollViewContentSize()
{
nfloat scrollViewHeight = 0.0f;
foreach (UIView view in this._scrollViewContainer.Subviews)
{
scrollViewHeight += view.Frame.Size.Height;
}
this._scrollViewContainer.Frame = new CGRect(0, 0, this._scrollView.Frame.Width, scrollViewHeight);
}
But I can't seem to figure out when to call it because the subviews are here but their sizes are unknown. I tried calling it from ViewDidLayoutSubviews().
Any help appreciated.
EDIT : Applying te recommandations of the accepted answer, led to scrolling being enabled but the scroll view always bounces back to top.
Here is the code right now:
public override void ViewDidLoad()
{
base.ViewDidLoad();
_scrollView = new UIScrollView
{
ShowsHorizontalScrollIndicator = false,
TranslatesAutoresizingMaskIntoConstraints = false
};
if (!UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
_scrollView.AlwaysBounceVertical = true;
_scrollView.Bounces = true;
}
_refreshControl = new UIRefreshControl { TranslatesAutoresizingMaskIntoConstraints = false };
_refreshControl.Enabled = true;
_refreshControl.ValueChanged -= RefreshControl_ValueChanged;
_refreshControl.ValueChanged += RefreshControl_ValueChanged;
if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
{
_scrollView.RefreshControl = _refreshControl;
}
else
{
_scrollView.AddSubview(_refreshControl);
}
this.View.AddSubview(_scrollView);
_scrollViewContainer = new UIView { TranslatesAutoresizingMaskIntoConstraints = false };
_scrollView.AddSubview(_scrollViewContainer);
_scrollView.Anchor(top: this.View.SaferAreaLayoutGuide().TopAnchor, leading: this.View.LeadingAnchor, bottom: this.View.SaferAreaLayoutGuide().BottomAnchor, trailing: this.View.TrailingAnchor);
// Only define the width of the container
_scrollViewContainer.TopAnchor.ConstraintEqualTo(_scrollView.TopAnchor).Active = true;
_scrollViewContainer.LeadingAnchor.ConstraintEqualTo(_scrollView.LeadingAnchor).Active = true;
_scrollViewContainer.WidthAnchor.ConstraintEqualTo(_scrollView.WidthAnchor).Active = true;
}
Card one:
CardView qolCard = new CardView();
_scrollViewContainer.AddSubview(qolCard);
qolCard.TranslatesAutoresizingMaskIntoConstraints = false;
qolCard.CornerRadius = 5f;
qolCard.ShadowOffsetHeight = 0;
qolCard.ShadowOffsetWidth = 0;
qolCard.BackgroundColor = UIColor.White;
...
qolCard.Anchor(leading: _scrollViewContainer.LeadingAnchor, trailing: _scrollViewContainer.TrailingAnchor, top: _scrollViewContainer.TopAnchor, padding: new UIEdgeInsets(10f, 10f, 10f, 10f));
card 2:
CardView goalsCard = new CardView();
_scrollViewContainer.AddSubview(goalsCard);
goalsCard.TranslatesAutoresizingMaskIntoConstraints = false;
goalsCard.CornerRadius = 5f;
goalsCard.ShadowOffsetHeight = 0;
goalsCard.ShadowOffsetWidth = 0;
goalsCard.BackgroundColor = UIColor.White;
...
// Top should be constrained to the previous CardView's Bottom
goalsCard.Anchor(leading: _scrollViewContainer.LeadingAnchor, trailing: _scrollViewContainer.TrailingAnchor, top: qolCard.BottomAnchor,padding: new UIEdgeInsets(10f, 10f, 10f, 10f));
Final anchor:
// constraint the last card bottom to the bottom of the scrollview container
goalsCard.Anchor(bottom: context._scrollViewContainer.BottomAnchor);
The Anchor Helper:
internal static void Anchor(this UIView uIView, NSLayoutYAxisAnchor top = null, NSLayoutXAxisAnchor leading = null, NSLayoutYAxisAnchor bottom = null, NSLayoutXAxisAnchor trailing = null, UIEdgeInsets padding = default, CGSize size = default)
{
uIView.TranslatesAutoresizingMaskIntoConstraints = false;
if (top != null)
{
uIView.TopAnchor.ConstraintEqualTo(top, padding.Top).Active = true;
}
if (leading != null)
{
uIView.LeadingAnchor.ConstraintEqualTo(leading, padding.Left).Active = true;
}
if (bottom != null)
{
uIView.BottomAnchor.ConstraintEqualTo(bottom, -padding.Bottom).Active = true;
}
if (trailing != null)
{
uIView.TrailingAnchor.ConstraintEqualTo(trailing, -padding.Right).Active = true;
}
if (size.Width != 0)
{
uIView.WidthAnchor.ConstraintEqualTo(size.Width).Active = true;
}
if (size.Height != 0)
{
uIView.HeightAnchor.ConstraintEqualTo(size.Height).Active = true;
}
}
A scroll view needs its Frame defined and it needs its Content defined. If the content is constrained properly, scrolling is automatic.
I'll go through this step-by-step - using Storyboard just so we can see what's happening. Absolutely no different when we do it through code.
Start with a View Controller, add a scroll view, and constrain it to all four sides (I gave it a green background so we can see it):
Now we add a "container" view (cyan background), and constrain all 4 sides to the scroll view:
If we run this, we see:
That's because the "container" has no width or height. Its Leading / Trailing / Top / Bottom constraints are defining the scroll view's content.
So, let's constrain it Equal Width and Equal Height to the scroll view:
run it, and we see this:
Which is fine, except... it will never scroll, because it is the same height as the scroll view.
So, I delete the Equal Height - because I want the container's content to determine its height.
I add a "CardView" and set its Top / Leading / Trailing constraints to Top / Leading / trailing of "container", with 10-pts "padding" and a Height of 100. I also give it a Bottom Constraint to the Bottom of "container" (+10) - which will "pull the bottom of the container" up to 10-pts below the bottom of the CardView:
resulting in:
Now I add a second CardView, constrain Leading / Trailing constraints to Leading / trailing of container, with 10-pts "padding" and a Height of 100. And I set its Top constraint to the Bottom of CardView-1 (+10), and I constrain its Bottom to the Bottom of "container" (+10):
Unfortunately, we now have a 10-pt constraint from Bottom of CardView-1 and a 10-pt constraint from Bottom of CardView-2 - which, of course, won't work.
So, let's delete the "Bottom to Superview" constraint from CardView-1
and we get this:
Let's repeat those steps for a few more card views, each time constraining Leading / Trailing to "container", Height of 100, Top to Bottom of previous CardView... and then constrain only the Bottom of the last CardView to the Bottom of "container":
and Yay! We can scroll:
Doing this via code is just a matter of remembering that each CardView's Top should be constrained to the previous CardView's Bottom -- except for the first card, which you constrain to the Top of "container" and the last card, which you constrain to the Bottom of "container".
All done with auto-layout. No need to do any "calculate height and set frame" code.
As a side note, you can use a StackView and get rid of most of that:
forget the "container"
add a stack view to the scroll view
axis: vertical, spacing: 10
constrain all 4 sides of the stack view to the scroll view with 10-pts padding
give the stack view a Width constraint equal to scroll view width -20 (for the 10-pts padding on each side)
Then, just add your CardViews as arrangedSubviews of the stack view.
And.... Done!

Adapt the height of a TextBox

I am working on a UserControl that contains a multiline TextBox.
When using my control, one will be able to set the text that will be displayed. The TextBox should then adapt its Height to make the text fit, the Width cannot change.
So here is the property that handles the text :
[Editor(typeof(MultilineStringEditor), typeof(UITypeEditor))]
public string TextToDisplay
{
get
{
return internalTextBox.Text;
}
set
{
internalTextBox.Text = value;
AdaptTextBoxSize();
}
}
My first attempt was rather simple :
private void AdaptTextBoxSize()
{
int nbLignes = internalTextBox.Lines.Length;
float lineHeight = internalTextBox.Font.GetHeight();
internalTextBox.Height = (int)((nbLignes) * lineHeight);
}
This did not work as it doesn't take into account spacing between two lines of text. So the more lines I have in the text, the more I get clipped.
So I tried this :
private void AdaptTextBoxSize()
{
Size textSize = internalTextBox.GetPreferredSize(new Size(internalTextBox.Width, 0));
internalTextBox.Height = textSize.Height;
}
This does work when all the lines in the textbox are shorter than the Width. But when one line is longer and should be clipped to the next line, GetPreferredSize() returns a larger width than the one I passed, and therefore the height is too small.
So I changed again and tried this one:
private void AdaptTextBoxSize()
{
Size textSize = TextRenderer.MeasureText(
internalTextBox.Text,
internalTextBox.Font,
new Size(internalTextBox.Width, 0),
TextFormatFlags.WordEllipsis
);
internalTextBox.Height = textSize.Height;
}
This time the returned Width is correct, as it does not exceed the one I passed, but the height is the same as the previous trial. So it doesn't work either. I tried different combinations for TextFormatFlags, but could not manage to find the winning one...
Is this a bug from the framework?
The real question here is, is there another thing I can try, or another to achieve what I want (i.e. auto-adapt the height when setting the TextToDisplay property)?
TextBox.GetPositionFromCharIndex returns the pixel position of a character. Position here means top/left so we need to add one more line..
This seems to work here:
textBox.Height = textBox.GetPositionFromCharIndex(textBox4.Text.Length - 1).Y + lineHeight;
I get the line height like this:
int lineHeight = -1;
using (TextBox t = new TextBox() { Font = textBox.Font }) lineHeight = t.Height;
I set the Height instead of the ClientSize.Height, which is slightly wrong unless BorderStyle is None. You can change to textBox.ClientSize = new Size(textBox.ClientSize.Width, l + lh);

Locating an element on hover - WPF - C# - Dynamic Data Display

In Dynamic Data Display, there is a class called MarkerPointsGraph. This derives from FrameWorkElement (through a series of other classes along the way, the most immediate parent being PointsGraphBase) and overrides the OnRenderMethod to draw a set of markers on the chart.
It does this by calling a render method in the appropriate marker type (say, Triangle, Circle and so on) once for each point rendered on the screen. I need to find a way to identify when the mouse hovers on one of these markers so that I can set the tooltip for that marker.
I have a set of methods that allow me to convert a point from the screen position to the viewport position and to the data position and back. i.e. converts a screen value to a corresponding data value or viewport value and vice versa.
I also have the tooltip opening event on this framework element and the pixel size of each of these markers. As long as the user hovers on a particular marker, I need to identify which point he has hovered on and let the markerpointsgraph set the tooltip value.
However, the transforms and methods for converting the values don't seem to be working fine, especially in the x direction. THe y - direction seems to be fine.
Below is some sample code that will explain the idea:
double selectedPointX = 0;
double selectedPointY = 0;
CoordinateTransform transformLocal = this.primaryPlotter.Transform;
if (series.SeriesDescription.YAxisAffinity == AxisAffinity_Y.Y1)
{
selectedPointX = Mouse.GetPosition(this.primaryPlotter).ScreenToViewport(transformLocal).X; //Getting the mouse positions
selectedPointY = Mouse.GetPosition(this.primaryPlotter).ScreenToViewport(transformLocal).Y;
}
else if (series.SeriesDescription.YAxisAffinity == AxisAffinity_Y.Y2 && injSecondaryAxis != null)
{
transformLocal = injSecondaryAxis.Transform;
selectedPointX = Mouse.GetPosition(this.injSecondaryAxis).ScreenToViewport(transformLocal).X;
selectedPointY = Mouse.GetPosition(this.injSecondaryAxis).ScreenToViewport(transformLocal).Y;
}
else if (series.SeriesDescription.YAxisAffinity == AxisAffinity_Y.Y3 && injTertiaryAxis != null)
{
transformLocal = injTertiaryAxis.Transform;
selectedPointX = Mouse.GetPosition(this.injTertiaryAxis).ScreenToViewport(transformLocal).X;
selectedPointY = Mouse.GetPosition(this.injTertiaryAxis).ScreenToViewport(transformLocal).Y;
}
foreach (var item in SeriesList)
{
if (item.Key == GraphKey)
{
for (int i = 0; i < item.Value.Collection.Count; i++)
{
//Calculate the size of the marker on the screen and allow for some level of inaccuracy in identifying the marker i.e anywhere within the marker is allowed.
double xlowerBound = item.Value.Collection[i].DataToViewport(transformLocal).X - series.MarkerSize;
double xUpperBound = item.Value.Collection[i].DataToViewport(transformLocal).X + series.MarkerSize;
double ylowerBound = item.Value.Collection[i].DataToViewport(transformLocal).Y - series.MarkerSize;
double yUpperBound = item.Value.Collection[i].DataToViewport(transformLocal).Y + series.MarkerSize;
//If point is within bounds
if (!(selectedPointX < xlowerBound || selectedPointX > xUpperBound || selectedPointY < ylowerBound || selectedPointY > yUpperBound))
{
strToolTip = item.Value.Collection[i].X + ", " + item.Value.Collection[i].Y; //This point is set as the tooltip
break;
}
}
break;
}
}
Here, injSecondary and injTertiary are two injected plotters that provide two vertical Y axes. They behave pretty similar to the primary chart plotter.
Is there anything wrong occurring here? For some reason, points well ahead of the actual clicked point are passing the buffer clause.
Hmm, looks like you're going about this the wrong way to me. If you dig into the source code of D3, you can open one of the marker classes and directly edit the tooltip there. Every System.Windows.Shapes element has a Tooltip property which you can assign right in the marker class. All you need to do is decide what data the tooltip holds.
Example - The CircleElementPointMarker Class :
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;
namespace Microsoft.Research.DynamicDataDisplay.PointMarkers
{
/// <summary>Adds Circle element at every point of graph</summary>
public class CircleElementPointMarker : ShapeElementPointMarker {
public override UIElement CreateMarker()
{
Ellipse result = new Ellipse();
result.Width = Size;
result.Height = Size;
result.Stroke = Brush;
result.Fill = Fill;
if (!String.IsNullOrEmpty(ToolTipText))
{
ToolTip tt = new ToolTip();
tt.Content = ToolTipText;
result.ToolTip = tt;
}
return result;
}
public override void SetMarkerProperties(UIElement marker)
{
Ellipse ellipse = (Ellipse)marker;
ellipse.Width = Size;
ellipse.Height = Size;
ellipse.Stroke = Brush;
ellipse.Fill = Fill;
if (!String.IsNullOrEmpty(ToolTipText))
{
ToolTip tt = new ToolTip();
tt.Content = ToolTipText;
ellipse.ToolTip = tt;
}
}
public override void SetPosition(UIElement marker, Point screenPoint)
{
Canvas.SetLeft(marker, screenPoint.X - Size / 2);
Canvas.SetTop(marker, screenPoint.Y - Size / 2);
}
}
}
You can see in this class we have code built in to handle tooltips. The ToolTipText is a property that derives from the parent class - ShapeElementPointMarker. All you have to do is assign that property with the data that you need to show.
Use a cursorcoordinategraph on the plotter in place of the Mouse.GetPosition to get the correct screen position and avoid the ScreenToViewport transform. Also, use to DataToScreen while creating the bounds.

Categories

Resources