RichTextBox scroll to end not working when not visible - wpf

I have a WPF RichTextBox in my application that sits in Grid. It gets updated every second or two as it displays logs (though sometimes there are no logs for up to a minute depending on the load).
The grid is not always visible, as it sits in its own tab. If the user is on another tab, the logger is not visible.
My problem is that I want the RichTextBox to scroll to the end every time a new paragraph is added. It seemed simple as there is a 'ScrollToEnd' method on the RichTextBox control and so I call that method every time text is added to the control.
The problem is that that method only works if the control is visible, if the user is on another tab, the RichTextBox will not scroll to the end and it looks weird when you click on the tab with the logger and after a couple of seconds or longer it scrolls to the bottom when it should already be at the bottom.
Is there a way around this annoying "feature" of the control? I would like to ALWAYS have the RichTextBox be at the bottom unless the user is manually taking control of the scroll bar.
Thanks!

By default, the TabControl actually doesn't change its contents visibility, it removes them from the view completely when you change tabs and then "re-attachs" them when you navigate back to the previous tab.
That's why the Visibility change doesn't get fired. Instead, you should handle the Loaded event, which should get fired right before the view is re-rendered.

Is there a reason you cannot simply call ScrollToEnd in response to the text box becoming visible? That seems like the simplest approach. Did you try it and run into an issue?
Edit: If you are using a TabControl, each TabItem has an IsSelected property you can bind to from the ItemContainerStyle. You could probably scroll your text box in response to the tab becoming selected.
As a separate note: if you are planning to make a custom control for this, here are some things to consider.
I wrote an auto-scrolling version of a FlowDocumentScrollViewer. (I never needed a RichTextBox specifically, but they display similar content.) I can tell you that there are a lot of things to account for, such as knowing when and when not to auto-scroll based on what the user is currently doing.
For example:
If the user takes over the scrolling themselves via the scrollbar or mousewheel, you don't want the control to fight with them.
If they start selecting text, you don't want to scroll it away from them mid selection.
If they scroll to the bottom, you probably want it to start auto-scrolling again.
Also, determining what the user is doing to begin with can sometimes be a complex process on its own.

Related

ListView item highlighting

I am designing a code viewer using a virtualized ListView control to display code lines.
Now I want to create a highlight effect when the user clicks a link which takes them to a particular line. I want the target line to be highlighted.
The effect will be either an "underline" appearing (and disappearing) or a semi-transparent overlay (like a marker pen) appearing (and disappearing). The actual graphical effect itself is unimportant, that's not the problem.
What is the best approach to achieve this? I'm not quite sure where to start.
Some technical requirements might be that I need to:
find the right events to react to - or use databinding
obtain the absolute bounding rectangle of the virtualized target item (although when brought into view the item should be available)
absolutely position a canvas effect on top, fade in and fade out
...Ideally some state changes in a view model, a piece of XAML is animated via a triggered storyboard to appear (fade in) above the relevant ListViewItem and then animates away again (fade out). Of course before the fade in, the element must already be correctly positioned over the relevant ListViewItem...
I have worked on a search feature for a Listview where every ListViewItem had few textbocks. When the user types something in a search textbox, all the matches in the listview was supposed to get highlighted.
I created Run objects based on the search string (used Regex to find the match) and then set the Background to some color. Also, held the reference of the ListViewItem in the tag of the Run object which helped me to use call ScrollIntoView. Hope this will be of some help in your case.

Disabling content behind an in-page dialog in WPF

I have some simple code for popping up a "dialog"-like thing over part of my application window. The idea is, the user must dismiss the dialog before continuing to work with that part of the page.
This works by hovering a large semi-transparent rectangle over the part of the page that is supposed to be disabled - which does a nice enough job of blocking clicks to the region. You see this sort of thing a lot in WPF and Web apps, I think.
The problem I have is, the user can still reach all those juicy blocked controls by tabbing to them using the keyboard. "No problem", I hear you say, "just set the IsEnabled on the panel to false, thereby blocking keyboard access".
Unfortunately, disabling the controls:
Doesn't look very nice
Tends to have unintended consequences with custom styles and bindings further down the tree
So, is there a better way to disable a part of the page, without setting the "IsEnabled" property, such that it doesn't change the visual appearance of any of the controls?
Thanks,
Mark
Can you put your "dialog" XAML in a popup window? Then, call ShowDialog() on the window to make it a modal window? If you don't want your popup to look like a standard window, you could always syle it to remove borders, etc.
I solved this by subscribing to the PreviewGotKeyboardFocus event, from the parent element in the tree, and then handling the event such that focus never gets passed to the children.
Also, I had to explicitly remove focus from the "disabled" controls as well, of course.

Transparent overlay which does not scroll with its parent

I would like to have a form which has a few controls as transparent overlays over a bitmap. This bitmap is subject to transform matrix (zoom & scroll). I'm trying to achieve a look similar to GoogleMaps where the controls do not move when the background image is panned/zoomed.
I've tried to mimic this in my OnPaint. However, when the window is scrolled only the newly exposed area gets invalidated so my control doesn’t repaint.
I've tried to calculate where the old control was, invalidate that area, and also invalidate the area where it's supposed to have been. When I do this it flickers and you can still see the image as its scrolled.
I tried to put a ButtonControl on my display window. However, it always scrolls with its parent control. I tried to capture the scroll events and then adjust the position of the ButtonControl. This also has a delay update effect so it looks not so good.
Any ideas would be greatly appreciated.
It sounds to me like you need to Invalidate() your control wich handles the OnPaint event.
Unfortunately, you get the flicker because the Auto-scrolling mechanism sets its position, and then you restore it. The result is two messages being sent to the button.
Place your bitmap and scroll logic in a separate control that fill the entire form. That means both your bitmap control and the button are child controls of the form.
Alternatively, draw the button yourself. You will then of course need to do some work on getting it to respond to mouse clicks etc. The ControlPaint class has methods that help you mimic the appearance of Windows controls.

How can I animate show/hide of an item in an ItemsControl, but only animate if the window is already visible?

I have an ItemsControl with a number of elements, each one with its own ViewModel instance. Each item's ViewModel knows whether that item should be visible (currently each ViewModel has a Visibility property that the UI binds to). When my window first opens, some of these items are visible, others are collapsed. Later, some items' visibility might change in response to user interaction. The window sizes to its content, so the window resizes when items are shown or hidden. And the window is initially centered on the screen (which means everything has to be arranged properly right away, so the window knows its initial size and can center itself accordingly).
Now I want to add animations whenever an item is shown or hidden -- but I only want to animate if the item's visibility changes after the window is already shown. So if the window is already open, and the user does something that makes one of the ViewModels want to appear, it animates in; if the user does something to make one of the ViewModels disappear, it animates out. But when the window first opens, I want everything to start out rock-solid -- no lingering animations.
And I want the window to still set its initial size based on its initially-visible content, and I still want it to be initially centered onscreen. (Although actually, in this case, it would be acceptable if it centered itself as if all items were visible, if event ordering made it work out that way.)
I know a fair bit about WPF, but I admit I'm lost when it comes to triggers and storyboards. I haven't really done anything with WPF animations before, and I'm not sure where to begin.
I already tried using Reveal from the Bag of Tricks, but I had several problems with it, the biggest being that it doesn't have the "only use animations after the window is shown" behavior that I want -- my window would appear and the initially-visible elements would still be animating in. It also didn't play well with my layout (it was centering the elements horizontally, instead of stretching them to the ItemsControl's width), and a few other problems that might or might not be fixable.
I'm not too picky about whether I animate by stretching (e.g. by animating a LayoutTransform from SizeX=1 SizeY=0 to SizeX=1 SizeY=1, thus starting with squished text and expanding to normal size) or by just changing the Height (thus starting with only part of the content visible and revealing more as the animation progresses) -- I'm fine with either.
I'm open to writing my own Panel descendant (I've done it before) if that's the best way to solve this, and I can always steal code from Reveal and hack until I get it working -- but it seems like there should already be an easier way to do this, if I just knew what it was. I'm open to learning about triggers and storyboards, or whatever, if someone can point me in the right direction.

How to show some animation while control is rendering?

I have WPF ListBox that shows a lot of data. I need smooth scrolling, so I've set ListBox.ScrollViewer.CanContentScroll to False that disables virtualization. Now when I open the tab where this ListBox is placed, I see nothing for few seconds because ListBox is loading/creating items/rendering. I also have a control that shows some animation that indicates that application is running and user should wait a bit.
How can I show this control while ListBox is not available?
Add a Grid in the location of your list box and place inside it your ListBox and your animation control. This way they are placed in the same location. The animation control should be on the top of the z-order and so displayed. Once the ListBox has finished loading you would then hide the animation control and so the ListBox would show instead. Any time you need to perform another long operation you set the animation control to visible again.
Clean shutdown in Silverlight and WPF applications
Check how the author of this application did it via code maybe it can help you though it is different scenario.

Resources