Hi Matthew,
Thanks for posting in the community.
I think this is the Excel's default behavior( click on the cell from
another application, will firstly activate the application). Also we should
not change the default behavior of Excel which may cause other problem.
Since you want the winform lie in another process, write an Addin will not
help in such case.
As a workaround,
You may consider embedding the workbook in the form itself.
Here is sample, you may take a look.
311765 SAMPLE: Visual C ActiveX Control for Hosting Office Documents in
http://support.microsoft.com/?id=311765
You may also try to host the excel window in winform
There are significant differences in the way the .NET Framework class
library supports managed windows and controls as compared with the Win32
API support for windows, or even the MFC/ATL class libraries.
Notwithstanding, a managed window will ultimately map to a native OS window
and can therefore be manipulated in the same way if necessary.
Technically, this includes windows created by Office applications. It
should be noted that it is generally not a good idea to interfere with the
management of an application's windows from outside the application. The
scope for conflict and unexpected behaviour is significant. From the
version table above, you'll see that the technique described here works for
Office 2003. While it works to a degree with earlier versions of Office,
there are a number of issues that make it impractical to use with earlier
versions.
Also the method has some drawback.
Given this warning, if you absolutely must perform some managed management
on an Office application's windows, this walkthrough will give you a
starting point. We'll build a managed user control to host Excel's main
window, and a managed Windows Forms test application.
Here is the mainly steps for you reference.
Excel User Control
1 First, create a new Windows class library, called ExcelControl. Add
references for the (Office 2003) PIAs for Excel. Add a new user control to
the project, using the filename ExcelControl.cs.
2. Declare some APIs.
[DllImport("user32.dll")]
public static extern int FindWindowEx(
int hwndParent, int hwndChildAfter, string lpszClass,
int missing);
[DllImport("user32.dll")]
static extern int SetParent(
int sheetWindow, int hWndNewParent);
[DllImport("user32.dll")]
static extern bool MoveWindow(
int hWnd, int X, int Y, int nWidth, int nHeight,
bool bRepaint);
3 We'll expose three custom public methods from this control:
public void OpenWorkbook(string fileName){}
public void CloseWorkbook(){}
public void QuitExcel(){}
4 Declare some class fields. We'll need a reference to the Excel
Application, and to an Excel Workbook. Also an integer for the Excel window
handle, and 3 simple integers for window metrics (we'll use these to
determine where to position the Excel window):
private Excel.ApplicationClass xl = null;
private Excel.Workbook book = null;
public static int xlWindow;
private int captionHeight;
private int borderWidth;
private int borderHeight;
5 In the control's constructor, after the call to InitializeComponent,
assign initial values to the 3 simple integers from the system metrics:
captionHeight = SystemInformation.CaptionHeight;
borderWidth = SystemInformation.BorderSize.Width;
borderHeight = SystemInformation.BorderSize.Height;
6 Now for the OpenWorkbook method. First, launch Excel, if we haven't
already:
if (xl == null)
{
xl = new Excel.ApplicationClass();
}
Then, find the Excel main window - this has a registered classname "XlMain":
if (xlWindow == 0)
{
xlWindow = FindWindowEx(0, 0, "XlMain", 0);
}
If we've found the Excel main window, make it a child of this control
window:
if (xlWindow != 0)
{
SetParent(xlWindow, this.Handle.ToInt32());
Then, try to open the requested workbook, make Excel visible, put it under
user control, and activate the current workbook:
try
{
object missing = Missing.Value;
book = xl.Workbooks.Open(fileName,
missing, missing, missing, missing, missing,
missing, missing, missing, missing, missing,
missing, missing, missing, missing);
xl.Visible = true;
xl.UserControl = true;
book.Activate();
Then, move the Excel window, eliminating the caption area and borders.
Note: clipping the window in this way may seem like a hack, but we want to
keep our external manipulation of Excel's windows to a minimum, and this is
a less intrusive approach than any alternative.
MoveWindow(xlWindow,
-borderWidth,
-(captionHeight +borderHeight),
this.Bounds.Width +borderWidth *2,
this.Bounds.Height +captionHeight +borderHeight *2,
true);
Note: the Excel commandbars will work OK, but the worksheet UI won't. Nor
will the scrollbars and sheet tabs. To fix these anomalies, we can simulate
some keyboard input:
SendKeys.Send(" ");
SendKeys.Send("{ESC}");
Finally, set the focus back to the parent form:
this.Parent.Focus();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
7 Implementing the CloseWorkbook method is simply a matter of cleaning up
the automation object for the workbook:
if (book != null)
{
try
{
object missing = Missing.Value;
book.Close(missing, missing, missing);
Marshal.ReleaseComObject(book);
book = null;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
8 Implementing the QuitExcel method is a little more involved. First, we
set the Excel window handle to null. Then, make sure we close any open
workbook before attempting to close Excel itself:
xlWindow = 0;
CloseWorkbook();
Then, if the Excel application reference is non-null, we can invoke Excel's
Quit method:
if (xl != null)
{
try
{
xl.Quit();
There is a race condition here, so we must make sure Excel has finished its
Quit cleanup operations before we attempt to release our final reference.
We can use the same technique as before: once the Excel main window has
been destroyed, we can assume cleanup has finished:
while (0 != FindWindowEx(0, 0, "XlMain", 0))
{
System.Threading.Thread.Sleep(100);
}
Finally, cleanup our object reference and memory:
Marshal.ReleaseComObject(xl);
xl = null;
GC.Collect();
GC.WaitForPendingFinalizers();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
9 Build the control project.
Windows Forms Test Application
10 Create a Windows Forms application, called HostExcelWindow. Put 3
buttons on the form to mirror the screenshot below - for invoking the
control's OpenWorkbook, CloseWorkbook and QutExcel methods. Get Click event
handlers for these 3 buttons. Also add a panel to the form. Also add an
OpenFileDialog to the form.
11 Add the ExcelControl user control assembly to the Toolbox. To do this,
right-click on the Toolbox and select "Customize Toolbox" or "Add/Remove
Items". Select the .NET Framework Components tab, and click the Browse
button. Navigate to the ExcelControl.dll to add it to the list:
12 Then, drag+drop an instance of the ExcelControl from the Toolbox onto
the panel on the form. Set the control's Dock property to Fill, so that it
fills the panel.
13 Declare a couple of string fields in the form class for use in the
OpenFileDialog:
private string ExcelFilter =
"Workbooks (*.xls)|*.xls|"+
"Templates (*.xlt)|*.xlt|"+
"Addins (*.xla)|*.xla|"+
"All files (*.*)|*.*" ;
private string testFileName = "HostExcelWindow.xls";
14 In the form's constructor, after the call to InitializeComponent, work
out the path to the sample test workbook, and setup the OpenFileDialog
properties:
string currentLocation = System.Environment.CurrentDirectory;
string currentPath = currentLocation.Substring(
0, currentLocation.IndexOf(@"bin\Debug"));
openFile.InitialDirectory = currentPath;
openFile.Filter = ExcelFilter;
openFile.RestoreDirectory = true;
openFile.Title = "Open Excel File";
openFile.FilterIndex = 0;
openFile.FileName = Path.Combine(currentPath, testFileName);
15 In the button Click handler for the "Open Book" button, show the
OpenFileDialog, to allow the user to choose a workbook to open. Then open
the workbook using the control's exposed OpenWorkbook method:
if (openFile.ShowDialog() == DialogResult.OK)
{
try
{
excelControl1.OpenWorkbook(openFile.FileName);
cmdOpenBook.Enabled = false;
cmdCloseBook.Enabled = true;
cmdQuitExcel.Enabled = true;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
16 In the Click handler for the "Close Book" button, invoke the control's
CloseWorkbook method:
try
{
excelControl1.CloseWorkbook();
cmdCloseBook.Enabled = false;
cmdOpenBook.Enabled = true;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
17 Add a new method to the form to invoke the control's QuitExcel method.
(We'll call this in two places):
private void QuitExcel()
{
excelControl1.QuitExcel();
cmdQuitExcel.Enabled = false;
cmdOpenBook.Enabled = true;
cmdCloseBook.Enabled = false;
}
18 The first place to call this method is the Click event handler for the
"Quit Excel" button; and the second place is in the Closing event handler
for the form itself (add this event handler using the events list in the
properties pane).
19 Build and test:
20 Note: there are several race conditions in this system. To make the
Excel control robust, we should hook events such as WorkbookBeforeClose,
WorkBookOpen, etc.
If you have any concern on this issue, please post here.
Best regards,
Peter Huang
Microsoft Online Partner Support
Get Secure! -
www.microsoft.com/security
This posting is provided "AS IS" with no warranties, and confers no rights.