How to open a non modal dialog in Prism 8 - wpf

I am using Prism 8 WPF and I can't find a way to open a NON-modal dialog. All the dialogs in Prism seem to be modal.

You can use the Show method of IDialogService for non-modal dialogs.
/// <summary>
/// Shows a non-modal dialog.
/// </summary>
/// <param name="name">The name of the dialog to show.</param>
/// <param name="parameters">The parameters to pass to the dialog.</param>
/// <param name="callback">The action to perform when the dialog is closed.</param>
public void Show(string name, IDialogParameters parameters, Action<IDialogResult> callback)
The alternative for showing a modal dialog is the ShowDialog method. See Dialog Service in the Prism documentation for details how to implement a dialog and use the IDialogService.

Related

Getting the parent window from the most inner WPF usercontrol

I have an WPF User control, let's say UCInner, which contains a WPF Popup. UCInner is used in another WPF user control, let's say UCOuter.
UCOuter is embedded in an ElementHost (ElementHost.Child = UCOuter).
Finally UCOuter is embedded within an Outlook VSTO custom task pane ahd this latter used in a winforms application (Outlook VSTO Add-in).
So from the most inner WPF Control, UCInner, I would like to obtain the parent Window. I have tried some alternatives with no success, I am always getting null or exceptions:
Window w = Window.GetWindow(myPopup);
Window w = Window.GetWindow(UCInner);
I also have tried what explained here and also this one.
UPDATED -
ANOTHER ATTEMPT:
I have tried below piece of code and i can get successfully the window handle, but now from the handle I need to get the Window Object.
dynamic activeWindow = Globals.ThisAddIn.Application.ActiveWindow();
Microsoft.VisualStudio.OLE.Interop.IOleWindow win = activeWindow as Microsoft.VisualStudio.OLE.Interop.IOleWindow;
IntPtr handle;
win.GetWindow(out handle);
So in order to get the Window object I have tried this based on the Window handle:
System.Windows.Interop.HwndSource hwndSource = System.Windows.Interop.HwndSource.FromHwnd(handle);
Window w = hwndSource.RootVisual as Window;
but this does not work, hwndSource is null.
If you need to figure out the right parent window to display your own WPF window, cast Application.ActiveWindow to IOleWindow (Application.ActiveWindow can return either Explorer or Inspector, they both support IOleWindow) and call IOleWindow.GetWindow. Once you have the HWND, create an instance of the WindowInteropHelper class and specify the Outlook window handle as the parent:
if (outlookHwnd != IntPtr.Zero)
{
WindowInteropHelper helper = new WindowInteropHelper(YourDialogWindow);
helper.Owner = outlookHwnd;
YourDialogWindow.ShowInTaskbar = false;
}
First, you need to retrieve the parent window handle, in case of Explorer window in Outlook you can use:
Outlook.Explorer explorer = OutlookApplication.ActiveExplorer();
IOleWindow oleWindow = explorer as IOleWindow;
IntPtr handle = IntPtr.Zero;
oleWindow.GetWindow(out handle);
if (handle != IntPtr.Zero)
{
WindowInteropHelper helper = new WindowInteropHelper(DialogWindow);
helper.Owner = handle;
DialogWindow.ShowInTaskbar = false;
DialogWindow.ShowDialog();
}
where IOleWindow can be defined in the following way:
/// <summary>
/// Implemented and used by containers and objects to obtain window handles
/// and manage context-sensitive help.
/// </summary>
/// <remarks>
/// The IOleWindow interface provides methods that allow an application to obtain
/// the handle to the various windows that participate in in-place activation,
/// and also to enter and exit context-sensitive help mode.
/// </remarks>
[ComImport]
[Guid("00000114-0000-0000-C000-000000000046")]
[InterfaceType (ComInterfaceType.InterfaceIsIUnknown)]
public interface IOleWindow
{
/// <summary>
/// Returns the window handle to one of the windows participating in in-place activation
/// (frame, document, parent, or in-place object window).
/// </summary>
/// <param name="phwnd">Pointer to where to return the window handle.</param>
void GetWindow (out IntPtr phwnd) ;
/// <summary>
/// Determines whether context-sensitive help mode should be entered during an
/// in-place activation session.
/// </summary>
/// <param name="fEnterMode"><c>true</c> if help mode should be entered;
/// <c>false</c> if it should be exited.</param>
void ContextSensitiveHelp ([In, MarshalAs(UnmanagedType.Bool)] bool fEnterMode) ;
}

WPF Best way of displaying a busy indicator when dynamically creating a page

I have a WPF application that runs as an XBAP in a browser. On a few pages all the controls are dynamically created depending on what the user selects. Because of this it can look like the application is not doing anything until all the controls are loaded. I'd like to have some sort of busy indicator displayed before hand to show the user that the controls are loading, it doesn't have to be animated although would be nice if it did. I've looked into the telerik busy indicator but this doesn't work as it's really for getting data for a single control and doesn't show until the controls are loaded which defeats the purpose.
I was thinking of displaying an overlay, or something similar, first, containing a loading logo, then load the page behind this and hide the overlay when the controls have loaded. I was wondering if this was the best way of going about this or if there's a better way?
Note: I haven't tried this in a XBAP browser app, but it works in WPF Apps without any problems!
I use a DispatcherTimer to show an hourglass when necessary, and abstract this code to a static class.
public static class UiServices
{
/// <summary>
/// A value indicating whether the UI is currently busy
/// </summary>
private static bool IsBusy;
/// <summary>
/// Sets the busystate as busy.
/// </summary>
public static void SetBusyState()
{
SetBusyState(true);
}
/// <summary>
/// Sets the busystate to busy or not busy.
/// </summary>
/// <param name="busy">if set to <c>true</c> the application is now busy.</param>
private static void SetBusyState(bool busy)
{
if (busy != IsBusy)
{
IsBusy = busy;
Mouse.OverrideCursor = busy ? Cursors.Wait : null;
if (IsBusy)
{
new DispatcherTimer(TimeSpan.FromSeconds(0), DispatcherPriority.ApplicationIdle, dispatcherTimer_Tick, Application.Current.Dispatcher);
}
}
}
/// <summary>
/// Handles the Tick event of the dispatcherTimer control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
private static void dispatcherTimer_Tick(object sender, EventArgs e)
{
var dispatcherTimer = sender as DispatcherTimer;
if (dispatcherTimer != null)
{
SetBusyState(false);
dispatcherTimer.Stop();
}
}
}
You would use it like this:
void DoSomething()
{
UiServices.SetBusyState();
// Do your thing
}
Hope this helps!

How to remove the maximize button from Caliburn Windows Manager Dialog

I am using the Caliburn Micro Window Manager to display a dialog. I would like to remove the maximize and minimize buttons from the dialog.
If I look at the Windows Manager source (under WPF) I can see a third parameter that lets me pass settings to the dialog. My problem is that I can't pass a third parameter - I get an error. This can be replicated in the hello window manager example project.
Any ideas?
Somehow I seem to be referencing an IWindowManager that only allows 2 parameters to ShowDialog.
This is what I want to do but causes an error:
var loginViewModel = new LoginViewModel();
WindowManager windowManager = new WindowManager();
Dictionary<string, object> settings = new Dictionary<string, object>();
// add settings here to pass to dialog
windowManager.ShowDialog(loginViewModel, null, settings);
This is IWindowManager interface that shows 3 parameters:
public interface IWindowManager
{
/// <summary>
/// Shows a modal dialog for the specified model.
/// </summary>
/// <param name="rootModel">The root model.</param>
/// <param name="context">The context.</param>
/// <param name="settings">The optional dialog settings.</param>
/// <returns>The dialog result.</returns>
bool? ShowDialog(object rootModel, object context = null, IDictionary<string, object> settings = null);
/// <summary>
/// Shows a non-modal window for the specified model.
/// </summary>
/// <param name="rootModel">The root model.</param>
/// <param name="context">The context.</param>
/// <param name="settings">The optional window settings.</param>
void ShowWindow(object rootModel, object context = null, IDictionary<string, object> settings = null);
/// <summary>
/// Shows a popup at the current mouse position.
/// </summary>
/// <param name="rootModel">The root model.</param>
/// <param name="context">The view context.</param>
/// <param name="settings">The optional popup settings.</param>
void ShowPopup(object rootModel, object context = null, IDictionary<string, object> settings = null);
}
dynamic settings = new ExpandoObject();
settings.WindowStyle = WindowStyle.ToolWindow;
settings.ShowInTaskbar = false;
settings.Title = "Test";
windowManager.ShowDialog(loginViewModel, "Modeless", settings);
I don't recall any recent version of Caliburn.Micro that used IWindowManager.ShowDialog with three parameters. Are you maybe referring to this link from the Codeplex site? It appears to be a user request that Rob rejected.
As you probably know, ShowDialog() will cause the IWindowManager to instantiate a view it finds (by naming convention) and attach it to whatever viewmodel you provide it (e.g. LoginViewModel is attached to a "found" view called LoginView).
I'm guessing that LoginView is a UserControl for you, but you can have this view be a regular Window and then set the WindowStyle property (in XAML) to use one of the options that does not have the maximize button. This MSDN article shows the various enumeration options. WindowStyle.ToolWindow is the only one that includes the close button and not the min/max buttons, but because it changes the appearance of the close button, you may prefer WindowStyle.None and just define your own window chrome and buttons.
<Window x:Class="MyNamespace.LoginView"
...
WindowStyle="ToolWindow">
...
</Window>
Instead of having your View as Usercontrol make it a Window and set its ResizeMode="NoResize"
<Window x:Class="Company.Project.Views.MyView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
ResizeMode="NoResize">
Now you will only have red "X" close button and no maximize/minimize buttons.

What goes in the Main method for WPF / MVVM?

Doing my first MVVM WPF application. I expected to see a Main() method in the App.xaml (I'm used to Silverlight) but it isn't there. I added my own Main method. In Silverlight I then created a View linked to a ViewModel and set it as the RootVisual. How do I correctly open my first View Window in WPF?
There are many ways, but I think the WPF equivalent of setting a Silverlight RootVisual is to call Application.Run
App.Run(new MainWindow())
In general, there is no right or wrong way here nor is there an accepted convention. Some people make this call in the Startup event. Other people don't use the event and override OnStartup instead. Still others use StartupUri in App.xaml.
When I created my first (and to date, only) WPF project, to display the appliation's main window (called MainWindow), I overrode the App class's OnStartup method as below:
/// <summary>
/// Raises the System.Windows.Application.Startup event.
/// </summary>
/// <param name="e">The <see cref="System.Windows.StartupEventArgs" /> that contains the event data.</param>
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
// I did some app-specific stuff here...
MainWindow view = new MainWindow();
// Allow all controls in the window to bind to the ViewModel by setting the
// DataContext, which propagates down the element tree.
MainWindowViewModel viewModel = new MainWindowViewModel();
// and I did some more app-specific stuff here...
view.DataContext = viewModel;
view.Show();
}
I believe this was the recommended way for MVVM applications (was a while back though); this code was taken from a .NET 3.5 application.

Pattern for unsaved changes

I'm developing a winforms app with lots of different forms and user controls. Is there a recommended pattern that I could implement that notifies the user that there are unsaved changes on the current form/control when the form/control is exiting and also when the app is closing?
Memento is a way to encapsulate undoable changes.
You can then keep a log of your uncommitted memento instances.
But that's usually way to complex.
State is usually best.
Your application has two "change" states: Saved All Changes, Unsaved Changes.
Each State has a transition rule based on "change" and "save" methods.
The Saved All Changes implementation of "save" does nothing.
The Unsaved Changes implementation of "save" sets the state to "Saved All Changes".
The Saved All Changes implementation "change" sets the state to Unsaved Changes.
The Unsaved Changes implementation of "change" does nothing.
I'm using LLBL Gen pro for the ORM so that has some good entity tracking built into the objects.
I've kind of rolled my own that seems to work pretty well.
I created a new interface that my base User Controls and base Forms implement:
public interface IClosingNotification
{
/// <summary>
/// True if there is a dirty entity (or a dirty entity in the collection) present
/// </summary>
bool DirtyEntityPresent { get; }
/// <summary>
/// Register an entity to be watched for changes
/// </summary>
/// <param name="entity"></param>
void RegisterForClosingNotification(IEntity entity);
/// <summary>
/// Register a collection to be watched for changes
/// </summary>
/// <param name="collection"></param>
void RegisterForClosingNotification(IEntityCollection collection);
/// <summary>
/// Returns true if the form should close without any notification
/// </summary>
/// <returns></returns>
bool ShouldClose();
}
In my base control/form I have a collection of entities that I watch on each form, and I have a CloseForm() method in these classes that I use when a form is closing.
In my forms, whenever I create an object I can then register it for closing notification using:
RegisterForClosingNotification(MyCustomer);
It works well in our scenario.

Resources