Can anyone tell me why the below is happening please? The red line signifies where the annotation should be positioned (along X axis), but it's always rendered right on the left edge... I did a few searches in Google and SO, and found an answer which implies that PixelPositionToValue(Mouse.X) would sort it, but even using this it ends up exactly the same.
Private Sub AssignNewDownTime()
Dim sStr As String = InputBox("Please enter downtime reason")
Dim annot As New Charting.RectangleAnnotation()
annot.ClipToChartArea = "Chart1"
annot.BackColor = Color.DarkRed
annot.ForeColor = Color.White
annot.AllowMoving = True
annot.AllowAnchorMoving = False
annot.AllowSelecting = False
annot.IsMultiline = False
annot.AllowTextEditing = False
annot.IsSizeAlwaysRelative = False
annot.X = Chart1.ChartAreas(0).AxisX.PixelPositionToValue(StartMousePoint.X)
annot.Y = 10
annot.Width = 25
annot.Text = sStr & " /X: " & annot.X & "Y:" & annot.Y
Chart1.Annotations.Add(annot)
Chart1.Invalidate()
End Sub
Gah! This always happens... I pull my hair out for hours, post on SO and within 5 minutes I've fixed it. Anyway, for future generations that may also pull their hair out, here's the solution:
Annotations X and Y aren't set to that of the chart, so whereas the chart will range from 0.0 to 1.0, annotations default range is 0 to 100. Nightmare! There are a couple of ways around this, I chose:
annot.AxisX = Chart1.ChartAreas(0).AxisX
Which sets the X axis of the annotation to mimic that of your chart. Thus the values and limits will be correct. As soon as I did this it worked instantly. You can of course set the AxisY of the annotation as well, but beware that in charting the Y is bottom to top. More information can be found here: http://msdn.microsoft.com/en-us/library/system.windows.forms.datavisualization.charting.annotation(v=vs.110).aspx - specifically (which I wish I'd read first...):
Annotations are commonly used to comment or elaborate on chart elements such as data points. Annotations can also be used to draw custom shapes.
By default, annotations are positioned using relative coordinates, with (0,0) representing the top-left corner, and (100,100) representing the bottom-right corner of the chart image. It is also possible to switch from this relative coordinate system to a system that uses axis values. With an axis coordinate system, X and Y, which represent the position of the top-left corner of an annotation, are set using X axis and Y axis values, instead of values that range from 0-100.
There are two ways to use axis values when you specify the position and size of an annotation:
Set the AxisX, AxisY or both of these annotation properties to the AxisX and AxisY property values of a ChartArea object.
Use the AnchorDataPoint property to anchor the annotation to a data point. In this case, its positioning is automatically calculated.
All annotations are derived from the Annotation class, which can be used to set attributes common to all Annotation objects, such as color, position, anchoring and so forth.
Anyway, hope this helps folk out.
Related
Is it possible to select shapes in Visio by specifying the coordinates of a selection rectangle? If so, how does one do this?
I need to select and delete any shape in a specific location on a Visio page.
I would like to be able to specify the coordinates of a lower left corner and an upper right corner on the page and have vba tell me the id's or handles or something that would allow me to delete these shapes as I need to place a new shape in that particular location. I am looking for something like
shapes = MyVisioPage.SelectByRectangularCrossingBox(lowerleftX,lowerleftY,upperrightX,upperrightY)
You could actually draw a rectangle with those coordinates and then use Shape.SpatialNeighbors to find out all shapes in that rectangle.. Something like this (VBA):
Function SelectByRectangularCrossingBox(page, _
lowerleftX, lowerleftY, upperrightX, upperrightY) As Selection
scopeId = page.Application.BeginUndoScope("try")
Set rc = page.DrawRectangle(lowerleftX, lowerleftY, upperrightX, upperrightY)
Set SelectByRectangularCrossingBox = rc.SpatialNeighbors(visSpatialContain, 0.01, 0)
page.Application.EndUndoScope scopId, False
End Function
The code is wrapped around with BeginUndoScope/EndUndoScope to cancel changes.
I've written some VBA code that automatically creates a chart. One of the axes on this chart doesn't use normal labels but a graphic. I've stored the graphic as an image and I use the .Copy and .Paste methods to get a copy of this image onto the chart.
Here is where it gets confusing. I need to rotate the image to get it aligned with the axis (using the .rotation property). But when I set the .top and .left properties the shape doesn't end up where I would expect. In fact setting the properties to 0 and 0 doesn't do what I would expect either. I've tried changing the order of the way I set the properties on the image object but it only appears in a different (wrong) location.
I'm sure I'm missing some vital aspect of how VBA/Excel is placing the object relative to what I'm setting the top and left properties to. Basically my goal is to make the image on the left side of the chart with the same width as the plot area's height (since the image is rotated I theorize this will make it the same size).
This code does not work:
Sheets(ImageSheet).Shapes("agreement").Copy
c.Chart.Paste
c.Chart.Shapes(1).Rotation=270
c.Chart.Shapes(1).width = c.Chart.PlotArea.height
c.Chart.shapes(1).left = 5
c.Chart.Shapes(1).top = c.Chart.PlotArea.top
I've also tried this code
c.chart.Shapes(1).top = c.chart.PlotArea.top + c.Chart.PlotArea.height
because I thought maybe it was calculating the "top" as the upper-left corner of the image object when it is not rotated (rotating 270 degrees makes this point in a place where it should align with the bottom of the plot area). But that doesn't do what I expected either.
The image is a skinny rectangle that acts as a label for the axis. The chart will end up being laid out like this: http://imgur.com/NrSXR and the axis label image would be something like this http://imgur.com/08EWU
What am I missing here?
Is it possible for you to align your chart into a position where the shape could rest align/on a cell?
IF YES then here is a suggestion:-
You could position shape into a cell. Then adjust the size to what you need. And rotate.
Then change its bring forward property be shown on the Chart.
Next Group Chart and the Shape
PS: I recorded a macro. However it's best if you could show us what your the exact picture (=how your sheeet/chart/image should look like) of your question.
I ended up rotating and resizing the image before copying and pasting to the chart and then positioning it. I had to use the IncrementLeft and IncrementTop methods rather than setting the left and top properties directly because that did not have the desired effect.
When doing the paste into the chart the object always ended up in the upper left hand-corner so I could increment to the left by the small amount I wanted as a margin I wanted there and increment the top by the value of PlotArea.top to align it with the plot area.
I was also surprised that when creating the copy of my image it retained the "name" i referred to it as when I copied it to the new sheet and chart. This was especially useful for positioning the image once it was on the chart.
I also needed execute this code at the very end of my procedure, after everything else had been positioned and aligned, or when I positioned the data labels for one of my series they wouldn't appear correctly.
Here is the code that I ended up using:
'make a copy of the label image and refer to it with a variable for convenience
Sheets(ImageSheet).Shapes("maturity").Copy
i = Sheets(ImageSheet).Shapes.Count
Sheets(ImageSheet).Paste
Dim axisImage As Shape
Set axisImage = Sheets(ImageSheet).Shapes(i + 1)
'rotate and resize the image
With axisImage
.Rotation = 270
.width = c.Chart.PlotArea.height
End With
'cut and paste the new image to the chart
axisImage.Cut
c.Chart.Paste
'position it in the chart using IncrementTop and IncrementLeft because setting the properties directly does not have the desired effect
c.Chart.Shapes("maturity").IncrementTop c.Chart.PlotArea.top
c.Chart.Shapes("maturity").IncrementLeft c.Chart.PlotArea.left - c.Chart.Shapes("maturity").height
Getting a strange error with this code. This excerpt is the beginning of the main code. First it opens a form that takes 3 inputs. The inputs aren't being used right now so it's not the issue.
Public triangle As Range
Public Height, Width, i As Integer
Sub asdf()
Set triangle = Selection
WeightsUserForm.Show
Height = triangle.Rows.Count
Width = triangle.Columns.Count
Debug.Print Height
Debug.Print Width
This gives me 12 as Height and 12 as Width, which is correct based on what I've selected.
Now here is the sub that runs when you press OK on the user form:
Private Sub OKButton_Click()
Set triangle = Selection
Height = triangle.Rows.Count
Width = triangle.Columns.Count
Debug.Print Height
Debug.Print Width
This gives me Height as 28.5 and Width as 99. I have no clue where this comes from. I even checked all the objects on my userform as if maybe it was interpreting that as my selection (which is clearly wrong since those objects wouldn't have a rows property).
Ideally I would like to save my initial selection as a "public variable" if that is a real thing.
Any help is appreciated.
EDIT: Further update. I have set the variable triangle manually now ie
triangle = Range("B3","M14")
This STILL gives me the same strange dimensions. Now I'm really stumped.
2xEDIT: When I don't use the variables Height and Width and just refer directly to triangle.Rows.Count and triangle.Columns.Count it gives me correct answers. So I can run my program properly now. I would still love to know why using a variable was wrong though.
So you're saying that the line
Public Height,Width,i As Integer
is only declaring i as an integer and not specifying a type for height and width? If so that would be my problem.
I redid it declaring both Height and Width properly as integers but it still bugged out the same way. However using private variables W and H defined the same way it works. shrug
Reading between the lines, I think your issue is that Debug.Print Height and Debug.Print Width is printing the size of your form.
This will happen if your declarations are in a Module rather than the Form class, because Height and Width are keywords applicable to the form, so the local versions are used in preference.
To solve, you should do all of these things (not all of this is absolutely necassary, but is all good practice):
Use Option Explicit in all your modules
Don't use Keywords for variable names
Scope your variables, in order of preference, to Procedure, then local Module, then public Module, and only when there is good reason
Prefix your remote module calls with the module name, eg Module1.Height
BTW, using Dim Height, Width, i as integer declares declares Height and Width as the default type which is Variant.
I'm attempting to write a solution so that my user's can "watermark" their Images with their Company Logo. I've got the actual watermarking part done and working so now I'm creating the "upload logo" feature so that they can provide me with the Logo they wish to appear watermarked onto their Images.
I'm using VB.NET and this will probably end up in a Web Service that accepts the Logo JPG file, and returns the "altered" Logo. What I need to happen in this Web Service is:
1) Gray-scale the image. Which I have working as well, thanks to this article.
2) Make the background transparent (so the logo looks clean when watermarked onto an image). This is where I'm stuck.
I think for the most part, any logos that are uploaded will have a generic white background but I can't assume that. Is there a way to somehow detect the background of an image or the background colors, so that I may make those colors transparent?
I've downloaded and ran this project from code.google.com, called Transpoint, which is pretty much what I need except I won't be able to have this as a stand-alone app. Also, I think this is written in Python which is foreign to me.
So basically what I need is just a way to determine the background on an Image (if that's even possible?) or even just the background colors so that I may make them transparent. Any help/advice/suggestions would be greatly appreciated!
Thanks,
Lloyd
You could do something like reading the upper-left pixel and assuming that that pixel is representative of the background color, and then iterating through the image and setting the alpha value of each matching pixel (matching by color) to 0.
I can pretty much guarantee you, however, that the result will be awful. For transparency to work properly and look good, the image needs to support partial transparency, such that some pixels are completely transparent whereas some are only partially transparent. Any algorithm that takes a non-transparent image and sets only one color in the image to fully transparent is going to end up with jagged edges.
Most companies I've ever dealt with have versions of their logos done by professional artists, with smooth, partial transparency. You'd be much better off just requiring customers to submit a logo with transparency than trying to make a non-transparent image into a transparent one with code. Sorry, but this just doesn't work.
I think this would actually work (sorry it's just a description):
Assuming the upper-left corner pixel is the image's background color, you could iterate through each pixel in the image and set the alpha to 0 if the pixel color matches the background color (either exactly or within some threshold color distance). This would then leave you with an image with a transparent background but jagged edges when you re-draw it on a different-color background. Also, if the background color is present anywhere inside the image itself, it will be turned transparent, which you don't want.
To fix the latter problem, your algorithm should start scanning each row of the image from left to right, and stop when it reaches a non-background color pixel; at this point it should start on the same row and scan right to left until reaching a non-bg pixel.
To fix the edges, you can just blur the alpha values of the bitmap. Basically, you re-calculate the alpha value of each pixel as the average of 9 pixels (itself and the 8 pixels surrounding it, and just the alpha values - not the rgb values). To prevent sequencing artifacts, you would have a temporary array to store the averaged pixel values, which you would then copy back into the image's alpha values at the end of the process.
Also, there's probably one or more third-party tools that do this (is LEAD Tools still around?).
#MusiGenesis (and anyone else who may be interested) here is what I did to (kind of) solve my problem. I basically followed the first half of your idea. I've created a function that will accept a bitmap, check each pixel against the first pixel at (0,0) - using a threshold of 10 for each RGB color. For each color within that threshold, the pixel is made transparent. Here is my code, which seems to work alright for the few images I've tried it with:
Private Function TransparifyBackground(ByVal bmp As Bitmap) As Bitmap
Dim temp As Color
Dim background As Color = bmp.GetPixel(0, 0) 'top left will be assumed background color
For y As Integer = 0 To bmp.Height - 1
For x As Integer = 0 To bmp.Width - 1
'get the pixel for this position:
temp = bmp.GetPixel(x, y)
If ColorsMatch(background, temp) Then
'Make the Alpha value 50 for each pixel, leaving the other colors
Dim newColor As New Color
newColor = Color.Transparent
bmp.SetPixel(x, y, newColor)
End If
Next
Next
Return bmp
End Function
Private Function ColorsMatch(ByVal background As Color, ByVal temp As Color) As Boolean
Dim nThreshold As Integer = 10
Dim temp_R As Integer = CInt(temp.R)
Dim temp_G As Integer = CInt(temp.G)
Dim temp_B As Integer = CInt(temp.B)
Dim R As Integer = CInt(background.R)
Dim G As Integer = CInt(background.G)
Dim B As Integer = CInt(background.B)
'check the difference of each value against our threshold:
If ((temp_R - R) < nThreshold) AndAlso ((temp_G - G) < nThreshold) AndAlso ((temp_B < B) < nThreshold) Then
Return True
Else
Return False
End If
End Function
How can I set my labels to align on the right even when they have diffrent lenghts.
I have a set of labels which are occuring next to each other and also underneath each other.
The problem now is that they always align from the left within the label row,but I need them to align on the right as they are showing sums from other rows. Just to verify I am not talking about the text align I am looking for a solution to align my labels.
Thanks in advance
Simply set the AutoSize property to False in the designer. Adjust the size to fit the column. Then set the TextAlign to one of the right-alignment ones.
You should be able to do it at runtime using the following code:
'find the current right alignment position
Dim rightAlign As Integer = Label1.Left + Label1.Width
'set the text (assumes AutoSize is set to True)
Label1.Text = value
'adjust position so the right hand point is in the same position as before
Label1.Left = rightAlign - Label1.Width
My method is even more strange. I create the labels and then when laying out the fields for the report adjust the labels for number (etc) that are to be right aligned
Note: all labels end with 'lbl'
- txtNew is the report column text box.
- get the column's left edge plus the width of the column minus the width of the label. Works! Just not my favorite way to do it.
' *** NEED TO CALC POSITION FOR RIGHT JUSTIFY OF LABEL !!!!!
If ShouldRightJustify(rs.Fields(i).Type) Then
rpt.Section(acPageHeader).Controls(rs.Fields(i).Name & "lbl").Left = _
(lblCol + txtNew.Width) _
- rpt.Section(acPageHeader).Controls(rs.Fields(i).Name & "lbl").Width
End If
If you are asking how to do this from the designer, use the Format Menu.
Select all the controls you want to align, then click the control you want the other aligned to. Do Format > Align > Rights.
If you are trying to do this at run-time you can loop through the controls you want to align and set their .X property according to their width. For example. To align a label so that it's right side is at X=200... SomeLabel.X = 200 - SomeLabel.Width.