Getting the parent window from the most inner WPF usercontrol - wpf

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) ;
}

Related

Creating and running WPF window in another thread

So I am working on a console project which should display a WPF window at some point where I can see the visual representation of my graph while it continues running through the algorithm.
I've come so far that I can create a Window and open it in another thread, but as soon as I call Dispatcher.Run() the Console gets blocked. My approach so far:
Step 1: Creating a thread which creates the Application and an SynchronizationContext for itself once run
Step 2: Calling the application's method (it's an inherited custom class which provides these methods) to create a window using a transportation class, using the sync-context of the thread. Expectation: It should create an instance of a Window owned by the separate UI thread.
Step 3: Calling the ShowWindow() method on the application using the sync-context of the thread, which shows the Window (does work) and then runs the Dispatcher. Because it's dispatched using the sync-context of the thread, it shouldn't lock the Console thread. It does, though.
Step 4: Using the Window's dispatcher to call the update methods for the graph.
Well, in my thoughts this should perfectly well work, but somehow the Dispatcher locks the Console thread and not the UI thread. Am I overlooking something here?
The custom Application:
internal sealed class CrossThreadApplication : Application
{
private SynchronizationContext _context;
///=================================================================================================
/// <summary> Constructor. </summary>
///
/// <param name="context"> The context. </param>
///=================================================================================================
public CrossThreadApplication(SynchronizationContext context)
{
if (context == null) throw new ArgumentNullException(nameof(context));
_context = context;
}
///=================================================================================================
/// <summary> Shows the window. </summary>
///
/// <exception cref="ArgumentNullException"> Thrown when one or more required arguments are
/// null. </exception>
///
/// <param name="window"> The window. </param>
///=================================================================================================
public void ShowWindow(Window window)
{
if (window == null) throw new ArgumentNullException(nameof(window));
_context.Send(state =>
{
((Window)state).Show();
System.Windows.Threading.Dispatcher.Run();
}, window);
}
///=================================================================================================
/// <summary> Creates a new object. </summary>
///
/// <param name="handle"> The creation handle for a Window. </param>
///
/// <returns> An object. </returns>
///=================================================================================================
public void Create(CreationHandle<Window> handle)
{
_context.Send(state =>
{
CreationHandle<Window> hnd = (CreationHandle<Window>) state;
hnd.Set(Activator.CreateInstance(hnd.CreationType));
}, handle);
}
}
The UIHost class which hosts the UI thread and interacts with the application
static UIHost()
{
HostThread = new Thread(Start);
HostThread.SetApartmentState(ApartmentState.STA);
HostThread.Name = "UI Thread";
CreationEvent = new ManualResetEvent(false);
HostThread.Start(CreationEvent);
}
private static void Start(object o)
{
ManualResetEvent ev = (ManualResetEvent) o;
SynchronizationContext context = new SynchronizationContext();
SynchronizationContext.SetSynchronizationContext(context);
_application = new CrossThreadApplication(context);
ev.Set();
_application.Run();
}
///=================================================================================================
/// <summary> Executes the user interface operation. </summary>
///
/// <param name="windowType"> The window type. </param>
/// <param name="reset"> The reset. </param>
///
/// <returns> An object. </returns>
///=================================================================================================
internal static object RunUI(Type windowType, ManualResetEvent reset)
{
CreationEvent.WaitOne();
CreationHandle<Window> handle = new CreationHandle<Window>();
_application.Create(handle);
_application.ShowWindow(handle.Get());
reset.Set();
return handle.Get();
}
The issue occurs in CrossThreadApplication.ShowWindow() at *.Dispatcher.Run();.
Why is this so? How can I get this working? It has to be possible somehow.

How to handle arbitrary events during a drag and drop operation in WPF?

I want to handle events such as OnMouseMove or MouseWheel during a drag/drop operation.
However, as far as I can tell from this MSDN topic on Drag/Drop, the only events that fire during a drag/drop operation are GiveFeedback, QueryContinueDrag, Drag Enter/Leave/Over, and their Preview* counterparts. Essentially, handling these events allows me to get the mouse position, or see if the user presses Ctrl, Shift, Alt, Esc, or presses or releases one of the mouse buttons.
What I'd like, though, is to handle other events during a drag/drop operation, such as MouseWheel. Specifically what I want to do is to let the user scroll the contents of a window (using the mouse wheel) while dragging something over it. I've tried writing handlers for these other events, both bubbling and tunneling versions, as well as attaching them to various levels of the control hierarchy, but as far as I can tell, none of them are firing.
I'm aware there's a partial solution (described here, for example) where you use DragOver to scroll the contents of a window when the mouse position is near the top or bottom of the window. But that's not what I want to do.
I came across an article that implies it is possible to handle (for example) the OnMouseMove event during a drag operation. I say that because the code in the article is a variant of the approach described above, but it handles OnMouseMove instead of DragOver. However, I tried adapting this approach and still cannot get the OnMouseMove event to fire while dragging. I've added my code below. It's in F#, so I used the F# XAML type provider from FSharpx.
MainWindow.xaml:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="500" Width="900">
<DockPanel Name="panel1">
<StatusBar Name="status1" DockPanel.Dock="Bottom">
<TextBlock Name="statustext1" />
</StatusBar>
</DockPanel>
</Window>
Program.fs:
(*
Added references: PresentationCore, PresentationFramework, System.Xaml, UIAutomationTypes, WindowsBase.
*)
// STAThread, DateTime
open System
// Application
open System.Windows
// TextBox
open System.Windows.Controls
// XAML type provider
open FSharpx
type MainWindow = XAML<"MainWindow.xaml">
type TextBox2 (status : TextBlock) as this =
inherit TextBox () with
member private this.preview_mouse_left_button_down (args : Input.MouseButtonEventArgs) = do
this.CaptureMouse () |> ignore
base.OnPreviewMouseLeftButtonDown args
// Fires while selecting text with the mouse, but not while dragging.
member private this.preview_mouse_move (args : Input.MouseEventArgs) =
if this.IsMouseCaptured then do status.Text <- sprintf "mouse move: %d" <| DateTime.Now.Ticks
do base.OnPreviewMouseMove args
member private this.preview_mouse_left_button_up (args : Input.MouseButtonEventArgs) = do
if this.IsMouseCaptured then do this.ReleaseMouseCapture ()
base.OnPreviewMouseLeftButtonUp args
do
this.PreviewMouseLeftButtonDown.Add this.preview_mouse_left_button_down
this.PreviewMouseMove.Add this.preview_mouse_move
this.PreviewMouseLeftButtonUp.Add this.preview_mouse_left_button_up
let load_window () =
let win = MainWindow ()
let t = new TextBox2 (win.statustext1)
do
t.TextWrapping <- TextWrapping.Wrap
t.AcceptsReturn <- true
t.Height <- Double.NaN
win.panel1.Children.Add t |> ignore
win.Root
[<STAThread>]
(new Application () ).Run(load_window () ) |> ignore
I think you can do this more effectively with PreviewDragEnter, PreviewDragOver, and Drop. I wrote a blog topic on writing your own Drag-and-Drop Textbox that should help get you started. You can add the scrolling functionality from there:
http://xcalibur37.wordpress.com/2011/12/10/wpf-drag-and-drop-textbox-for-windows-explorer-files/
The code:
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
// Initialize UI
InitializeComponent();
// Loaded event
this.Loaded += delegate
{
TextBox1.AllowDrop = true;
TextBox1.PreviewDragEnter += TextBox1PreviewDragEnter;
TextBox1.PreviewDragOver += TextBox1PreviewDragOver;
TextBox1.Drop += TextBox1DragDrop;
};
}
/// <summary>
/// We have to override this to allow drop functionality.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void TextBox1PreviewDragOver(object sender, DragEventArgs e)
{
e.Handled = true;
}
/// <summary>
/// Evaluates the Data and performs the DragDropEffect
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TextBox1PreviewDragEnter(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
e.Effects = DragDropEffects.Copy;
}
else
{
e.Effects = DragDropEffects.None;
}
}
/// <summary>
/// The drop activity on the textbox.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void TextBox1DragDrop(object sender, DragEventArgs e)
{
// Get data object
var dataObject = e.Data as DataObject;
// Check for file list
if (dataObject.ContainsFileDropList())
{
// Clear values
TextBox1.Text = string.Empty;
// Process file names
StringCollection fileNames = dataObject.GetFileDropList();
StringBuilder bd = new StringBuilder();
foreach (var fileName in fileNames)
{
bd.Append(fileName + "\n");
}
// Set text
TextBox1.Text = bd.ToString();
}
}
}
The blog topic gives you a broken-down analysis of each section.
I know this is an old question, but I had to do something similar and my solution also works for this problem. I figure I might as well link it here just in case. It's not the simplest solution but it works perfectly.
I ended up a using hooks via p/invoke to get at the native window messages before they were consumed by the drag and drop operation. By using the WH_MOUSE hook I was able to intercept the WM_MOUSEMOVE message and track the mouse without WPF's Mouse and DragDrop events. This should work for all mouse messages including WM_MOUSEWHEEL.
You can check out my question where I ended up posting my own answer. I included most of the source code with it:
WPF - Track mouse during Drag & Drop while AllowDrop = False

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 re-style WPF application when using lower resolution screen

I am developng a WPF application which will be used on a couple of different PC types. The first type is more of a development machine with dual hi-res monitors; the second is a hand-held touch screen about 800x600.
Using the MVVM pattern, I have developed some different Views which display more or less information depending on the chosen mode. In general, the layout scales well enough.
I also have some dialog boxes but at the moment these are optimised for the hi-res mode. Unfortunately the buttons appear very small on the touch screen and are difficult to hit reliably.
I was wondering how to restyle the dialogs dynamically. I would like to have one application setting which sets the screen type and thereby control styles. So for example, if the screen is a touch screen then all buttons and menus will have a larger default size.
Are Themes the way to go? If so, can someone point me at a good tutorial? (not just using existing themes but also creating them)
In the interest of changing styles, I do the following:
Create a style sheet for each type of theme I want.
Set all style references throughout my project to DynamicResource instead of "StaticResource".
Use manipulation of ResourceDictionaries.
In this example, I have themes for High, Medium, and Low quality. My style sheets paths are:
/Assets/Styles/GlobalStylesLow.xaml
/Assets/Styles/GlobalStylesMed.xaml
/Assets/Styles/GlobalStylesHigh.xaml
The code for the manipulation works like this:
/// <summary>
/// Level of graphics quality enum.
/// </summary>
public enum GraphicsQuality
{
/// <summary>
/// Low
/// </summary>
Low = 0,
/// <summary>
/// Medium
/// </summary>
Medium = 1,
/// <summary>
/// High
/// </summary>
High = 2
}
/// <summary>
/// Sets the Application Resource Dictionaries based on selection.
/// </summary>
/// <param name="quality">The quality.</param>
/// <param name="onRedraw">The on redraw.</param>
public static void UpdateStyles(
Enums.GraphicsQuality quality = Enums.GraphicsQuality.High, Action onRedraw = null)
{
// Reset resource dictionaries
Application.Current.Resources.MergedDictionaries.Clear();
// Base style path
const string basePath = "/<project_base>;component/Assets/Styles";
// Evaluate global quality
switch (quality)
{
case Enums.GraphicsQuality.High:
LoadStyle(basePath + "/GlobalStylesHigh.xaml");
break;
case Enums.GraphicsQuality.Medium:
LoadStyle(basePath + "/GlobalStylesMed.xaml");
break;
case Enums.GraphicsQuality.Low:
LoadStyle(basePath + "/GlobalStylesLow.xaml");
break;
}
// Redraw
if (onRedraw != null)
{
onRedraw();
}
}
/// <summary>
/// Loads a specific style by Uri.
/// </summary>
/// <param name="stylePath">The style path.</param>
private static void LoadStyle(string stylePath)
{
var dic = new ResourceDictionary
{
Source = new Uri(stylePath, UriKind.Relative)
};
Application.Current.Resources.MergedDictionaries.Add(dic);
}
When the condition exists to change your StyleSheet, call UpdateStyles.
Note:
The onRedraw parameter is simply a final action to perform after updating the style. In some cases, you may want to pass
this.InvalidateVisual
from the UI if you are having issues with the UI properly updating.

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.

Resources