I have a VariableSizeGridView (aka GridView with a VariableSizedWrapGrid) and since the framework doesn't support ISupportIncrementalLoading on VariableSizedWrapGrid I implemented my own by "listening" to the GridView parent ScrollViewer (this specific case a Hub control)
Everything works fine, when I reach on the end of the scrollviewer my code calls a routine to get more data.
The problem occurs when the new data gets on the screen, the scrollviewer automatically scrolls to the end and the load process starts again, ending up in an infinite loop if I don't move the scrollbar elsewhere.
Is there any way to prevent that the scrollviewer automatically goes to the end after I added some items?
Thanks
You can create a Custom-control that overrides from VariableSizeGridView and listen, as you do, to the scrollviewer. Since you have access to the latest element of the GridView, after starting loading more elements, you can set the scrollviewer to the that latest position position. Get a visual reference to that element and then call this code:
FrameworkElement focusedElement = FocusManager.GetFocusedElement() as FrameworkElement;
GeneralTransform focusedVisualTransform = parent.TransformToVisual(_scrollViewer);
ApplyHorizontalScrolling(focusedElement, focusedVisualTransform);
private void ApplyHorizontalScrolling(FrameworkElement focusedElement, GeneralTransform focusedVisualTransform)
{
Rect rectangle = focusedVisualTransform.TransformBounds(new Rect(new Point(focusedElement.Margin.Left, focusedElement.Margin.Top), focusedElement.RenderSize));
double horizontalOffset = _scrollViewer.HorizontalOffset + (rectangle.Left);
_scrollViewer.ChangeView(horizontalOffset, 0, _scrollViewer.ZoomFactor);
}
Using and tunning this code, will help you preventing the scrollbar to go to the latest position.
Related
I am working with a SplitterPanel in winforms where on the right hand side I want a custom dropdown list control which displays a 2 columns dropdown list.
The problem is that with there being two columns I want to be able to have a larger dropdown list area than the actual dropdown, and therefore overlap the SplitterPanel if the list doesn't fit in the split area.
I have tried using .BringToFront();, however this does not work on the SplitterPanel and the control is hidden. I come from a web background where I would have used z-index for this but I am stumped with winforms. See below image of my issue.
Does anyone know how I can resolve this?
The z-index will determine which child controls sit higher and can overlap which others child controls. But it never helps when you want a (real as opposed to drop downs or menues) child overlap its own container. This never happens; and since the CheckedListBox is the child of the split panel it will never overlap it.
You will need to make the CheckedListBox sit not inside the splitter panel but in its Parent so the they are 'brethren'. Let's assume the SplitContainer sits in a TabPage tabPage8. Then you can show it fully by moving it to that tabPage:
moveCtlToTarget(tabPage8, checkedListBox1);
using this function.
void moveCtlToTarget(Control target, Control ctl)
{
// get screen location of the control
Point pt = ctl.PointToScreen(Point.Empty);
// move to the same location relative to the target
ctl.Location = target.PointToClient(pt);
// add to the new controls collection
ctl.Parent = target;
// move all up
ctl.BringToFront();
}
As I don't know how you create and show it, resetting it is up to you. Note that as it now is no longer in the split panel it will not move when you move the splitter..
You may want to do this only the first time and later align it with the ComboBox..
TaW's answer above helped my solve my issue. I modified it slightly for my situation where I moved the parameters into the method as I already had my checkbox control set as a property of the control and got the target by looping up the parents until I got to the top.
private void moveCtlToTarget()
{
Control Target = Parent;
while (Target.Parent != null)
Target = Target.Parent;
Point pt = CheckBox.PointToScreen(Point.Empty);
CheckBox.Location = Target.PointToClient(pt);
CheckBox.Parent = Target;
CheckBox.BringToFront();
}
I have a user control which consists of two controls and four buttons, arranged on a Form: the two controls are on the sides and the buttons in the middle in a vertical row.
When using the control in an app, I place it on a form.
Now, when re-sizing the form horizontally, the two controls just move left or right w/o changing their size.
What I need is that the controls stay anchored to the middle of the form and grow to the sides (sorry about the lack of clarity, I prepared screenshots but the site wouldn't let me attach them).
Is there a way to accomplish this without overriding the Resize event?
Use a TableLayoutPanel as base for your user control.
You need 3 columns and one row. The middle column needs to have a fixed size, and the other 2 you set to 50%. Not to worry, .Net is smart enough to calculate the percent they actually take.
Inside the right and left columns you put your controls and set the Dock property of both to fill. In the middle column you put a panel and set it's Dock property to fill as wall, and In that panel you put the buttons in the middle.
Set your table layout panel Dock to fill as well, and when adding the user control to the form use Dock top, bottom or fill as well.
Erratum:
The above code works most of the time, but it fails for certain Move-Resize sequences. The solution is to respond to the Move and Resize events of the parent form (the consumer of the control), not of the control itself.
One more thing: due to the event firing order (Move first followed by Resize, had to move the working code from Resize() to Move(), which seems counterintuitive but it seems the right way nevertheless.
It seems indeed that it cannot be done in the Designer, but here is the solution using overrides.
It works ok, except for some control flickering which I haven't been able to overcome.
public partial class SB : UserControl
{
//variables to remember sizes and locations
Size parentSize = new Size(0,0);
Point parentLocation = new Point (0,0);
......
// we care only for horizontal changes by dragging the left border;
// all others take care of themselves by Designer code
public void SB_Resize(object sender, EventArgs e)
{
if (this.Parent == null)
return;//we are still in the load process
// get former values
int fcsw = this.parentSize.Width;//former width
int fclx = this.parentLocation.X;//former location
Control control = (Control)sender;//this is our custom control
// get present values
int csw = control.Parent.Size.Width;//present width
int clx = control.Parent.Location.X;//present location
// both parent width and parent location have changed: it means we
// dragged the left border or one of the left corners
if (csw != fcsw && clx != fclx)
{
int delta = clx - fclx;
int lw = (int)this.tableLayoutPanel1.ColumnStyles[0].Width;
int nlw = lw - delta;
if (nlw > 0)
{
this.tableLayoutPanel1.ColumnStyles[0].Width -= delta;
}
}
this.parentSize = control.Parent.Size;//always update it
this.parentLocation = control.Parent.Location;
}
//contrary to documentation, the Resize event is not raised by moving
//the form, so we have to override the Move event too, to update the
//saved location
private void SB_Move(object sender, EventArgs e)
{
if (this.Parent == null)
return;//we are still in the load process
this.parentSize = this.Parent.Size;//always update it
this.parentLocation = this.Parent.Location;
}
}
The above code works most of the time, but it fails for certain Move-Resize sequences. The solution is to respond to the Move and Resize events of the parent form (the consumer of the control), not of the control itself.
One more thing: due to the event firing order (Move first followed by Resize, had to move the working code from Resize() to Move(), which seems counterintuitive but it seems the right way nevertheless.
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 have multiple webbrowser controls inside a listBox (Hierarchy: DataBoundListBox->Multiple ExpandableItems->Each expandable item shows a webbrowser control on expansion.).
The problem is, the webbrowser control captures the drag gesture and it is impossible to scroll the listbox once the webbrowser control is expanded. If the content of the webbrowser is too long, the user is stuck and cannot scroll anymore as the webbrowser fill the entire screen. Now, the webbrowser only needs to respond to simple tap events (buttons).
The webbrowser control captures all touch gestures and events internally, but it seems that there is a border element where touch events can be intercepted before the touch gestures reach the tilehost and is gone forever:
Border border = WebViewer.Descendants<Border>().Last() as Border;
To capture the touch gesture, I added a ManipulationDelta event to the border as it does not allow me to add a Gesture Listener event like DragStarted, DragDelta, DragCompleted.
Now I need to get this ManipulationDelta event data to the scrollviewer inside the listbox where the browser is located.
Is this a valid conceptual approach and is somebody able to help me on how to get this running?
P.S.: I tried it the other way around (sending tap gesture events from a canvas overlay to a javascript inside the webbrowser control page and simulate a tap on the page) but this does not seem to work very well as the touch coordinates and the javascript coordinates have a unknown offset.
Thanks for help!
It turns out that the last method with javascript was actually working quite well, the problem was the double x and y value which was passed to the javascript. After converting it to integer, it detected the underlying html element pixel precise.
If you want to know how you can transfer tap events to a website in a webbrowser element, you need to do the following:
Add "LinqToVisualTree" to your project and your document (f.e.: using LinqToVisualTree;)!
Add this javascript code to your website (besides jquery):
function simulateClick(x, y) {
jQuery(document.elementFromPoint(x, y)).click();}
Add silverlight toolkit (search for GestureService for info) to your solution and add this event to the overlay canvas (the canvas element which is placed over the webbrowser element) in the XAML file (example, note the canvas I put also in there. You would have to replace it with your own canvas or grid or whatever:
<Canvas>
<toolkit:GestureService.GestureListener>
<toolkit:GestureListener Tap="GestureListener_Tap"/>
</toolkit:GestureService.GestureListener>
</Canvas>
Add this to your xaml file codebehind c# (note that I did a little recalculation of the original coordinates from the tap, you may have to change the number 1.3499. This is the static offset factor of the buttons on the emulator.):
private void GestureListener_Tap(object sender, Microsoft.Phone.Controls.GestureEventArgs e)
{
Canvas cv = sender as Canvas;
//!!! You need to change the following search for the webbrowser to your own xaml structure.
// It uses "LinqToVisualTree" to find the element! Add it to your document!
WebBrowser wb = cv.Ancestors<Grid>().First().ElementsBeforeSelf<WebBrowser>().First() as WebBrowser;
var gesture = e.GetPosition(cv);
var gx = gesture.X;
var gy = gesture.Y;
tapBrowser(wb, gx, gy);
}
private static void tapBrowser(WebBrowser wb, double x, double y)
{
GeneralTransform gt = wb.TransformToVisual(Application.Current.RootVisual as UIElement);
Point offset = gt.Transform(new Point(0, 0));
double rx = (x / 1.3499);
double ry = (y / 1.3499);
int ix = (int)rx;
int iy = (int)ry;
wb.InvokeScript("simulateClick", ix.ToString(), iy.ToString());
}
Make sure your canvas has the same size as the webbrowser element!
Thats it. You now have a tap enabled browser but you can keep the rest of the gestures inside the native app and the browser won't catch them! Theoretically, you can redirect more gestures, but you need to have a javascript/jquery function which can "resimulate" the gesture you want to pass on.
I have a window that has a menu, a toolbar at the top, and various other controls. I then have my own control that derives from ContentControl that I want to have use up all remaining space. I can't leave it to its own devices unfortunately, because the control is a Win32 control that's sort of... put inside this WPF control, and I need to use SetWindowPos.
At the moment what I am doing is using ArrangeOverride, getting the MainWindow.Content control and looking at the Height and Width. I then use Size I get in as a parameter and call the SetWindowPos function. It's written in C++/CLI, and here's the code:
Size WebView::ArrangeOverride(Size finalSize)
{
Application::Current->MainWindow->Measure(finalSize);
UIElement^ obj = dynamic_cast<UIElement^>(Application::Current->MainWindow->Content);
double objHei = obj->RenderSize.Height;
double objWid = obj->RenderSize.Width;
SetWindowPos(hWnd, NULL, objWid-finalSize.Width, objHei-finalSize.Height, finalSize.Width, finalSize.Height, NULL);
So in my head I thought this would then set the position of the control to within the remaining available space. And it does sort of work, but it seems as if the MainWindow.Content control is not being measured until afterwards? What am I doing wrong here?
edit: most of the problems seem to be when full-screening the window and then un-fullscreening it.
I have managed to fix this by using the answer to this question here
I simply put my control into a Frame, so it'd be the parent.
Then using the Point I set the window position to that, along with the size that is passed through as a parameter to the ArrangeOverride method.