C# Calculate the image scroll with scrollbar - c#

I'm working a 2d tile-editor, used Winforms and XNA(for the map rendering) and i have a problem with the custom scrollbar.
I would like to know the formula for calculating the map scrolling depending the position value of my scrollbar.
Let me explain
Actually, i have a functional code but is not exact.
Here is my calculation (is the problem):
(Point Map.PixelMapSize = map size in pixel)
hScrollBarMap.Minimum = 0;
hScrollBarMap.Maximum = 100;
hScrollBarMap.LargeChange = hScrollBarMap.Size.Width * 100 /(Map.PixelMapSize.X);
mapScrollCalcul = ((hScrollBarMap.Value - hScrollBarMap.Minimum) * 100 / (hScrollBarMap.Maximum - hScrollBarMap.Minimum)) * Map.PixelMapSize.X / 100;
Sorry, I do not know how to explain. I found it by making a lot of tests...
But, the mapScrollCalcul is the final calcul to be applied to map position display.
What is the exact calculation for this ?
I hope you understand, I'm not speak English, just little understand.
But I understand you. :) (programmation language is universal)
Thank you for reading and, maybe, future responses.

You are overcomplicating things.
First, you are setting your LargeChange value relative to your image, not your view, and your scroll.Maximum relative to... what, exactly?
On MSDN, they say:
User interface guidelines suggest that the SmallChange and LargeChange
properties are set relative to the size of the view that the user
sees, not to the total size including the unseen part. For example, if
you have a picture box with scroll bars displaying a large image, the
SmallChange and LargeChange properties should be set relative to the
size of the picture box, not to the size of the image.
Now, lets assume that your image is 1000x1000 and your view is 100x100, and you are scrolling vertically. Then:
myVscroll.Minimum=0;
myVscroll.Maximum=image.Height; //1000
myVscroll.SmallChange = 10; // here: view.Height/10 or other value that makes scrolling look smooth
myVscroll.LargeChange = 100; // in this example, this is set to view.Height
And finally:
mapScrollCalcul = myVscroll.Value;

Related

Calculating image width when zoomed in C# Winforms

I'm working on a Winforms app that contains a large map image (5500px by 2500px). I've set it up so the map starts in full size, but the user can zoom out to a few different scales to see more of the map. The user is able to drag the map around to shift what they are looking at (like Google Maps, Bing Maps, Civilization, etc.).
When the map is full sized (scale = 1.0), I am able to prevent the user from scrolling past the borders of the image. I do this by calculating if they are trying to move past 0, or past the image width - current window size, similar to this:
if (_currHScroll <= 0) {
_currHScroll = 0;
}
This all works just fine. But, when I zoom out on the map (thus, making the image smaller), the limits for the bottom and right of the map break down. I know why this happens--because the Transform that is performed basically "compresses" the map a little bit, and so what used to be a 5000 px image is now smaller, depending on the scale. But, my limiters are based on the image size.
So, the user can scroll past the end of the map, and just sees white space. Worse things happen, I realize, but if possible I'd like to keep them from doing that.
I'm sure there is a straight-forward way to do this, but I haven't figured it out yet. I've tried simply multiplying my calculation by the scale, but that didn't seem to work (seems to under-estimate the size initially, then over-estimate on the smallest sizes). I've tried calculating the transform location of the bottom right of the image, and using that, but it turns out, that number is inverted, and I can't find what it relates to.
I'm including my transform point method here. It works just fine. It tells me, regardless of zoom level, what pixel was clicked on the original image. Thus, if someone clicks on point 200, 200 but the image is scaled at .5, it will show something like 400,400 as what was clicked (but, as I said, I don't think the scale value is a multiplier--using this just for demonstration purposes).
public Point GetTransformedPoint(Point mousePoint) {
Matrix clickTransform = _mapTransform.Clone();
Point[] xPoints = { new Point(mousePoint.X, mousePoint.Y) };
clickTransform.Invert();
clickTransform.TransformPoints(xPoints);
Debug.Print("Orig: {0}, {1} -- Trans: {2}, {3}", mousePoint.X, mousePoint.Y, xPoints[0].X, xPoints[0].Y);
return xPoints[0];
}
Many thanks in advance. I'm sure it's something relatively easy that I'm overlooking, but after several hours, I'm just not finding it.
If i understand right, you can calculate the maximum with your method GetTransformedPoint by using width and height from your Image as Point. The result can then be used inside your check...
And by the way, you are right, the scale value is a multiplier used as a factor. The only thing is, you have to cast the result to an integer.

Fill width of panel with rectangles(variable size)

I'm making a sorting algorithm vizualizer(Like Sound of Sorting without the Sound) But i stumbled upon a problem, and i can't seem to figure it out. I need to layout rectangles over the width of a panel(variable amount).
Image of the bars:
But when i get more bars, sometimes i will get a blank space because it won't fit:
How can i make it that some rectangles are a bit bigger than some others to make it fit? The way i calculate the width right now is like this:
barWidth = draw.Width / amount; //draw is the panel where i draw the rectangles on
Any help will be greatly appreciated!
I guess this is a rounding issue so I think you need to do some calulations:
totalBarWidth = sum of barWidths
blankSpace = draw.Width - totalBarWidth
numberOfBarsPerBlankSpacePixel = amount / blank space
Loop over the bars and add a pixel to the width of every numberOfBarsPerBlankSpacePixel bars.
Also don't forget to add the pixels to the x-axis location of the following bars.

Number line compression and docking controls

Hi Mathmeticians out there.
I am a little stumped and I was wondering if there was any sort of algorithm that could help me.
First the conceptual problem, Lets say I have a bunch of boxes that lie along an X axis. I want to be able to choose an arbitrary point A on the axis and have everything on the left scaled to 95% of its original width and position and to compensate, everything on the right will have to be scaled to 105%. The width of the resulting boxes is easy to calculate since it is the original width times the scale. The problem I am having is how to calculate the gap which has now been created at point A so that I can shift the second part left to close that gap.
Furthermore, I would like to not only select a point A, but also a B and C, etc.. as well and be able to close their gaps likewise.
--The real reason I am asking--
Now for the actual problem (in case anyone else out there has gone through this.) I have a control in a C# Winforms app that was made by some programmer before I got here. The control can contain any number of child controls that each have their own relative coordinates as a percentage of the Width or Height (i.e. A control with a relative X coordinate of 0.5 will be placed halfway across the parent container.
We desperately need to support multiple monitors and the problem that I am having is that if you dock a control or toolbar next to our proprietary control then the ClientRectangle is smaller so it shifts around the child borders like so
My boss doesn't like that the lines shift over monitor boundaries and wants me to only mess with the lines on the same monitor where the window was docked. I have been able to get 90% of the way using the concept above, but I can't seem to get the re-spacing calculation right.
Here is a Mathematical model of what I think for calculating the gap.
Let's say that you have a starting point A, and lets define it as xA.
Now, let's define the boxes
//Box{x0,x1}
Boxes = {[B1]{0,100},[B2]{100,200},[B3]{200,400},[B4]{400,450},[B5]{450,700}}
Now we have 5 boxes on the X axis.
Let's define;
A = xA = 370;
TotalLength = 700;
If you divide 700 by 2, that makes 350 which makes the mid point, and 370 is bigger than the mid point value. So that is being said, in this case you would need to shift the elements on the left to right. The calculation of the gap is as the following;
IF(Midpoint < A)
Gap = ((A- Midpoint) * 100 ) / TotalLength //This is the gap in percent
ELSE
Gap = ((Midpoint - A) * 100) / TotalLength
This way, you can find the gap. The Axis you need to shift towards will need to be decided based on the point you are selecting, if the selected point less than the Mid point then shift to right, if higher shift to left (to the positive axis route).
I hope this helps.
Thank you for your help Surgeon. Unfortunately I wasn't able to find a solution using your method. On the bright side, I was able to find a solution. The trick was to treat the X coordinate as the width between the Client Rectangle's left edge and the X coordinate's position and calculate it similarly to the child width.
For more specifics, here's the algorithm I have come up with for dealing with the docking problem:
var clientOriginalWidth = what the width of the Client rectangle would be without docks
var clientCompressedWidth = the width of the client now
//Calculate the Compression Ratio for each screen as follows
foreach(var screen in Screens){
var widthOfClientRectOnScreenNow = how much of the client rectangle is on this screen
var widthOfClientRectOnScreenWithoutDocks = how much of the client rectangle was on the screen before the docks were there
var compressionRatio = (widthOfClientRectOnScreenNow / clientCompressedWidth) /
(widthOfClientRectOnScreenWithoutDocks / clientOriginalWidth);
//Assuming control.xScale and control.widthScale are initially 0
foreach(var control in ParentControl){
var controlBounds = where the control was when the client was full width
if(controlBounds.X > screen.right){
var percentOfXPositionOnScreen = screen.right - screen.left / control.x;
controlBounds.xScale += percentOfPositionOnScreen * compressionRatio;
}
else if(control.X > screen.left){
var percentOfXPositionOnScreen = control.x - screen.left / control.x;
controlBounds.xScale += percentOfPositionOnScreen * compressionRatio;
}
if(screen.intersects(controlBounds){
var percentControlIsOnScreen = what percent of the control's width was on this screen
control.widthScale += percentControlIsOnScreen * compressionRatio;
}
}
}
The position is then found by multiplying the original X coordinate by the scale (same for the width). Once the docks are removed, recalculate the scale. When all docks are removed, the scale should be 1.
I have left out some specifics to make it more of a generalized algorithm, but one should be able to work through this on their own system.

How to get the height of the draggable bar in a scrollviewer?

I am trying to get the distance in pixel between the top of the control and the middle of my vertical scrollbar (not all the scrollviewer, only the bar you can drag to scroll the control).
I don't understand which property i should use.
This is the code i wrote:
double barHeight = /*to do*/;
double barUpperEdge = scrollViewer.VerticalOffset;
double distance = barUpperEdge + (barHeight/2);
Another question: which is the mesurament unit of scrollViewer.VerticalOffset? If it isn't in pixel what cast should i do?
You can calculate that value from the IScrollInfo Interface. The ScrollViewer Class implements this interface and exposes the relevant properties that you need to use. As far as I can remember, you need to utilise three properties:
ExtentHeight - Gets a value that contains the vertical size of the extent.
ViewportHeight - Gets a value that contains the vertical size of the content's viewport.
VerticalOffset - Gets a value that contains the vertical offset of the scrolled content.
To explain a little further, the viewport relates to the visible area of the ScrollViewer and the Extent represents the total size of the scrollable area. The VerticalOffset describes the amount that the scrollable content has been scrolled. Using these three values, you should be able to calculate your required values that relate to the ScrollBar. Try something like this:
double barHeight = ViewportHeight * scrollviewer.ActualHeight / ExtentHeight;
UPDATE >>>
Please note that it is generally bad practice to use constant values in your calculations. Microsoft have exposed many common properties for this very reason. In your case, I believe that you can make use of the SystemParameters.VerticalScrollBarButtonHeight property, although you may need to add something to accommodate Padding and/or Margin values:
double barHeight = ViewportHeight * (scrollviewer.ActualHeight - 2 *
SystemParameters.VerticalScrollBarButtonHeight) / ExtentHeight;
You know... I've just thought of something... you may even be able to get your required thumb Height from these SystemParameters... you could try the SystemParameters.VerticalScrollBarThumbHeight Property, although I don't think that it will work with custom ScrollBars.

How can I predict a XAML Label's height on a Canvas

I have an application where I need to dynamically build the content to a Canvas. Everything works just fine, but I am a little unsure of how I can set the y coordinates for the labels in the safest way. For example, I need to add three labels that are essentially lines of text. In Java Swing or C# GDI I would just query the the font for the line height and add that value to the y coordinate of the drawText command.
This is my code.
double y = 0.0;
_line1.Content = "Line1";
_line1.SetValue(Canvas.TopProperty, y);
_line1.SetValue(Canvas.LeftProperty, 0.0);
CanvasChart.Children.Add(_line1);
double textHeight = _line1.Height;
y += textHeight;
_line2.Content = "Line2";
_line2.SetValue(Canvas.TopProperty, 0.0);
_line2.SetValue(Canvas.LeftProperty, y);
CanvasChart.Children.Add(_line2);
This does not work because _line1.Height does not seem to be set to anything useful at this point. I suppose it has not rendered yet. The above code is in the loaded event for the window. ActualHeight does not help either.
Most code that I've seen seems to just set them to a hard coded value. That I suppose looks right on the developer's display, and you just hope looks good at other resolutions/DPI. In Swing and GDI I always had the best results finding out exactly how many pixels a string will be rendered at and using this to offset the next line.
You must call the Measure method, specifying an infinite available size. This will update the DesiredSize of the control:
_line1.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
double textHeight = _line1.DesiredSize.Height;
Another easy way to achieve the desired effect is to put the labels in a StackPanel.
In Swing and GDI I always had the best results finding out exactly how many pixels a string will be rendered at and using this to offset the next line.
This is possible in WPF as well. The GlyphTypeface class provides the AdvanceWidths and AdvanceHeights properties for each character in a typeface. By using CharacterToGlyphMap, you can map a character to an index within the AdvanceHeights, and use that to determine the actual height of any character.
For a detailed example, see GlyphRun and So Forth.

Categories

Resources