I have referred the below forum
https://social.msdn.microsoft.com/Forums/en-US/1885dcfa-af48-47ee-85d7-bb9809e24f38/scrollviewerscrolltoverticaloffset-not-working-in-windows-8?forum=winappswithcsharp
By invoking ScrollViewer.UpdateLayout() method, we can set the position of scrollviewer in vertical. I can see vertical offset value is changed in UI with/without invoking ScrollViewer.UpdateLayout(). But when i try to get the value from ScrollViewer.VerticalOffset which doesn't gets changed.
ScrollViewer.ScrollToVerticalOffset(1905);
// ScrollViewer.UpdateLayout();
Any other way to update this ScrollViewer.VerticalOffset?
Recommended way to set vertical / horizontal offset for a ScrollViewer is to use ChangeView() method. Here's how to use it
scrollViewer.ChangeView(scrollViewer.HorizontalOffset, 500, scrollViewer.ZoomFactor);
You can get the changed VerticalOffset or HorizontalOffset properties in ViewChanged event. E.g.
private void scrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
double verticalOffset = scrollViewer.VerticalOffset;
}
Related
I have a listview which i reload when i click on an item. I want to remember the scroll position so I use the following code:
private double scrollPosition;
public void SaveListPosition()
{
scrollPosition = scrollViewer.VerticalOffset;
}
public void ScrollToSavedPosition()
{
scrollViewer.ChangeView(0, scrollPosition, null, false);
}
Its working fine, but it shows the scrolling. After I change the ChangeView method's disableAnimation parameter to true it doesn't show scrolling as expected, but it completely messes up list positions and doesn't scroll to the element that I clicked.
So questions are:
1) is this a bug in winrt?
2) can I override the ChangeView's animation so it will instantly scroll exactly like supplying true for the disableAnimation parameter?
3) Any other solution?
In the WinRt/WP 8.1 MapControl, how do I differentiate between when the user changed the center of the screen by swiping vs a programmatic change?
The WinRt/WP 8.1 MapControl has a CenterChanged event ( http://msdn.microsoft.com/en-us/library/windows.ui.xaml.controls.maps.mapcontrol.centerchanged.aspx ), but this does not provide any info about what caused the center change.
Is there any other way of knowing whether or not the user changed the map center?
/* To give some more context, my specific scenario is as follows:
Given an app which shows a map, I want to track the gps position of a user.
If a gps position is found, I want to put a dot on the map and center the map to that point.
If a gps position change is found, I want to center the map to that point.
If the user changes the position of the map by touch/swipe, I no longer want to center the map when the gps position changes.
I could hack this by comparing gps position and center, but he gps position latLng is a different type & precision as the Map.Center latLng. I'd prefer a simpler, less hacky solution.
*/
I solved this by setting a bool ignoreNextViewportChanges to true before calling the awaitable TrySetViewAsync and reset it to false after the async action is done.
In the event handler, I immediately break the Routine then ignoreNextViewportChanges still is true.
So in the end, it looks like:
bool ignoreNextViewportChanges;
public void HandleMapCenterChanged() {
Map.CenterChanged += (sender, args) => {
if(ignoreNextViewportChanges)
return;
//if you came here, the user has changed the location
//store this information somewhere and skip SetCenter next time
}
}
public async void SetCenter(BasicGeoposition center) {
ignoreNextViewportChanges = true;
await Map.TrySetViewAsync(new Geopoint(Center));
ignoreNextViewportChanges = false;
}
If you have the case that SetCenter might be called twice in parallel (so that the last call of SetCenter is not yet finished, but SetCenter is called again), you might need to use a counter:
int viewportChangesInProgressCounter;
public void HandleMapCenterChanged() {
Map.CenterChanged += (sender, args) => {
if(viewportChangesInProgressCounter > 0)
return;
//if you came here, the user has changed the location
//store this information somewhere and skip SetCenter next time
}
}
public async void SetCenter(BasicGeoposition center) {
viewportChangesInProgressCounter++;
await Map.TrySetViewAsync(new Geopoint(Center));
viewportChangesInProgressCounter--;
}
I have FlipView (it's virtualized with Mode="Standard") with ScrollViewer, Image (inside ScrollViewer as DataTemplate). I set ImageOpened event handler on Image with such code:
private void Image_ImageOpened(object sender, RoutedEventArgs e)
{
var image = sender as Image;
double width = image.ActualWidth;
double height = image.ActualHeight;
var sv = image.Parent as ScrollViewer;
if (sv != null && width != 0 && height != 0)
{
var properZoom = ZoomUtilities.CalculateZoomFactor(width, height);
sv.MinZoomFactor = 0.3f;
sv.ChangeView(null, null, properZoom);
sv.MinZoomFactor = properZoom;
}
}
properZoom is always correct value. One out of the thousand times when I change item (swipe) or load page with this FlipView application crash with breakpoint on sv.ChangeView(..) and AccessViolationException exception is thrown. Does anyone know what could be the reason of such behaviour? Are there any restriction when I can call ChangeView method?
EDIT:
Forgot to mention - there is also DoubleTapped event handler on ScrollViewer which also calls ChangeView
I had the same AccessViolationException using .ChangeView(..). I noticed the exception was thrown when the ScrollViewer I wanted to manipulate had focus. For example when a user was dragging through the list or controlling the scrollbar with his mouse.
Disabling animations solved my problems. If you want to keep the animations you should look for a way to remove focus on the controls before changing the view. I guess the animated ChangeView tries to change a property that is locked because of the user input.
Hope this helps.
I had the same problem.
Solution to me: call inverse, from image object to parent got error.
Call normal path: objectscroolviewer.changeview, works no error.
Then:
var sv = image.Parent as ScrollViewer;
change to
var sv = ScrollViewerObject;
Get object to variable externally, not by internal reference of parent, and ignores sender parameter.
Just a little background on what I am trying to do. I have a custom skin where I have a stylable text field to display the date. When clicking on the stylable text field, which is binded to the date, a date spinner comes up. Behind the datespinner I draw a sprite which needs to cover the whole screen so I can detect a click and make the datespinner go away.
Now the problem-
Without overriding the get width or get height I haven't been able to fill the whole screen. However when I do this the datespinner breaks because its getting the height from the override. Just wondering if anyone knew a way to override just one component and set all others to their default values. I know this might seem like a noob question and maybe I am missing something obvious, I am mostly new to as3.
Here is some code-
override protected function createChildren():void {
super.createChildren();
if(!img) {
img = new dateButtonBG();
addChild(img);
}
if (!maskSprite) {
maskSprite = new Sprite();
maskSprite.graphics.beginFill(0xFFFFCC, .5);
maskSprite.graphics.drawRect(-((stage.height)/2),-((stage.width)/2), stage.width, stage.height);
maskSprite.graphics.endFill();
}
if(!dateButton) {
dateButton = new StyleableTextField();
todayDate = new Date();
BindingUtils.bindProperty(dateButton, "text", date, "selectedDate");
dateButton.addEventListener(MouseEvent.CLICK, onDateButtonClick);
addChild(dateButton);
}
invalidateDisplayList();
}
protected function removeSpinner( event:Event):void{
maskSprite.removeEventListener(MouseEvent.CLICK, removeSpinner);
removeChild(date);
removeChild(maskSprite);
}
protected function onDateButtonClick (event:Event):void {
addChild(maskSprite);
addChild(date);
maskSprite.addEventListener(MouseEvent.CLICK, removeSpinner);
}
override public function get width () : Number {
return _width;
}
override public function get height () : Number {
return _height;
}
The code is not complete but is just for getting my point across. Thanks for reading and all your help.
EDIT-
I figured out how to do it.Adding some information in case some one has the same problem-
Flex limits the size of your sprite( or any UI component) to the size of the container. If you try to go over the size of the container it just returns the size of the container. In my code-
override public function get width () : Number {
return _width;
}
override public function get height () : Number {
return _height;
}
This is commonly touted as a fix to go over the size of the container. This approach is flawed however because it overrides everything that asks for width and height. In this case it tries to make everything the size of _height and _width. For any skin that has more than 1 component this is a huge problem, either you can try to set sizes for items indivigually, which for me didn't work or find an alternate approach.
Here is what works-
public override function validateDisplayList():void {
super.validateDisplayList();
yourComponent.width = someConstantW;
yourComponent.height = someConstantH;
}
This is not going to be enough however. You might need to move your component outside of the container for this try-
override protected function layoutContents(unscaledWidth:Number, unscaledHeight:Number):void {
super.layoutContents(unscaledWidth, unscaledHeight);
setElementPosition(yourComponent, x, y);
}
Hopefully I saved someone a few hours of work :)
It's not quite clear from the code you have posted, but perhaps you should be overriding the updateDisplayList() method instead of the width and height.
updateDisplayList() is the Flex life cycle method that components implement to size and position their child objects.
updateDisplayList() is called by the Flex framework, and it passes in two values: unscaledWidth and unscaledHeight. Your implmentation of updateDisplayList() should honor those values and size/position the child objects accordingly. You should use other life cycle methods (shown below) to do the actual sizing/positioning.
Here's an example implementation to get you started. It may be off a little, as I don't fully understand the code snippet you provided.
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
super.updateDisplayList(unscaledWidth, unscaledHeight);
// size/position the background (or whatever it is that should occupy the whole screen)
background.setLayoutBoundsPosition(0,0); // unnecessary because 0,0 is default
background.setLayoutBoundsSize(unscaledWidth, unscaledHeight);
// size/position the text in the center
var textWidth:Number = text.getPreferredBoundsWidth;
var textheight:Number = text.getPreferredBoundsHeight
text.setLayoutBoundsPosition( (unscaledWidth - textWidth)/2, (unscaledHeight - textHeight)/2 );
text.setLayoutBoundsSize(textWidth, textHeight); // probably not necessary
}
I've got a subclassed UserControl that is the content for my main window. I added a DepedencyProperty to my usercontrol, of type ResizeMode, and the PropertyChanged callback sets the ResizeMode of the main window to the value correctly. It runs fine. I can set it from the code behind, or from the XAML, and it works correctly.
However, when I set it from XAML, the designer throws an Object reference not set to an instance of an object exception, on the code in the PropertyChanged callback that sets the window's resize.
<classes:MyUserControl ResizeMode="NoResize">
<...>
</classes:MyUserControl>
This is the callback. MainWindow is a reference to the parent window.
private static void OnResizeModeChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
(o as MyUserControl).MainWindow.ResizeMode = (ResizeMode) e.NewValue;
}
public ResizeMode ResizeMode
{
get { return (ResizeMode) GetValue(ResizeModeProperty); }
set { SetValue(ResizeModeProperty, value); }
}
public static readonly DependencyProperty ResizeModeProperty =
DependencyProperty.Register("SizeToFit", typeof(ResizeMode), typeof(MyUserControl),
new UIPropertyMetadata(ResizeMode.CanResize, new PropertyChangedCallback(OnResizeModeChanged)));
I could ignore it, or set it in the code behind, but I don't really understand the reason for this error, and I would prefer to set it in XAML.
Can anyone shed some light?
Do you know exactly where the NullReferenceExceptoin is being thrown? For example, if you try this instead:
var uc = o as MyUserControl;
var mw = uc.MainWindow;
mw.ResizeMode = (ResizeMode)e.NewValue;
... then is the exception raised on the second line or the third?
My feeling is that MainWindow has not been assigned by the time ResizeMode is first given a value, so accessing MainWindow.ResizeMode is causing the error.
If that's the case, it's safe to ignore:
var mw = (o as MyUserControl).MainWindow;
if (mw == null) return;
But you might want to cache the value somewhere, and then assign it to MainWindow.ResizeMode when MainWindow gets assigned later.
OK, I think I found the culprit.
The MainWindow is set by App.Current.MainWindow.
Now from what I've read, the Current.MainWindow doesn't exist in Design time, and then when the OnResizeModeChanged methods fire during designtime, MainWindow.ResizeMode, boom! Exception!
I added this line to my methods:
if ((bool) (DesignerProperties.IsInDesignModeProperty.GetMetadata(typeof(DependencyObject)).DefaultValue)) return;
I encountered another problem where my XAML suddenly couldn't load my usercontrol, due to me setting some properties on MainWindow in the constructor, added this:
if (DesignerProperties.GetIsInDesignMode(this))
return;