I'm trying to get the maximum amount a scrollviewer can scroll in the vertical and horizontal direction but I need to be doing this in a layout updated callback. This is what I'm currently doing:
viewer.ScrollToRight( );
doublehmax = viewer.HorizontalOffset;
viewer.ScrollToBottom( );
double vmax = viewer.VerticalOffset;
But this casues an error: "Unhandled Error in Silverlight 2 Application Layout cycle detected. Layout could not complete."
Is there a way I can get the max horizontal and vertical offsets of the scroll view area reliably even after window resizes and the like?
use ScrollableWidth and Scrollableheight
I think what you want is the ExtentWidth and ExtentHeight, right? You want the max size of what's inside the ScrollViewer.
To get max offset you need to check the ScrollViewer's ScrollBar.
ExtentWidth and ScrollableWidth is not what you want, they include the ScrollViewer's ViewportWidth (same with height).
Here's a ScrollViewer extension method to get a ScrollBar reference (because it's part of ScrollViewer's template):
public static ScrollBar GetScrollBar(this ScrollViewer sv, Orientation orientation) {
if(orientation == Orientation.Vertical) {
return sv.Template.FindName("PART_VerticalScrollBar", sv) as ScrollBar;
}
return sv.Template.FindName("PART_HorizontalScrollBar", sv) as ScrollBar;
}
Then to get the maximum:
var hScrollBar = viewer.GetScrollBar(Orientation.Horizontal);
double maxOffset = hScrollBar.Maximum;
(This is w/ WPF not Silverlight but googling landed me here so not sure if Template names are the same but you should get the idea)
Related
I have some string labels that are associated with a number each.
I've created a Chart element with one ChartArea and one Series which is using the SeriesChartType.Bar type to show my labels on the x-axis (which, confusingly, is the vertical axis when using the Bar type, but anyway...) and show the number as a bar next to it.
Since I have many labels that don't fit on the screen at once, I "enabled" the scrollbar using the Zoom method in my Paint event handler like this:
private void chart1_Paint(object sender, PaintEventArgs e)
{
var scaleView = chart1.ChartAreas.First().AxisX.ScaleView;
var pos = double.IsNaN(scaleView.Position) ? 0.0 : scaleView.Position;
scaleView.Zoom(pos, pos + chart1.Height / 22.0);
}
I don't know if this is the proper way to do that, but it does (almost) what I want:
show the scrollbar if there are too many data points (labels) to fit on the screen
update the visible area properly when the window is resized
There is only one annoying thing: If due to scrolling a long label appears in the visible area or disappears, the area occupied by the labels is adjusted to the longest visible label. I hope these screenshots explain what I mean:
Here one long label is visible (at the bottom):
Here I scrolled up by one unit so that the long label is not visible any more:
This is super annoying during scrolling as everything gets rescaled whenver a long label appears or disappears.
How to fix the area occupied by the labels to always fit the longest label in the series, no matter if it is currently visible or not? I tried IsLabelAutoFit = false for both, x- and y-axis, but that doesn't help.
Ok, I've got it. I used
chartArea.InnerPlotPosition.Auto = false;
InnerPlotPosition.X = 33.333;
to give one third of the chart area to the labels and the other two thirds to the bars.
InnerPlotPosition.Auto = false makes this fixed so that it doesn't update while scrolling.
Before I go into my question, let me explain my setup:
First: I have a PictureBox that holds a Bitmap which is generated at runtime. This Bitmap can be different widths but always the same height.
Second: PictureBoxes do not support scrolling, therefore, I have the PictureBox docked in a panel. Initially, I had used the panel's autoscroll feature, but abandoned that after I discovered through this article that PictureBoxes have a size limit. I also learned that it's better to instead have small PictureBoxes and only draw what needs to be seen instead of the whole image.
Third: So I added a HScrollBar, which is fine and dandy, but I can't seem to figure out the math behind how big to make the scroller. I tried setting the maximum of the scrollbar to the length of the bitmap, but as you can see the size of the scroller is much smaller in mine than the one Windows puts in if I use the autoscroll feature.
My question is, what is the math behind the scroller size and how do I emulate that in my custom scrollbar?
Let me know if my question is unclear and I will try my best to make it more understandable. And thanks in advance for your help!
I figured out what was the problem. Perhaps I should have tried a little longer. :)
The answer lies in the LargeChange property. I let the Maximum at the total width of the bitmap and then set the LargeChange to the width of what I wanted to show. (i.e. the width of the PictureBox)
The size of the "scroller" is determined by the ratio of the value of LargeChange to the value of Maximum. For example, if the width to show (LargeChange) is 100 and the total width (Maximum) is 300 then the "scroller" size will be 1/3 of the scrollbar length. (100/300).
I got same problem too and tried to figure it out, I have a panel which contain another panel inside it called panelChild, and the default scrollbar is small, I need lager scrollbar, so I use HScrollBar to do that (display over-top of default scrollbar), I post my solution here, may be it helpful to someone
public Form() {
InitializeComponent();
hScrollBar.Maximum = panelChild.Width;
hScrollBar.LargeChange = panel.Width; // panel which contain panelChild, and this hScrollBar will same as panel scrollbar
hScrollBar.Scroll += HScrollBar_Scroll;
}
private void HScrollBar_Scroll(object sender, ScrollEventArgs e)
{
int diference = e.OldValue - e.NewValue;
foreach (Control c in panel.Controls)
{
c.Location = new Point(c.Location.X + diference, c.Location.Y);
}
}
So My first question is whether the scrollviewer automatically updates its extent or whether I need to call invalidate measure on scrollviewer to force it to update.
Anyway, I have a scrollviewer with a graph inside. When I use a zoom select box I want the axis division unit to change, the scrollviewer's content to change, scrollviewer to recalculate its new extent, and finally to scroll to the correct offset.
Currently I have AxisDivisionUnit as a Dependency Property that on update invalidates the contents, which then remeasures the content and redraws.
sg.InvalidateMeasure();
sg.UpdateLayout();
sg.DrawSignal();
and in another class,
AxisDivisionUnit = NewAxisDivisionUnit;
//need to scroll to new position based on location chosen with zoombox
//calculate position of originalOffsetinTimescaleunits on new scale.
double NewNumberOfPixelsPerUnit = (double)(NUMBER_OF_DEVICE_INDEP_PIXELS_PER_INCH) / AxisDivisionUnit;
double NewPixelOffset = HorizontalUnitOffset * NewNumberOfPixelsPerUnit;
if (NewPixelOffset > sv.ExtentWidth)
{
NewPixelOffset = sv.ExtentWidth - sv.ViewportWidth;
}
sv.ScrollToHorizontalOffset(NewPixelOffset);
However, The scrollviewer extent has not updated by the time I wish to scroll, so the code doesn't function as I wish. I guess I could also potentially call invalidatemeasure on the scrollviewer here, but then how do i ensure that the AxisDivision has changed? I need to ensure that since the size of the content depends on the property.
Hi I had the same trouble and I fixed it by calling updateLayout method of scrollviewer control before check scrollviewer.extent property.
I hope it helps.
I am trying to create a custom user control in WPF. I want to be able to set the size manually when I later use the control within another window.
As a short test I have just made a control comprising a canvas within a grid, which totally fills the control. When initialised it draws a rectangle within itself showing its size. I then position this on a window, making it whatever size I want.
However I now have problems, as if I make the height of the rectangle I draw
this.ActualHeight
then when the control initialises this value is still 0, and so I get nothing. If instead I use
this.Height
then I get the height that I made it during design time, and not the size I have subsequently made it within the window.
The height and width seem to be set within the XAML designer, so I don't know what to do.
Is there an easy way around this?
I think what you're experiencing is how WPF performs layout and specifically how Canvas does not participate in Layout.
In your specific case you are setting the width of the Rectangle to Canvas.ActualWidth? Unless the width / height of the canvas have been explictly set then the ActualWidth/Actualheight will be zero, hence you can't get a point of reference within which to position children of Canvas. What I would do is bind the canvas width and height to its parent container (or set in code) to get the ActualWidth / ActualHeight correctly propagated.
As a point of interest try this example to understand how the WPF Layout engine works. The following code can force a height on a FrameworkElement to set the Width, Height as normal, then force a layout (measure, arrange pass) on the element in question. This causes the WPF layout engine to measure/arrange the element and propagate the Width, Height to ActualWidth, ActualHeight.
For example:
// Set the width, height you want
element.Width = 123;
element.Height = 456;
// Force measure/arrange
element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
element.Arrange(new Rect(0, 0, element.DesiredWidth, element.DesiredHeight));
// Subject to sufficient space, the actual width, height will have
// the values propagated from width/height after a measure and arrange call
double width = element.ActualWidth;
double height = element.ActualHeight;
Also see this related question for clarity. I have used the above method occasionally to measure the text in a textblock so as to position correctly on a canvas.
I have a control whose parent is a ScrollableControl. How do I find the part of the control that's actually visible to the user? Both are rectangular - there's no funny business with Regions.
I think the GetVisibleRectangle method I wrote below is what you were requesting. Successive runs of this with scrolling yielded the following output as the control was scrolled:
{X=0,Y=0,Width=0,Height=0} - button4 was scrolled out of view. Note that the value here is Rectangle.Empty.
{X=211,Y=36,Width=25,Height=13} - button4 was scrolled so the upper left corner was visible
{X=161,Y=36,Width=75,Height=13} - button4 was scrolled so the top half and entire width was visible
{X=161,Y=26,Width=75,Height=23} - button4 was scrolled to be entirely visible
Note how in addition to the Width and Height changes that the X,Y also changed with scrolling.
Source:
private void button1_Click(object sender, EventArgs e)
{
Rectangle r = GetVisibleRectangle(this.panel1, button4);
System.Diagnostics.Trace.WriteLine(r.ToString());
}
public static Rectangle GetVisibleRectangle(ScrollableControl sc, Control child)
{
Rectangle work = child.Bounds;
work.Intersect(sc.ClientRectangle);
return work;
}
AutoScrollPosition represents the location of the scrollable control's display rectangle. The X and Y coordinate values retrieved are negative if the control has scrolled away from its starting position (0,0). When you set this property, you must always assign positive X and Y values to set the scroll position relative to the starting position. For example, if you have a horizontal scroll bar and you set x and y to 200, you move the scroll 200 pixels to the right; if you then set x and y to 100, the scroll appears to jump the left by 100 pixels, because you are setting it 100 pixels away from the starting position. In the first case, AutoScrollPosition returns {-200, 0}; in the second case, it returns {-100,0}.
Source: MSDN: ScrollableControl.AutoScrollPosition Property
The following link might helps to resolve this problem
http://www.trace-solution.com/2012/06/how-to-get-visibleviewable-area-of-user.html .