wpf scrollviewer scrolltoverticaloffset - wpf

The events scrolltoVerticalOffset or the scrolltoHorizontalOffset do not change the values of the scrollviewer.
Please tell me at which event does the values HorizontalOffset and the VerticalOffset get changed?
I have tried LayoutUpdated() method but it goes in a infinite loop.
Thanks in advance

In general the values of HorizontalOffset and VerticalOffset are not updated except during the LayoutUpdated event after the ScrollContentPresenter (or other IScrollInfo) has updated its value and called InvalidateScrollInfo(). The one exception is that the DependencyProperty for each of these is updated during deferred scrolling (but surprisingly the corresponding CLR property is not updated), but this probably does not apply in your case.
There are no ScrollToHorizontalOffset or ScrollToVerticalOffset events in WPF, but there is both a ScrollViewer method and a RoutedCommand of these names. Both the command version and the method version remember your request and execute it at the next LayoutUpdated event, so if all you want to do is make sure the scrolling happens, just send the command or call the method.
If you want to verify that HorizontalOffset or VerticalOffset has indeed been updated as desired you can simply catch the ScrollChangedEvent, which fires after the values have been updated, like this:
scrollViewer.ScrollChanged += (obj, e) =>
{
// Get offset information from 'e' or from scrollViewer
}
I did not understand what you meant by "I have tried LayoutUpdated() method but it goes in a infinite loop," since you didn't explain what "LayoutUpdated() method" is, but the above information should make the order of events clear and help you on your way to a solution. In any case, all the information you need to make your decision should be available from the ScrollChanged event.

I ran into the same problem, thanks for posting the solution.
The LayoutUpdated() method is called by the framework in an infinite loop, when you use ScrollChanged() instead of LayoutUpdated() it fixes the problem.

Related

OnRender not called after InvalidateVisual()

A custom WPF Control overrides OnRender. The method generates and displays a Path from custom data. The data provider is bound using a Dependency Property. The Dependency Property registers for an event when data changed. This event in turn calls InvalidateVisual().
However, after the InvalidateVisual() the OnRender is not always called.
We use the Prism Framework and the Region functionallity. The Control in question is embedded in such a Region, which is activated and deactivated. However, the Control's property "IsVisible" is true whenever the region is active. But still, when calling InvalidateVisual() the OnRender method is not called...
What could prevent the OnRender method from being called?
I just had this problem, too.
Context
I've got a load of controls based on the DynamicDataDisplay graph components inside a VirtualizingStackPanel (inside a ListBox).
When there are more controls that are visible at once, but not enough for the VirtualizingStackPanel to start re-using them when you scroll then I see this issue with the D3 AxisControl class. For some reason it does a lot of work in it's OnRender method, which it tries to trigger by calling InvalidateVisual when something changes.
In the problem case the problem controls call InvalidateVisual but they never get a call to MeasureOverride, ArrangeOverride or OnRender. Interestingly, most of the controls still work, in one particular problem case I get the last 3 out of a set of 11 failing to work properly. Notably those 3 (and only those 3) receive a call to MeasureOverride immediately before the data binding update that triggers the call to InvalidateVisual.
My Fix
In the end I managed to fix it by adding a call to InvalidateMeasure alongside the call to InvalidateVisual.
It's a horrible solution, but it's not a performance critical part of our application, so I seem to be getting away with it.
If the size of your control is staying the same, you should not be using InvalidateMeasure() or InvalidateVisual() because they trigger an expensive re-layout.
WPF is a retained drawing system. OnRender() might be better called AccumulateDrawingObjects(), because it doesn't actually draw. It accumulates a set of drawing objects which WPF uses to draw your UI whenever it wants. The magic thing is, if you put a DrawingGroup into the DrawingContext during OnRender(), you can actually efficiently update it after OnRender, anytime you like.
See my answer here for more details..
https://stackoverflow.com/a/44426783/519568
I just had this problem, too.
I had a scrollbar for a control which only figured out during OnRender() how much space is really needed to display all content, which could be bigger than the available display space and therefor needed a scrollbar. It could happen that OnRender() called some methods which ultimately changed the value of the scrollbar which was supposed to start OnRender() with InvalidateVisual().
However, OnRender() did not get called again after InvalidateVisual(). I guess the reason is that InvalidateVisual() sets some flags which tells WPF that the control needs to get drawn again, but once OnRender() finishes, that flag gets reset. Here some pseudo code how I expect it to happen:
//someCode:
control.InvalidateVisual()
//code of InvalidateVisual()
control.RedrawFlag = true;
//WPF some time later:
if (control.RedrawFlag){
control.OnRender()
//OnRender code
//do some stuff
//decide control needs to be redrawn
//however, RedrawFlag is alreday true!
//next line is not changing anything
control.RedrawFlag = true;
//WPF finished executing control.OnRender
control.RedrawFlag = false;
}
I didn't further investigate if WPF really works this way, but it would explain why OnRender() does not get called a second time.
Instead of wasting even more time, I changed how to calculate the total width of the control content can be and put this code outside of OnRender().

Event that fires after element is resized

Here is a problem: after loading some visual elements, I need to change something knowing their new sizes. There is MeasureOverride method, but it is called before changing the size. Is there any method that is called after it?
P.S. I know that I can calculate new sizes having old ones, but new sizes aren't calculated simply. It would be much easier to just use such an event (if it exists).
Are you looking for the FrameworkElement.SizeChanged event?
MSDN's description:
Occurs when either the ActualHeight or the ActualWidth properties change value on a FrameworkElement.
EDIT:
This article has a good description of both the SizeChanged and LayoutUpdated events, including an overview of how the layout loop works.

Make WPF TextBox or other focusable controls transparent to certain key presses (i.e. pass-through, not swallow)

I have a ListBox with a custom data template which contains a CheckBox, a TextBlock and a TextBox. Normally when you select an item in a ListBox, the underlying ListBoxItem is actually what has the focus and as such, it responds to the up and down keys. Additionally, if the CheckBox has focus, since it doesn't do anything with the up and down keys itself, it just happily ignores them and they're handled by the underlying ListBoxItem as well. All's good so far.
However, a TextBox has its own processing rules for the up and down keys, namingly moving the caret up or down a line in text, which doesn't apply here because in this case it's a single line (it's a number actually.) As such, if the TextBox has focus, the up and down keys break the navigation of the ListBox's selection, nor do they really help with editing.
Now while I could handle the PreviewKeyDownEvent (which I do below, but for different reasons) and manually process the behaviors depending on the pressed keys, that's a very specific solution and requires the control to have knowledge of its container's behavior.
In a perfect world (and in pseudocode), I'd like to just say MyTextBox.KeysToIgnore(Up, Down) or something similar and have it do just that... ignore those keys as if it wasn't even there. (Again, not swallow, but ignore so they pass through.)
But until then, here's what I've come up with, which seems to work, but just looks so 'hacky' to me...
private void PreviewKeyDownHandler(object sender, KeyEventArgs e) {
switch (e.Key){
case Key.Up:
case Key.Down:
case Key.OtherKeyToIgnore
case Key.AndAnother
e.Handled = true;
FrameworkElement target = VisualTreeHelper.GetParent(
e.Source as DependencyObject) as FrameworkElement;
target.RaiseEvent(
new KeyEventArgs(
e.KeyboardDevice,
PresentationSource.FromVisual(target),
0,
e.Key
){
RoutedEvent=Keyboard.KeyDownEvent
}
);
break;
}
}
This also has the added negative of not sending the PreviewKeyDown event to the target. Now I could work around that and fake it by sending that event first, then looking at the e.Handled before sending the actual KeyDown message, which makes sense, but then I hit another wall with the PreviewKeyUp and KeyUp events since thanks to setting e.Handled above, I never get the real 'key up' events to know when to send the fake ones. Plus I'm pretty sure I'd also be breaking the direction of the PreviewKeyxxx messages since they bubble the opposite direction from the regular non non-preview versions. (Maybe that's handled internally but I don't think so.)
Like I said... hacky, hacky, hacky!
But it does work so there's that. And I can implement this via Attached Behaviors which is why I even went this route. (In the attached behaviors implementation, it's not a case statement but rather a check against a collection of keys that I specify in XAML.) I just don't like the idea of losing all the other behaviors that I want.
Again, I just want to say 'Hey TextBox... when you see the Up or Down keys being pressed, STFU ya b*stard!!' and otherwise make it key-transparent.
Thoughts anyone?
Man... not even one comment, let alone an answer in almost a month! Oh well... guess my 'hacky' solution above is the way to do this so I'm marking this as the answer.
Yeh, TextBox key event handling sure can give you a headache.
The trouble is that it is impossible to determine call order of callbacks, registered via EventManager.RegisterClassEventHandler. I.e. your callback gets called on unhandled event, then event is handled, and thats it...
I found a way to "unhandle" key events by subclassing TextBox and by calling "AddEventHandler(KeyDownEvent, callback, true)" in constructor. Then set e.Handled = false by circumstances. Seems like callback gets called after event is processed by TextBox.
It is very not nice to have instance of delegate per instance of TextBox, rather than to have one delegate instance per class, but i can't see any other way to workaround.

WPF - Why isn't Keyboard.Focus() working?

have a TextBox item (MyTextBox) on a TabItem control. I have code that looks as follows:
MyTextBox.Focus();
Keyboard.Focus(MyTextBox);
When I run this code through the debugger I see the following after the lines are executed:
MyTextBox.IsFocused = true
MyTextBox.IsKeyboardFocused = false
Can anyone tell me why the textbox isn't receiving keyboard focus? It's just a standard TextBox control that is enabled.
When you try to set Focus to an element besides the things enumerated above by our coleague, you must also know that WPF does not allow cross threaded operations.
In some cases this exception is not raised like in the Focus method call case. What I've done to fix this issue is to call all the code that involves Keyboards focus in an action.
This action is ran inside the control dispatcher to make sure that my code is not being executed from another thread than the UI thread (e.g. timer event or an event raised from another thread):
[UIElement].Dispatcher.BeginInvoke(
new Action(
delegate{
/// put your Focus code here
}
)
);
MyTextBox.IsKeyboardFocused is false because you are looking at it under debugger and the keyboard focus is probably in your Visual Studio... Try debugging focus without breakpoints (e.g. Debug.Write or trace brakepoints) to see actual values of MyTextBox.IsKeyboardFocused in runtime.
Also notice that Focus() method returns boolean value that indicates whether focus was successfully set. Does it return False in your case? If yes, I would suggest stepping into Focus() method in order to find out what is wrong.
3 important properties must be true: IsVisible="True", Focusable="True". IsEnabled="True".
To be focusable, Focusable and IsEnabled must both be true.
http://msdn.microsoft.com/en-us/library/system.windows.uielement.focus.aspx
The accepted answer here does not solve the problem of textboxes who dont gain focus, no matter what the debugger tells you. If you have and can write to your textbox, then you have it keyboard-focused.
I found this here solving the problem (and actually gaining focus, not just settings the values so it looks like focus in the debugger), it comes very close to Pavlov's answer but with the "Focus code" : Keyboard.Focus does not work on text box in WPF
This worked for me (had to do UpdateLayout, otherwise Focus() didn't work immediately after changing tab from script)
tabControl.SelectedIndex = 2;
this.UpdateLayout();
txtMyTextBox.Focus();
It's important where your first two lines of code are executed.
If they are in an event handler that relates to the user pressing a key, using the mouse, altering the visibility of a control, or otherwise taking an action that might have an impact on focus, I find manually calling Focus() often doesn't work.
My theory is that internally, WPF operates as follows:
User or code takes action which could have an impact on focus, e.g. a TextBox control becomes enabled inside a focus scope which previously had no focusable control.
WPF notifies various event handlers, including yours which calls Focus().
WPF updates focus based on the state changes in step 1. This overrides whatever you did in step 2.
That is why this answer suggests to call your Focus() in a queued callback which will be executed after step 3.
Side note: you don't need to call both UIElement.Focus and Keyboard.Focus since the first includes the second (at least if you trust the Microsoft docs).
In conclusion, replace your first two lines of code with this:
// using System.Windows.Threading;
Dispatcher.BeginInvoke(DispatcherPriority.Input, MyTextBox.Focus);

silverlight keydown event doesn't fire for arrow keys

I have a canvas inside a scrollview. I attached a keydown event handler to the scrollview. For most keys, the handler gets called.
However, for the arrow keys, the handler does not get called. Instead, the scrollview gets scrolled in the appropriate direction.
I also attached a keyup handler to the scrollview and the keyup does get called for the arrow keys.
Is there any way to get the arrow key down event here?
Just use the KeyUp event. Works like a charm.
I found this silly hack to make it work. Setting the scrollview to not be a tabstop keeps it from eating the key events.. but then I had another textbox on the page that all of a sudden ALWAYS had focus because the scrollview didn't anymore. So I fixed that by letting an invisible textbox get focus.
scrollView.IsTabStop = false;
invisibleTextBox.Foreground = new SolidColorBrush(Colors.Transparent);
invisibleTextBox.Background = new SolidColorBrush(Colors.Transparent);
Canvas.SetZIndex(invisibleTextBox, -1000);
invisibleTextBox.KeyDown += new KeyEventHandler(HandleKeyDown);
invisibleTextBox.KeyUp += new KeyEventHandler(HandleKeyUp);
Edit: I also had to move the text box off the canvas because despite being invisible, its outline still showed up.
2nd Edit: I used a textbox because that was the first thing I found that could capture KeyDown events. However, a UserControl can. So it would probably be better practice to use a UserControl instead of an invisible text box. You can call Focus() on the UserControl if needed.
This is a possible answer - I haven't had a chance to test this. I've had similar trouble in the past though, when a control is consuming the events before you can get at them. There's a few things you may be able to try:
Use the PreviewKeyDown event, I think that's what it's called. It may let you get at the event before it's consumed by the control.
Try mblandfo's suggestion, although if you do this you probably ant to wrap the whole thing up in a user control to hide what you're doing from the rest of your code.
Add a key handler to the Canvas object, you may be able to catch the event there, and "bubble" it up through your own event.
Except for 1) all of these count as hacks, really, but good luck, I hope one of them works for you!
"Use the PreviewKeyDown event, I think that's what it's called. It may let you get at the event before it's consumed by the control."
This works. Just set the event arguments "Handled = true" and the ScrollViewer (or ListBox) wont grab onto the event after you've already handled it. I didn't need to use the IsTabStop property for the ListBox though; that and it didn't seem to do anything anyways.

Resources