Click here to Skip to main content
15,887,946 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more:
I'd want to make an extra panel of options for another application.

I succeeded making my window borderless and placing it constantly on top of the other window, but there's a few problems in this method: 1) it looks out of place 2) it covers some of "host" applications ui.

Better way seems to be making the other window take place in my window as a child. To my surprise, it worked like charm on Win 7! But on windows 10 there seems to be 2 problems: 1) the other window doesn't go borderless 2) parenting doesn't work.

Parenting isn't absolutely necessary as I can just place the other window on my window, but it simply works better when embedded window is a child (having it move perfectly with main window and no z-order trickery needed). The main problem here is those borders... but if setting parent is possible on Win 10 it's a great bonus :)

So I need help either getting this thing fixed or another perspective how to accomplish original goal...

What I have tried:

This example program launches notepad and embeds it on top of childPlaceholder control. On window resize the child window is adjusted accordingly.
Here's the code which works on Windows 7:

WPF XAML:
XML
<Window x:Class="EmbedWindow.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Embedded window example" Height="350" Width="525" Loaded="Window_Loaded" SizeChanged="Window_SizeChanged">
    <DockPanel>
        <Grid Name="myContent" DockPanel.Dock="Right">
            <TextBlock Margin="10">Some extra content here...</TextBlock>
        </Grid>
        <Border Name="childPlaceholder" BorderBrush="DarkGray" BorderThickness="2" >
            <TextBlock Margin="10">Here should be the embedded window...</TextBlock>
        </Border>
    </DockPanel>
</Window>


Code behind (C#):
C#
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Interop;

namespace EmbedWindow
{
	public partial class MainWindow : Window
	{
		IntPtr otherWindow = IntPtr.Zero, thisWindow = IntPtr.Zero;

		public MainWindow()
		{
			InitializeComponent();
		}

		private async void Window_Loaded(object sender, RoutedEventArgs e)
		{
			//This window's handle
			thisWindow = new WindowInteropHelper(this).Handle;

			//Open another program for embedding
			Process proc = new Process();
			proc.StartInfo = new ProcessStartInfo("Notepad.exe");
			proc.Start();

			//Wait until otherWindow has been opened
			await Task.Delay(500);

			otherWindow = proc.MainWindowHandle;

			//This bit removes the border of otherWindow and sets thisWindow as parent
			//I actually don't know what flags should be set, but simply setting the WS_VISIBLE flag seems to make window work, however borderless.
			WinHelper.SetWindowLong(otherWindow, WinHelper.GWL_STYLE, WinHelper.winStyle.WS_VISIBLE);
			WinHelper.SetParent(otherWindow, thisWindow);
			ArrangeWindows();
		}

		private void Window_SizeChanged(object sender, SizeChangedEventArgs e)
		{
			ArrangeWindows();
		}

		private void ArrangeWindows()
		{
			//Moves the otherWindow on top of childPlaceHolder
			Point topLeft = childPlaceholder.TransformToAncestor(this).Transform(new Point(0, 0));
			Point bottomRight = childPlaceholder.TransformToAncestor(this).Transform(new Point(childPlaceholder.ActualWidth, childPlaceholder.ActualHeight));
			WinHelper.MoveWindow(otherWindow, (int)topLeft.X, (int)topLeft.Y, (int)bottomRight.X - (int)topLeft.X, (int)bottomRight.Y - (int)topLeft.Y, true);
		}

		//Some imported functions from user32.dll
		static class WinHelper
		{
			//Sets a window to be a child window of another window
			[DllImport("user32.dll")]
			public static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

			[DllImport("user32.dll")]
			public static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint);

			//Sets window attributes
			[DllImport("user32.dll")]
			public static extern int SetWindowLong(IntPtr hWnd, int nIndex, winStyle dwNewLong);

			public static int GWL_STYLE = -16;

			[Flags]
			public enum winStyle : int
			{
				WS_VISIBLE = 0x10000000,
				WS_CHILD = 0x40000000, //child window
				WS_BORDER = 0x00800000, //window with border
				WS_DLGFRAME = 0x00400000, //window with double border but no title
				WS_CAPTION = WS_BORDER | WS_DLGFRAME //window with a title bar
			}
		}
	}
}
Posted
Updated 17-May-16 3:19am
Comments
Vin Gallo 28-May-20 6:22am    
I have a very good reason for wanting this.
I have a two panel file explorer (almost like File Commander) that also has a third panel for previewing many different file types.

When all three panels are shown, they each share a third of one screen.
I have two screens and I want the view panel to be docked on the second screen.
The view panel is stuck inside the explorer app and it can't be moved, even inside the main app.

I want the space to be used by the two explorer lists.

1 solution

It's hard to say about detail of the difference between different OS versions, so sorry for not giving you the comprehensive answer. I can just give you some general ideas and hope that you can take them into account in practice.

The root problem is that such "embedding" of one application in another one (unless the applications are based on standard console) is not the intended feature of Windows OS. The fact that such tricks are possible at all is some consequence of the Windows legacy. First versions of Windows were not OS products. It was the graphical environment over the MS DOS system, which itself could not be considered as a fully-fledged OS in the sense we usually use; and this is not about the size of the system, but the principles. Back to Windows: even though the applications were the separate processes, they were not the processes with the features of the NT-based Windows. All applications worked in the same address space, and direct modification of any part of memory was always possible. On this background, direct manipulations with windows across processes was pretty much natural. It was possible through Windows API and through sending/posting Windows messages directly to the application. Everything worked as in one single application sharing all resources.

With the advent of Windows NT and virtual memory, processes have been isolated, each working in its separate space. However, direct manipulation of windows across processes survived, which can be explained as an attempt to preserve backward compatibility with applications developed for "non-OS" versions of Windows. This is not a natural feature of modern OS.

So, it's not totally surprising to me that such functionality can eventually be broken. Again, sorry, I don't know any further detail.

All I can do is one practical advice: what you do is against the intention of the Windows API, is a kind of abuse. You simply should not do such things. There are no serious reasons for embedding of one application window in another. The application collaborate with each other when they are designed for such collaboration. You really need to develop more reasonable application architecture. Perhaps I could help more if you shared some of your ultimate goals.

—SA
 
Share this answer
 
v2
Comments
aXu_AP 17-May-16 10:05am    
Normally I would totally agree that programs simply shouldn't do such things, but since this is for myself and not a work I'm going to publish anywhere I feel such abuse is okay ;).
In addition I'm simply interested what can be accomplished...
Sergey Alexandrovich Kryukov 17-May-16 12:57pm    
I understand. I wrote the words about abuse not because I wanted to blame you for your failure, but only to explain why I think it's hardly possible to overcome the problem. I imagine that you tried that our of curiosity, some study on the wedge of the OS purpose or beyond. I wanted to add that the responsibility for this situation is mostly on Microsoft.

But it does not mean you can do it or should do it. And that's why I mentioned that if you explain your ultimate goals, it could help, so you could proceed with something. Essentially, I meant to help you to stop wasting time on something which is not really supposed to work. Why ramming a brick wall for a questionable gain?

So, I would advise you to accept the answer formally and/or provide some more information and ask further questions, if you want.

—SA
aXu_AP 17-May-16 14:30pm    
Well, you've made your point of it being silly, but the question remains if it's possible to parent/child and remove border of another window. Which I think IS a valid question.
I think I just might do it the wrong way, and someone more familiar with winapi could help. In the end they did include those commands in the first place, and I don't see anywhere them being deprecated or obsolete (altough their absence from .NET says something).
Maybe the answer is that Win10 simply doesn't support these functions - but if that's the case I would want to see some sort of statement/smh from Microsoft about the matter (I'm however not sure if such a comprehensive changelog exists)...
Sergey Alexandrovich Kryukov 17-May-16 14:49pm    
I understand.

Sorry, I don't know; and I don't have Windows 10 at this moment to check up. I started my post by admitting that I don't know the detail. I just advised you not to waste time of it, but you decide, of course. Maybe someone else can give you more help, but would not count on that, because not many would do such weird things.
Also, this is a kind of incompatibility which Microsoft tends to keep unnoticed. Some aspect of Microsoft documentations are notoriously poor, especially when it comes to admitting some bugs/inconsistencies. I think you are right: you cannot be sure that such changelog exists. Moreover, as this is a very unusual functionality, Microsoft people may be even unaware of the problem. I faced UI incompatibility problem in much more legitimate use of the API, and those problem were not documented anywhere.

—SA
aXu_AP 17-May-16 14:58pm    
Thanks for your input on the matter. I hope there's someone who has encountered the same problem - if not I guess I'm off to my original way of doing things...

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900