General Question About WPF Control Behavior and using Invoke - wpf

I have been putting off activity on SO because my current reputation is "1337". :)
This is a question of "why" and not "how". By default, it seems that WPF does not set focus to the first control in a window when it's opening. In addition, when a textbox gets focus, by default it does not have it's existing text selected. So basically when I open a window, I want focus on the first control of the window, and if that control is a textbox, I want it's existing text (if any) to be selected.
I found some tips online to accomplish each of these behaviors, and combined them. The code below, which I placed in the constructor of my window, is what I came up with:
Loaded += (sender, e) =>
{
MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
var textBox = FocusManager.GetFocusedElement(this) as TextBox;
if (textBox != null)
{
Action select = textBox.SelectAll;
//for some reason this doesn't work without using invoke.
Dispatcher.Invoke(DispatcherPriority.Loaded, select);
}
};
So, my question. Why does the above not work without using Dispatcher.Invoke? Is something built into the behavior of the window (or textbox) cause the selected text to be de-selected post-loading?
Maybe related, maybe not--another example of where I had to use Dispatcher.Invoke to control the behavior of a form:
WPF Focus In Tab Control Content When New Tab is Created

All WPF controls have thread affinity. The Dispatcher manages the thread that each control was created on (typically this is a single thread for every control in the application, but not necessarily). Work is queued on this thread and executed in priority order.
Any UI-manipulating code has to be executed on the same thread as the control was created on - the Dispatcher thread - and so any method has to invoke back to that thread before it can do anything that would affect the UI (such as selecting text in the TextBox).
That said, it's my understanding that the Loaded eventhandler would fire on the Dispatcher thread by default, so I'm not entirely sure why you're seeing this behaviour in your specific example!

I should start by mentioning i had no issues making that work w/ out the dispatcher call in .net 4.0 (it may have been fixed in the framework update)- however, what the previous poster mentioned is accurate and has been the pardigm since the dawn of winforms (.DoActions() and .Invoke()). However, in 3.5 the above did work w/ out dispatcher if you use a method defined in the codebehind as the target call in your lambda:
Loaded += (sender, e) =>
{
this.SelectText();
};
void SelectText()
{
MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
var textBox = FocusManager.GetFocusedElement(this) as TextBox;
if (textBox != null)
{
textBox.SelectAll();
}
}
As to why, I cant really give you the specifics but I've run into similar issues w/ using lambdas to route events on presenters. I want to say its something to do w/ reference or context of the compiled expression- in this case it needs a ref to the containing object in order to know how to delegate the operation (selecting the textbox text on the right thread). I also believe that GC can occasionally clean up resources so deferred execution gets botched (seen it in F#...believed that was the cause of my issue in C# as well).

Related

Starting a WPF application as hidden

I'm writing a WPF application and want it to start as a hidden window. I've created the Window object and set its Visibility property to Visibility.Hidden before calling Application.Run(). Then, I have an event handler for Window.Loaded that also sets the visibility to Visibility.Hidden. Between the call to Application.Run() and the callback to OnWindowLoaded(), there is a black outline of the window that flashes up on the screen and then disappears. It's like the window manager is creating a drop shadow for the window or something and then hides it immediately.
After running my project through instrumentation, I finally found that Window.Show() was somehow getting called. So, I looked into the source code at http://www.dotnetframework.org/Search.aspx:
Application.Run() ends up calling a private method named Application.RunInternal().
RunInternal() checks the visibility of the Window object that was passed in to the Run() method.
If the Visibility property is not Visibility.Visible, a call to Window.Show() is made.
I then looked at the source for System.Windows.Window:
Window.Show() sets the Visibility property on itself (the window) to be Visibility.Visible.
Based on this, I don't see how to force the window to stay hidden. By trying to make the window invisible at startup, I'm causing the Application object to call Window.Show(); I don't understand why the Application object even cares about the window's visibility. It's been a frustrating experience... :-(
I've seen other answers that say to not call Application.Run() and to instead set up your own event dispatchers, but that seems like overkill for something that should be easy. I just want the main window to stay hidden, for no "flicker" to appear at app startup, and for the window to become visible when I'm ready for it to do so (which happens later in my application logic).
Can anyone offer a suggestion?
Did you remove the StartupUri entry in App.xaml? If you do, the App class won't instantiate the window for you and show it. You can do this by yourself by overwriting the App.OnStartup method.
Basically, I build a composition root in this OnStartup method and just create a window at the end of the process:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// Do your custom initialization code here
MainWindow = new MainWindow();
MainWindow.Show();
}
If you really want to omit the whole application build up process (which I wouldn't recommend, as you won't have features like the fallback to Application Resources), you can create a Dispatcher by yourself using this code:
var dispatcher = Dispatcher.CurrentDispatcher;
var synchronizationContext = new DispatcherSynchronizationContext(dispatcher);
SynchronizationContext.SetSynchronizationContext(synchronizationContext);
Dispatcher.Run();
The comment on this Answer finally led me to find the solution to this issue. I needed to display multiple windows on multiple screens at once, and by minimizing the window it gives me the performance I needed. Thanks.

How to animate WPF WebBrowser

Just documenting this as a question an answer so that somebody else doesn't have to suffer the same pain.
I have a WPF application that animates pages, much like swiping on an iPhone. All was good until one of the pages needed to contain a WebBrowser. It did not respond at all well to the animation - when it was supposed to slide in, it wouldn't appear until you focused it, and when it was supposed to slide out, it would go away until you moved the mouse over it. In both cases it just popped in/out rather than animating.
Complicating matters, during the project it was decided to move back to .net 3.5 instead of 4 for unrelated reasons.
So the question is: how can I either (a) get the WebBrowser to properly animate; or (b) how can I hide the WebBrowser at the start of animation and show it again at the end. The animation is currently defined in XAML, and I don't particularly want to change it to code.
And a follow up question is: is there a better way, still using .net 3.5?
UPDATE The WPF WebBrowser is so pathetically lame compared to the WinForms one, I have swapped over, using WindowsFormsHost. Everything below still applies, but the WebBrowser is now not so nobbled (eg. it has a DocumentCompleted event).
I pretty quickly gave up on the option to animate the WebBrowser, as it just got all too hard, and instead decided to hide and re-show it. The start of the animation is triggered by a Command on the View Model. It then finds the page that should be displayed, creates it, and kicks off the animation through an attached property that reflects the transition state.
I created an interface, IRequireTransitionInfo, such that a call to IRequireTransitionInfo.TransitioningFrom gives it a chance to hide itself and IRequireTransitionInfo.TransitioningTo to show again. TransitioningFrom was easy, but TransitioningTo had to be called when the storyboard completed.
Initially, in the constructor of the View Model, it went looking for the Storyboard and hooked into its Completed event, as in the code below:
Storyboard animation = Application.Current.FindResource("SlideAnimation") as Storyboard;
if (animation != null)
{
animation.Completed += new EventHandler(animation_Completed);
}
And then the event handler:
void animation_Completed(object sender, EventArgs e)
{
IRequireTransitionInfo info = currentViewModel as IRequireTransitionInfo;
if (info != null)
info.TransitioningTo(currentView);
}
This seemed to be working pretty well with .net 4. After downgrading to .net 3.5, when the code above to hook up the Completed event ran, I got the following error:
Specified value of type 'System.Windows.Media.Animation.Storyboard' must have IsFrozen set to false to modify.
Despite some of the other answers on SO, you cannot unfreeze a frozen Freezable, and moving the code into the constructor of the MainWindow didn't help.
I went down the path of an attached property on the Storyboard that was bound to a command on the View Model.
<Storyboard x:Key="SlideAnimation" local:EventCommand.StoryboardCompleted="{Binding Source={StaticResource Locator}, Path=Current.MainViewModel.StoryboardCompletedCommand}">
However, this resulted in the following error at runtime:
Cannot convert the value in attribute 'ContentTemplate' to object of type 'System.Windows.DataTemplate'. Cannot freeze this Storyboard timeline tree for use across threads.
It seems you can't do any databinding on a Storyboard (under .net 3.5 at least). Consequently, I solved the problem somewhat inelegantly by having the attached property just define the string name of a resource that was expected to implement an interface supporting notification of storyboard completion.
<Storyboard x:Key="SlideAnimation" local:EventCommand.StoryboardCompletedHandler="Locator">
If anybody knows of a better way to handle this situation under .net 3.5, I would be glad to hear.

WPF Multithreading: Using Dispatcher but the UI still hangs?

I am having a bit of a problem with my UI hanging even though I am using a Dispatcher, and before I get any further I'm wondering if it's the way I'm handling the data retrieval.
Right now I have my main window creating a View and ViewModel. Then, inside of a new Thread (using a Dispatcher) it sets the View.DataContext = ViewModel. A very large ObservableCollection is lazily created when the binding kicks in which is causing the slowdown. However, it seems that some of the other UI items that should be showing up before that slowdow don't actually show up.
private void ButtonClick(Object sender, RoutedEventArgs e)
{
MyView view = new MyView();
MyViewModel vm = new MyViewModel();
TabItem tabItem = new TabItem();
tabItem.Header = "MyView";
tabItem.Content = view;
MyTabCollection.Items.Add(tabItem);
Window working = new Working();
working.Show();
ThreadStart thread = delegate()
{
DispatcherOperation operation = Dispatcher.BeginInvoke(
DispatcherPriority.Normal,
new Action(delegate()
{
view.DataContext = vm;
((FrameworkElement)view.Parent).Focus();
working.Close();
}
)
);
};
Thread theThread = new Thread(thread);
theThread.Start();
}
This basically says it's supposed to create a view and a viewmodel, then add the view to the tab collection I have (which means it should show the new tab at the least). And, it should also show a "Working..." window. After that, a separate thread is supposed to link the ViewModel to the view, focus on that tab and close the working window. The problem is that the first portion doesn't show until everything is done; The tab is not displayed and the working window is not shown until after the new Thread actually finishes (which causes the Working window to show/close right away). I'm guessing it might have to do with the way I retrieve the data, but I'm not sure. Here is the way it does it:
Create View
Create ViewModel
Create TabItem with Content set to the View and add the TabItem to the TabCollection.
Create/Show the "Working..." window
Dispatcher: Set the View.DataContext = ViewModel. This event sets off the DataBindings, which in turn grab the ObservableCollection. Since the OC is created Lazily it is now being created (this is the bottleneck). <-- Is this messing up my separate thread/dispatcher?
Dispatcher: Set Focus to the tab
Close the "Working..." window
All your extra thread is doing is marshalling another call back to the dispatcher thread. Presumably you actually want to do work on the extra thread, or there's no point in creating it.
Ideally your extra thread should be fetching all the data appropriately, leaving you only to actually connect it all up in the dispatcher thread. The important thing is to decide which work you need to do on the UI thread and which work you need to do on the background thread.
Obviously your analysis of the problem is correct. Your view model is lazily loading data when it is needed, and this is not happening until the Dispatcher callback, at which point you are back on the UI thread again and everything is locked up.
In my opinion, the solution is to do the threading in the data access layer:
For collections: You can define special collections that return only items that have already been loaded from the upstream data source, then trigger loading of additional items on a separate thread when someone subscribes to INotifyCollectionChanged. When the additional items arreive, fire INotifyCollectionChanged events. When INotifyCollectionChanged is unsubscribed, cancel any pending load.
For totals and the like: Same idea. As data comes in the total increases and events occur (automatically for DependencyProperty or using INotifyPropertyChanged).
In addition, the data layer should have a parallel property to each collection, sum, or other delay-loaded value indicating whether it is fully loaded or not, allowing the UI to gray out sections that aren't fully loaded. It is also convenient to have an overall "loading" flag somewhere that can be used to gray out UI sections when anything at all is loading (easier to write the UI this way).
Note that sometimes an operation must block until the actual data has been retrieved. I think the easiest thing in this case is to provide methods in the data layer to force data to be loaded synchronously.
Your DispatcherPriority is set to Normal - try setting it to Background as this may improve the rendering

WPF Focus In Tab Control Content When New Tab is Created

I've done a lot of searching on SO and google around this problem, but can't seem to find anything else to try.
I have a MainView (window) that contains a tab control. The tab control binds to an ObservableCollection of ChildViews (user controls). The MainView's ViewModel has a method that allows adding to the collection of ChildViews, which then creates a new tab. When a new tab is created, it becomes the active tab, and this works fine. This method on the MainView is called from another ViewModel (OtherViewModel).
What I am trying to do is set the keyboard focus to the first control on the tab (an AutoCompleteBox from WPFToolkit*) when a new tab is created. I also need to set the focus the same way, but WITHOUT creating a new tab (so set the focus on the currently active tab).
(*Note that there seem to be some focus problems with the AutoCompleteBox--even if it does have focus you need to send a MoveNext() to it to get the cursor in its window. I have worked around this already).
So here's the problem. The focusing works when I don't create a new tab, but it doesn't work when I do create a new tab. Both functions use the same method to set focus, but the create logic first calls the method that creates a new tab and sets it to active. Code that sets the focus (in the ChildView's Codebehind):
IInputElement element1 = Keyboard.Focus(autoCompleteBox);
//plus code to deal with AutoCompleteBox as noted.
In either case, the Keyboard.FocusedElement starts out as the MainView. After a create, calling Keyboard.Focus seems to do nothing (focused element is still the MainView). Calling this without creating a tab correctly sets the keyboard focus to autoCompleteBox.
Any ideas?
Update:
Bender's suggestion half-worked.
So now in both cases, the focused element is correctly the AutoCompleteBox. What I then do is MoveNext(), which sets the focus to a TextBox. I have been assuming that this Textbox is internal to the AutoCompleteBox, as the focus was correctly set on screen when this happened. Now I'm not so sure. This is still the behavior I see when this code gets hit when NOT doing a create. After a create, MoveNext() sets the focus to an element back in my MainView.
The problem must still be along the lines of Bender's answer, where the state of the controls is not the same depending on whether a new tab was created or not. Any other thoughts?
Final Update
As noted, majocha's suggestion worked.
I wanted to update this in case anyone happened upon this same problem with the AutoCompleteBox. It appears that setting focus does not activate it in the UI--you need to do a MoveNext on it to move focus forward once to the control's internal Textbox. This is based on my debugging experience, which may not be 100% scientific. If I have time, I will attempt to create a small repro project and submit it to the WPFToolkit team.
You can try defering the focus change with
Dispatcher.BeginInvoke(MyChangeFocusAction, DispatcherPriority.ContextIdle);
It will get queued after layout and properties updates are done.
I don't think it's best practice, but it works for me.
The control must be visible to be focused, you may try to defer focusing by subscribing to the IsVisibleChanged event, something similar to the following should work:
public static void setFocusLate(this Control control)
{
DependencyPropertyChangedEventHandler handler = null;
handler = delegate
{
control.Focus();
control.IsVisibleChanged -= handler;
};
control.IsVisibleChanged += handler;
}

WPF RichTextBox tab selection eating up system memory!

I have a TabControl in WPF / MVVM that is bound to an ItemsSource on top of an ObservableCollection. Each tab has its own collections and bindings with components such as Images, Richtextboxes, and user input boxes, and everything seems to work well.
However, I have noticed that each time I switch a tab, it uses about 100k of system memory which never gets reclaimed! If I hold ctrl-tab down to cycle through all tabs, I can use up 200 megs of memory within a minute.
Now - I created a blank WPF app with just a tab control, and it also uses memory for each tab switch (albiet MUCH less). Is this just some .NET bug, or a feature? Maybe it stores a breadcrumb trail, maybe its used for debugging (although I compiled in release mode).
How do I reclaim my memory? Or better yet, not lose memory to tab switchings?
This fixed the issue to me:
http://blingcode.blogspot.com/2010/10/memory-leak-with-wpfs-richtextbox.html
Basically add two attributes to each RichTextBox :) :)
IsUndoEnabled="False" UndoLimit="0"
You might want to check if there are any event handlers left behind.
In the case where you registered an event in other control, the garbage collector will not collect the objects that are no longer needed, because the event is still attached so to speak.
So if you registered Loaded somewhere in code behind
public ParentEditor()
{
InitializeComponents();
control.Loaded += OnControlLoaded;
}
or in XAML or the ParentControl
<Control Loaded="OnControlLoaded" />
You basically have two solutions to tackle this problem:
Solution 1 - Remove event handlers when they are no longer needed:
You might want to remove this handler in the unloaded of the parent control like so:
public ParentEditor()
{
InitializeComponents();
control.Loaded += OnControlLoaded;
this.Unloaded += OnParentUnloaded;
}
void OnParentUnloaded(object sender, RoutedEventArgs e)
{
//Remove unloaded event
this.Unloaded -= OnParentUnloaded;
//Remove event from child control
control.Loaded -= OnControlLoaded;
}
You can also use the Unloaded event of the child control of course.. that is up to you..
Solution 2 - Using the WeakEvent pattern:
Another solution for events would be the WeakEvent pattern, which bypasses this problem.
Why Implement the WeakEvent Pattern?
Listening for events can lead to
memory leaks. The typical technique
for listening to an event is to use
the language-specific syntax that
attaches a handler to an event on a
source. For instance, in C#, that
syntax is: source.SomeEvent += new
SomeEventHandler(MyEventHandler).
This technique creates a strong
reference from the event source to the
event listener. Ordinarily, attaching
an event handler for a listener causes
the listener to have an object
lifetime that influenced by the object
lifetime for the source (unless the
event handler is explicitly removed).
But in certain circumstances you might
want the object lifetime of the
listener to be controlled only by
other factors, such as whether it
currently belongs to the visual tree
of the application, and not by the
lifetime of the source. Whenever the
source object lifetime extends beyond
the object lifetime of the listener,
the normal event pattern leads to a
memory leak: the listener is kept
alive longer than intended.
In either case, good luck.. its quite hard to find leaks like the one you are experiencing!
Good advice, but in the end I decided a bindable textblock was more useful and simpler than a richtextbox. I never found out what was causing the leak - but it was definitely in the bindablerichtextbox code (OnInitialized was being called each time a tab switched, which was beyond my control).
The leaks are gone, and my app runs quicker because of the use of the simpler bindable textblock.

Resources