1. 程式人生 > >WPF自定義exe程序嵌入控制元件

WPF自定義exe程序嵌入控制元件

       原文地址:http://blog.csdn.net/zhuo_wp/article/details/78830055 轉載請註明出處

       在Windows上開發客戶端程式的時候,有時候我們希望能將其他exe程序的視窗嵌入到我們自己的程式視窗中,從視覺效果上看就像是其他程序的視窗是我們自己的程式視窗的一部分。具體的思路是,呼叫Windows API的SetParent方法,將外部程序主視窗的父級元素設定為承載嵌入程序的程式的一個容器元素的控制代碼。

       在Winforms程式中,因為大部分Winform視窗控制元件都有自己的控制代碼,很容易實現此功能。但是在WPF中會稍微麻煩一點,因為WPF的容器控制元件是沒有自己的獨立的控制代碼的。因此解決思路為先在WPF中嵌入一個Winform的Panel控制元件(Winform中的Panel控制元件有自己獨立的控制代碼),然後再將Panel控制元件的控制代碼設定為外部程式主視窗的父容器的控制代碼。

       為了便於複用,現將相關的功能整理後封裝成了一個WPF自定義控制元件。

       一 程式碼結構
       
       如上圖,整個控制元件的程式碼結構分為三部分:一是控制元件的預設模板AppContainer.xaml,二是控制元件的邏輯控制程式碼AppContainer.cs,包括一些對外介面方法的類AppContainer.cs,三是c#呼叫Win32Api的介面類Win32Api.cs。

       二 預設模板
       AppContainer的預設模板非常的簡單,模板中只有一個WindowsFormsHost控制元件,此控制元件用來存放Winform的Panel控制元件。

<ResourceDictionary  
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
    xmlns:wfi ="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"  
    xmlns:local="clr-namespace:AppContainers">  
    <Style TargetType="{x:Type local:AppContainer}">  
        <Setter Property="Template">  
            <Setter.Value>  
                <ControlTemplate TargetType="{x:Type local:AppContainer}">  
                    <Border Background="{TemplateBinding Background}"  
                            BorderBrush="{TemplateBinding BorderBrush}"  
                            BorderThickness="{TemplateBinding BorderThickness}">  
                        <Grid>  
                            <wfi:WindowsFormsHost x:Name="PART_Host"/>  
                        </Grid>  
                    </Border>  
                </ControlTemplate>  
            </Setter.Value>  
        </Setter>  
    </Style>  
</ResourceDictionary>  

       三 Win32Api
       主要用到了Win32Api的SetParent方法來設定被嵌入程式的父容器控制代碼以及MoveWindow來設定被嵌入程式在容器中的位置。
[DllImport("user32.dll", SetLastError = true)]  
public static extern int SetParent(IntPtr hWndChild, IntPtr hWndNewParent);  
  
  
[DllImport("user32.dll", SetLastError = true)]  
public static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);  


       四 邏輯控制

       1 控制元件的初始化
       如程式碼所以,在複寫控制元件的OnApplyTemplate方法的時候,通過GetTemplateChild方法找到模板中的WindowsFormHost控制元件,當其不為空的時候,例項化Winform的Panel控制元件,並將其新增到WindowsFormHost中去。

public override void OnApplyTemplate()  
{  
    base.OnApplyTemplate();  
  
    _winFormHost = GetTemplateChild("PART_Host") as WindowsFormsHost;  
    if(_winFormHost != null)  
    {  
        _hostPanel = new System.Windows.Forms.Panel();  
        _winFormHost.Child = _hostPanel;  
    }  
} 
       2 外部視窗的嵌入
       外部視窗的嵌入方法有兩個:一個是給定程式路徑,讓控制元件啟動並嵌入程式;一個是當被嵌入程式已經啟動時,直接傳入已經啟動的被嵌程式的程序,然後呼叫嵌入程序的介面嵌入程式。


       啟動並嵌入外部程序的方法:
public bool StartAndEmbedProcess(string processPath)  
{  
    bool isStartAndEmbedSuccess = false;  
    _eventDone.Reset();  
  
    //啟動程序  
    _process = StartApp(processPath);  
    if (_process == null)  
    {  
        return false;  
    }  
  
    //確保可獲取到控制代碼  
    Thread thread = new Thread(new ThreadStart(() =>  
    {  
        while (true)  
        {  
            if (_process.MainWindowHandle != (IntPtr)0)  
            {  
                _eventDone.Set();  
                break;  
            }  
            Thread.Sleep(10);  
        }  
    }));  
    thread.Start();  
  
    //嵌入程序  
    if (_eventDone.WaitOne(10000))  
    {  
        isStartAndEmbedSuccess = EmbedApp(_process);  
        if (!isStartAndEmbedSuccess)  
        {  
            CloseApp(_process);  
        }  
    }  
    return isStartAndEmbedSuccess;  
} 

        直接嵌入外部程序的方法:
public bool EmbedExistProcess(Process process)  
{  
    _process = process;  
    return EmbedApp(process);  
} 

       嵌入程序的方法:
/// <summary>  
/// 將外程序嵌入到當前程式  
/// </summary>  
/// <param name="process"></param>  
private bool EmbedApp(Process process)  
{  
    //是否嵌入成功標誌,用作返回值  
    bool isEmbedSuccess = false;  
    //外程序控制代碼  
    IntPtr processHwnd = process.MainWindowHandle;  
    //容器控制代碼  
    IntPtr panelHwnd = _hostPanel.Handle;  
  
    if (processHwnd != (IntPtr)0 && panelHwnd != (IntPtr)0)  
    {  
        //把本視窗控制代碼與目標視窗控制代碼關聯起來  
        int setTime = 0;  
        while (!isEmbedSuccess && setTime < 10)  
        {  
            isEmbedSuccess = (Win32Api.SetParent(processHwnd, panelHwnd) != 0);  
            Thread.Sleep(100);  
            setTime++;  
        }  
        //設定初始尺寸和位置  
        Win32Api.MoveWindow(_process.MainWindowHandle, 0, 0, (int)ActualWidth, (int)ActualHeight, true);  
    }  
  
    if(isEmbedSuccess)  
    {  
        _embededWindowHandle = _process.MainWindowHandle;  
    }  
  
    return isEmbedSuccess;  
}  

       3 當外部程式放大縮小時,被嵌入程式視窗介面要能跟著改變,所以要複寫OnRender方法,在方法中呼叫MoveWindow方法來設定被嵌程式的初始位置和大小
protected override void OnRender(DrawingContext drawingContext)  
{  
    if (_process != null)  
    {  
        Win32Api.MoveWindow(_process.MainWindowHandle, 0, 0, (int)ActualWidth, (int)ActualHeight, true);  
    }  
    base.OnRender(drawingContext);  
}  
  
protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)  
{  
    this.InvalidateVisual();  
    base.OnRenderSizeChanged(sizeInfo);  
}
       4 當外部程式關閉時,要能同時關閉被嵌入程序。
/// <summary>  
/// 關閉程序  
/// </summary>  
/// <param name="process"></param>  
private void CloseApp(Process process)  
{  
    if (process != null && !process.HasExited)  
    {  
        process.Kill();  
    }  
}  

public void CloseProcess()  
{  
    CloseApp(_process);  
}  


       五 控制元件的應用
<Window x:Class="WpfAppContainerTest.MainWindow"  
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"  
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"  
        xmlns:local="clr-namespace:WpfAppContainerTest"  
        xmlns:container="clr-namespace:AppContainers;assembly=AppContainers"  
        mc:Ignorable="d"  
        Title="MainWindow" Height="350" Width="525">  
    <Grid>  
        <container:AppContainer x:Name="ctnTest" Margin="20"/>  
    </Grid>  
</Window>  
       視窗載入的時候嵌入Windows自帶的畫圖程式。
private void MainWindow_Loaded(object sender, RoutedEventArgs e)  
{  
    if (!_isLoadSuccess)  
    {  
        _isLoadSuccess = ctnTest.StartAndEmbedProcess(@"C:\Windows\system32\mspaint.exe");  
    }  
}  

       效果圖:




原始碼

相關推薦

WPF定義exe程序嵌入控制元件

       原文地址:http://blog.csdn.net/zhuo_wp/article/details/78830055 轉載請註明出處        在Windows上開發客戶端程式的時候,有時候我們希望能將其他exe程序的視窗嵌入到我們自己的程式視窗中,從視

WPF TabIndex預設樣式修改:去掉預設虛線框、定義樣式(Button控制元件為例)

去掉Tab選中預設虛線框 Tab鍵切換時,被選控制元件自動存在虛線框,有時候為了介面美觀,這個虛線框就顯得比較麻煩。廢話不多說,下面是方法。 <Window.Resources> <Style x:Key="MeyFocusVisual" TargetType="{

WPF TabIndex預設樣式修改:去掉預設虛線框、定義樣式(Button控制元件為例)

去掉Tab選中預設虛線框 Tab鍵切換時,被選控制元件自動存在虛線框,有時候為了介面美觀,這個虛線框就顯得比較麻煩。廢話不多說,下面是方法。 <Window.Resources> <Style x:Key="MeyFocusVisual" Tar

windows系統定時重啟定義exe程序

util 文件 shu n) nbsp pat jdk getc exe程序 工作需要, Windows系統定時重啟自定義exe程序. 寫了如下程序, 按照說明(readme.txt)修改批處理文件中的四個參數即可: 1.readme.txt 第一個參數:進程名(不用帶e

c# 定義事件傳參(控制元件資料同步更新)

下面是傳值的視窗程式碼   public partial class Form1 : Form {//自定義一個事件 public event EventHandler SetMsg; public Form1() {

Android定義View--翻書控制元件(一)

0.前言 最近重看了一遍封神演義,感覺QQ閱讀那個翻書的效果挺好的,準備做一個。上週五下午用了兩個小時只寫了一部分功能,以後有時間再完善 1.分析 先看效果圖 這個空間,說簡單也簡單,說難也難,簡單就在於這個效果主要就是依賴canvas的clippath才見到部分canvas,難就難在裁

Silverlight定義資料繫結控制元件應該如何處理IEditableObject和IEditableCollectionView物件

原文: Silverlight自定義資料繫結控制元件應該如何處理IEditableObject和IEditableCollectionView物件 原創文章,如需轉載,請註明出處。   最近在一直研究Silverlight下的資料繫結控制元件,發現有這樣兩個介面IEditableObject

Ext 6.5.3 classic版本,定義實現togglefield開關控制元件

1,在Ext 6.5.3的classic版中沒有提供開關控制元件,參照modern版中 togglefield開關的實現,繼承滑動器(sliderfield),自定義一個開關按鈕。支援value繫結和點選切換狀態以及表單提交。 2,完成後效果如圖:   3, js程式碼如下:

C#GDI+定義繪製曲線圖表控制元件DataChart 簡單實現 C#GDI+ 繪製線段(實線或虛線)、矩形、字串、圓、橢圓

C#GDI+自定義繪製曲線圖表控制元件DataChart 這裡只說明在計算刻度和曲線繪製的時候 只提供思路,只是做了下簡單的計算,不喜勿噴 還望見諒,高手直接飄過吧。這個要做好,還是需要研究研究演算法的,比如刻度隨著控制元件的大小發生改變的時候計算不同的值,根據刻度範圍來計算刻度以及刻度值等,這裡沒有研究,

定義View——聚焦動畫控制元件

我儘量不打錯別字,用詞準確,不造成閱讀障礙。 這個自定義View是很簡單的一個,效果是在拍攝圖片時的對焦動畫,在自定義SurfaceView中使用這個控制元件就好了。 效果如下: 程式碼: public class FocusView extends Vi

Qt定義按鈕彈窗控制元件

  一直以為做一個按鈕彈窗控制元件很簡單,可做起來發現並不是那麼順利,折騰了挺長時間的,先看下效果: 前言   嘗試過兩種方案,方案一:使用QToolButton控制元件,我們可以自定義一個widget,然後setMenu設定為該widget;方案二:點選一個

定義餅狀圖控制元件PieView

本篇參考:GcsSloop的安卓自定義View進階-Canvas之繪製圖形 一、分析如上餅狀圖,所需要得資訊如下 各塊的顏色 所有塊量得總和 各個塊得量佔總量的百分比,並通過百分比得到各個塊所佔扇形的弧度 第一個塊得起始角

定義Tablayout——ViewPager導航控制元件_SimpleViewpagerIndicator

寫這個小控制元件是因為最近負責維護的一款app大改版,設計師給了一個新的ViewPager導航樣式,但找了幾個常用的導航控制元件發現都無法100%實現設計師給的效果,於是就乾脆自己動手豐衣足食了。 控制元件只有一個單獨的java類,程式碼也很簡單,放出來希望能幫到需要的人。

定義下拉重新整理控制元件-仿美團重新整理效果

概述   下拉重新整理是平時專案中最常用的功能,今天要說的就是如何自定義下拉重新整理控制元件。   第三方重新整理控制元件也比較多,例如Android-PullToRefresh,XListView等,但是這些控制元件自定義重新整理頭部不那麼容易擴充套件,它

定義.NET Calarder日曆控制元件(實現部落格園日曆)

這幾天看了下.net 日曆控制元件,去Google了下出來很多,大家都知道.netframework版本中自帶的日曆控制元件只有日期沒有備註的,比如我在1月1日下面要顯示出‘元旦節’這三個字,那要怎麼做呢? 下面先熟悉下Calarder控制元件的常用屬性: Selectio

android 定義鍵盤輸入密碼控制元件

直接上程式碼 public class PwdInputView extends RelativeLayout { private Context mContext; private GridView

Android定義多TAB懸浮控制元件實現蘑菇街首頁效果

說明: 1.viewpager不能左右滑動; 2.轉載時程式碼略有改動(注:修復tab控制元件不能自動滑動的問題); 3.原始碼下載後不能直接執行,經過調整此問題已經修復。 核心程式碼: package com.Imy.Fuli.Fra

一個方法搞定定義tabBarItem中的控制元件位置均分

京東 qq 自定義tabbar控制元件均分 程式碼如下 //ViewController.m - (void)viewDidLoad { [super viewDidLoad

Android 之定義佈局(繼承控制元件

就是繼承已有的控制元件,建立新控制元件,保留繼承的父控制元件的特性,並且還可以引入新特性。下面就以支援橫向滑動刪除列表項的自定義ListView的實現來介紹。 1、建立刪除按鈕佈局delete_btn.xml,這個佈局是在橫向滑動列表項後顯示的: <?xml version="1.0" en

Android定義View之組合控制元件 ---- LED數字時鐘

先上圖 LEDView效果如圖所示。 之前看到一篇部落格使用兩個TextView實現了該效果,於是我想用自定義控制元件的方式實現一個LEDView,使用時即可直接使用該控制元件。 採用組合控制元件的方式,將兩個TextView疊放在一起,再使用digital-7.ttf字