WPF Startup Screen like Blend - wpf

Our main WPF window ('Main Window') takes considerable time to create (it is using a ribbon and docking panel similar to Visual Studio). Just like in Blend the first action will always be to either choose an existing project to work on or to create a new project. Therefore we would like to show this 'Select Project' window as quickly as possible and create the heavy 'Main Window' in the background (invisible until the user clicks OK on the 'Select Project' window).
What's the best way to architect this startup scenario, preferably using 2 WPF windows (one for the project selection and one for the main control)?
I'm aware of this sample which demonstrates how to load a Win32 window shortly after application startup, but I'd prefer a WPF-only solution.
Another potential solution is to use multiple WPF UI threads, however lifetime management and window-to-window communication are non-trivial.
Questions:
Will I be able to see a faster startup time when implementing the 2 WPF window solution as compared to simply creating the 'Main Window' and not implement the 'Select Project' window (assuming I can find an implementation that takes care of the window lifetimes)?
Should the 'Select Project' window be created on the main application thread and the 'Main Window' on a background thread (or visa versa)?
If I need to use a Win32 startup window for speed, how would I communicate the user's choice of project name to the main WPF application window?
Does anyone know how Blend actually implements their startup window (which I am trying to mimic here)?
Any implementation pointers would be much appreciated!

Best way to architect this scenario
I would:
Create classes to manage the data for your "Select Project" window (such as MRU lists, templates, etc), making sure they are self-contained and don't require interaction with the main thread.
Store information about the "current" project(s) in the Application object or a singleton object.
Create the "Select Project" window to operate completely independently of the main window.
Set your "Select Project" window's "Open" and "Create" buttons to fire the Application.Open command, and bind the CommandParameter on the button to the property that contains the filename to open (or the template to instantiate).
Add a CommandBinding for the Application.Open command at the window level to take the command parameter and set the current project in the Application object.
In your application's startup code, immediately spawn a thread to create the "Select Project" window, then immediately do {a Dispatcher.Invoke at ApplicationIdle priority followed by a very short Thread.Sleep} several times, then proceed, allowing the main window to be created but make it initially invisible.
Set a trigger in the main window to make it visible once the current project has been set for the first time.
Here is how the Open command handler in the "Select Project" window might update the project path in the Application object:
Application.Current.Dispatcher.Invoke(DispatcherPriority.Send, new Action(() =>
{
((App)Application.Current).CurrentProjectPath = e.Parameter;
}
Here is what the application's startup code would look like:
public override void OnStartup(StartupEventArgs e)
{
new Thread(() => { new SelectProject().Show(); }).Start();
for(int i=0; i<10; i++)
{
Dispatcher.Invoke(DispatcherPriority.ApplicationIdle, () => {});
Thread.Sleep(20);
}
_guts = new MainWindowContent(); // probably a UserControl
_guts.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
_guts.Arrange(new Rect(0, 0, guts.DesiredSize.Width, guts.DesiredSize.Height));
_guts.SetBinding(VisibilityProperty,
new Binding { Source = this, Path="StartupFinished",
Converter = BoolToVisibiltyConverter.Instance });
_mainWindow = new MainWindow { Content = guts };
// Note: Title and other properties of MainWindow would be set in the XAML
}
Note the extra code to make sure the guts get measured and arranged ahead of time.
Here is the code to set the current project path:
public string CurrentProjectPath
{
get { return _currentProjectPath; }
set
{
_currentProjectPath = value;
LoadProject();
_startupFinished = true;
OnPropertyChanged("StartupFinished");
}
}
This code assumes the XAML binds _mainWindow's Visibility property
StartupFinished is a DependencyProperty that the main window's Visibility property is bound to, but it could be done in other ways.
Will you be able to see faster startup time this way?
Yes.
Should Select Project be created on main thread & main on other thread, or vice versa
You should create MainWindow on the main thread so it shares the same Dispatcher with the Application object. That means "Select Project" has to be in the second thread.
If you need to use a Win32 startup window for speed
It is probably probably not necessary. Startup of WPF itself is only a fraction of a second. But if it is necessary, the Application.Current.Dispatcher.Invoke technique described above will work.
Do you know how Blend implements its startup window?
No

Do you have a full working axample that I might look at? I tried to work something up from the code snippets enclosed above but wasn't able to get an example to work...

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.

wpf dual monitor application?

I'm very new to WPF. I want to create a dual monitor/projector application. What I want to do is have the "presenters screen" on one monitor and another panel on the secondary monitor, similar to how powerpoint works. I'm struggling to wrap my mind around the panels and XAML. So what I'm after is user clicks on a button on screen1 and information gets updated on screen2.
I'm using this code:
this.Width = System.Windows.SystemParameters.VirtualScreenWidth;
this.Height = System.Windows.SystemParameters.VirtualScreenHeight;
this.Top = 0;
this.Left = 0;
to set the width and height of the screen.
Edit:
The later goal is to cause screen2 to retrieve items out of a database based on the selection on screen1
Question: tutorials, places to go, nudges on how to update monitor2 from a button on monitor1
Short Answer
Create a view model that shared between two views; make one of the views the master (makes the changes) and the other pure presentation. The views are new windows. Initially do not be concerned with the window position (we'll get to that later) just get the shared viewmodel working.
Tip: research the MVVM pattern. Google has a lot of articles on the subject.
Long Answer
After you have researched MVVM and created a few example applications (from scatch or using a framework), below are few additional features you want implement to create the "powerpoint-like" application.
Fullscreen Mode
At the very least you will want the presentation window to be full screen. To achieve this, you set the WindowStyle to None and AllowsTransparency to True.
If you want to make the second window also fullscreen you may need to do some Win32 overrides to get the window to maximize properly without covering the taskbar (post a comment if you want to know how to do this).
Detect Multiple Monitors
Get the size and position of the monitors using Win32 Interop commands. There will be plenty of articles on the Internet that will help you with this (or post another StackoverFlow question).
This would be a neatâ„¢ feature as it will position the two windows correctly (use the secondary screen as the presentation).
That is all that I can think of now, post-back if you any questions on MVVM or any of the additional points above.
1) You should have 2 Windows, the way this looks I'd make monitor2 a child window of monitor1 (after all, it is a child ;)
What i mean by that, is that StartupUri in App.xaml should point to monitor1, and in monitor1's constructor, you should create an instance of monitor2 (which would be a singleton if i were to do it).
2) To maximize a window on the second screen:
Subscribe to the Loaded event of the window (in code-behind), and set
private void Window_Loaded(object sender, RoutedEventArgs e) {
WindowState = WindowState.Maximized;
}
More info (and source): here
3) As to how to make monitor2 react when you set something in monitor1, make monitor1 and monitor2 bind to the same ViewModel, only they show different stuff.
Hope this helps!

WPF: Managing windows (opening, closing, etc...) in MVVM?

I've read about it in a bunch of places. Most of the people are referring to these two links:
How do I handle opening and closing new Windows with MVVM?
http://waf.codeplex.com/
I don't understand either of them. I am a beginner when it comes to MVVM. Some people are mentioning controllers when it comes to window manipulation in MVVM. What are those and how are they implemented? By book, MVVM is consisted of model, viewmodel and view - where do controllers come in?
If someone could provide a sample of the following use case, that would be terrific (for all those people who are just getting started with this, as I am):
Prerequisite: A window is opened.
User clicks a button.
New window is opened and some data is passed to that window, i.e. some string.
New window is closed (or a button is clicked) and some data is passed to the first
window.
Passed data has changed something on the window.
The ViewModel to ViewModel communication is usually handled by an implementation of the Event Aggregator pattern.
MVVM Light uses the Messenger class, Prism has another implementation but basically that is one way to send messages between View Models without coupling.
There are some examples, and Articles describing the usage.
I would suggest taking a look at it.
Regarding the controllers stuff in WPF, I don't know.
Regarding the example:
-I Have a Windows with its WindowsViewModel. This class should have a Command bound to the Button.
-The user clicks the button. Executes the command.
-The Command opens a new Window.
Here you should create the Dialog View Model and somehow you should create the Window. Or create the Window with a ViewModel, but ViewModel shouldn't know much about the View otherwise is not testable.
We use something like this because we have some requirements, but it
could be much much simplier, it happens it's the only example I have at hand:
bool? ShowDialogImpl<TViewModel>(Action<TViewModel> setup) where TViewModel : ViewModel
{
return (bool?)DispatcherHelper.UIDispatcher.Invoke(
(Func<bool?>)(() =>
{
var viewModel = viewModelFactory.Get<TViewModel>();
viewModel.ViewService = this;
setup(viewModel);
var window = new Window
{
Owner = this,
SizeToContent = SizeToContent.WidthAndHeight,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Content = ViewFactory.CreateView<TViewModel>(),
DataContext = viewModel,
WindowStyle = WindowStyle.ToolWindow,
ShowInTaskbar = false
};
window.SetBinding(TitleProperty, new Binding("Title"));
openDialogs.Push(window);
window.Activated += (sender, args) => window.SizeToContent = SizeToContent.Manual;
var result = window.ShowDialog();
openDialogs.Pop();
viewModelFactory.Release(viewModel);
return result;
}));
}
Basically: We create a window and with a view model.
The view model is created from a factory using an container.
The setup Action delegate is the entry point for our data.
Communication:
The first Windows is a Grid, and the second Dialog to edit data of the grid.
Inf the Windows we have:
messenger.Register<EntityUpdated<FooClass>>(this, message => UpdateItem(message.Entity));
And in the Dialog:
messenger.Send(new EntityUpdated<FooClass>(subject));
This way, we know when something was updated in the Edit Dialog in order to refresh the grid.
Hope this help you :)
If you aren't planning on allowing the user to switch back and forth between the windows, while both are open (i.e., the first opens the second, and the second must be closed to return to the first), you could simply set the viewmodel for both windows to the same viewmodel instance, and open the 2nd window as modal, and the strings you are passing back and forth, would simply be properties of the view model, with data bindings to something in the view.

General Question About WPF Control Behavior and using Invoke

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).

WPF, Prism v2, Region in a modal dialog, add region in code behind

I have a composite WPF application. In one of my modules I want to make a wizard and have the steps show up in a region so I can switch between the steps easier. Originally I had this wizard showing up in a tab region and the nested region worked fine. Now I want to make it into a modal dialog box, but after I open it the inner region never gets registared with the region manager; So I can't add my wizard steps.
I was under the impression that the region manager was global, and just adding cal:RegionManager.RegionName="WizardSteps" would do it, but apparently not.
If i pass the region manager to the view I might be able to use it...Does anyone know how to add a region to a ContentControl in code behind?
The problem is that regions search up the visual tree for the RegionManager attached property, and then register themselves with that manager. In the main window that's fine, but in a child window this doesn't happen.
In the Bootstrapper, after the shell is created, the following code is performed.
RegionManager.SetRegionManager(shell, this.Container.Resolve<IRegionManager>());
RegionManager.UpdateRegions();
To get the region manager to work with your child window do the same thing right after you've created the window.
EDIT
To set the region name of a control, you also set the attached property of the RegionManager, like so...
RegionManager.SetRegionName(control, "MyRegion");
However you can do this in xaml aswell. The reason why your regions in a separate window don't work is because the RegionManager needs to be set on the base window, like I showed above.
It is actually quite simple.
In your popup xaml add a regionname as you do in the shell.
Then in the popups constructor, add the following call:
public Popup(IRegionManager regionManager)
{
InitializeComponent();
RegionManager.SetRegionManager(this,regionManager);
}
This works for me in Prism v.1 - shouldn't be too much different in later versions.
I found something thats almost working. I'm sure if i could bind the region's active view to the contentContol's content property then it would work, but I haven't managed that yet.
IRegionManager MyRegionManager = container.Resolve<IRegionManager>();
SingleActiveRegion newRegion = new SingleActiveRegion();
MyRegionManager.Regions.Add("WizardSteps", newRegion);
//Binding
Binding myBinding = new Binding("ActiveViews");
myBinding.Source = newRegion;
view.stepControl.SetBinding(ContentControl.ContentProperty, myBinding);

Resources