728x90
반응형
728x170
■ WPF는 응용 프로그램을 만들기 위한 풍부한 환경을 제공한다. 그러나 Win32 코드에 상당한 투자가 있는 경우 WPF 응용 프로그램에서 해당 코드 중 일부를 완전히 다시 작성하는 것보다 재사용하는 것이 더 효과적일 수 있다. WPF는 WPF 페이지에서 Win32 창을 호스팅하기 위한 간단한 메커니즘을 제공한다.
▶ ControlHost.cs
using System;
using System.Runtime.InteropServices;
using System.Windows.Interop;
namespace TestProject
{
/// <summary>
/// 컨트롤 호스트
/// </summary>
public class ControlHost : HwndHost
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Import
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Private
#region 윈도우 생성하기 (확장) - CreateWindowEx(extendedStyle, className, windowName, style, x, y, width, height,
parentWindowHandle, menuHandle, instanceHandle, parameter)
/// <summary>
/// 윈도우 생성하기 (확장)
/// </summary>
/// <param name="extendedStyle">확장 스타일</param>
/// <param name="className">클래스명</param>
/// <param name="windowName">윈도우명</param>
/// <param name="style">스타일</param>
/// <param name="x">X</param>
/// <param name="y">Y</param>
/// <param name="width">너비</param>
/// <param name="height">높이</param>
/// <param name="parentWindowHandle">부모 윈도우 핸들</param>
/// <param name="menuHandle">메뉴 핸들</param>
/// <param name="instanceHandle">인스턴스 핸들</param>
/// <param name="parameter">매개 변수</param>
/// <returns>윈도우 핸들</returns>
[DllImport("user32", EntryPoint = "CreateWindowEx", CharSet = CharSet.Unicode)]
private static extern IntPtr CreateWindowEx
(
int extendedStyle,
string className,
string windowName,
int style,
int x,
int y,
int width,
int height,
IntPtr parentWindowHandle,
IntPtr menuHandle,
IntPtr instanceHandle,
[MarshalAs(UnmanagedType.AsAny)] object parameter
);
#endregion
#region 윈도우 제거하기 - DestroyWindow(windowHandle)
/// <summary>
/// 윈도우 제거하기
/// </summary>
/// <param name="windowHandle">윈도우 핸들</param>
/// <returns>처리 결과</returns>
[DllImport("user32", EntryPoint = "DestroyWindow", CharSet = CharSet.Unicode)]
private static extern bool DestroyWindow(IntPtr windowHandle);
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// WS_CHILD
/// </summary>
private const int WS_CHILD = 0x40000000;
/// <summary>
/// WS_VISIBLE
/// </summary>
private const int WS_VISIBLE = 0x10000000;
/// <summary>
/// LBS_NOTIFY
/// </summary>
private const int LBS_NOTIFY = 0x00000001;
/// <summary>
/// HOST_ID
/// </summary>
private const int HOST_ID = 0x00000002;
/// <summary>
/// LISTBOX_ID
/// </summary>
private const int LISTBOX_ID = 0x00000001;
/// <summary>
/// WS_VSCROLL
/// </summary>
private const int WS_VSCROLL = 0x00200000;
/// <summary>
/// WS_BORDER
/// </summary>
private const int WS_BORDER = 0x00800000;
/// <summary>
/// 호스트 너비
/// </summary>
private readonly int hostWidth;
/// <summary>
/// 호스트 높이
/// </summary>
private readonly int hostHeight;
/// <summary>
/// 호스트 윈도우 핸들
/// </summary>
private IntPtr hostWindowHandle;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Property
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 리스트 박스 윈도우 핸들 - ListBoxWindowHandle
/// <summary>
/// 리스트 박스 윈도우 핸들
/// </summary>
public IntPtr ListBoxWindowHandle { get; private set; }
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - ControlHost(width, height)
/// <summary>
/// 생성자
/// </summary>
/// <param name="width">너비</param>
/// <param name="height">높이</param>
public ControlHost(double width, double height)
{
this.hostWidth = (int)width;
this.hostHeight = (int)height;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Protected
#region 윈도우 코어 만들기 - BuildWindowCore(parentHandleRef)
/// <summary>
/// 윈도우 코어 만들기
/// </summary>
/// <param name="parentHandleRef">부모 핸들 참조</param>
/// <returns>핸들 참조</returns>
protected override HandleRef BuildWindowCore(HandleRef parentHandleRef)
{
ListBoxWindowHandle = IntPtr.Zero;
this.hostWindowHandle = IntPtr.Zero;
this.hostWindowHandle = CreateWindowEx
(
0,
"static",
"",
WS_CHILD | WS_VISIBLE,
0,
0,
this.hostWidth,
this.hostHeight,
parentHandleRef.Handle,
(IntPtr)HOST_ID,
IntPtr.Zero,
0
);
ListBoxWindowHandle = CreateWindowEx
(
0,
"listbox",
"",
WS_CHILD | WS_VISIBLE | LBS_NOTIFY | WS_VSCROLL | WS_BORDER,
0,
0,
this.hostWidth,
this.hostHeight,
this.hostWindowHandle,
(IntPtr)LISTBOX_ID,
IntPtr.Zero,
0
);
return new HandleRef(this, this.hostWindowHandle);
}
#endregion
#region 윈도우 프로시저 처리하기 - WndProc(windowHandle, message, wordParameter, longParameter, handled)
/// <summary>
/// 윈도우 프로시저 처리하기
/// </summary>
/// <param name="windowHandle">윈도우 핸들</param>
/// <param name="message">메시지</param>
/// <param name="wordParameter">WORD 매개 변수</param>
/// <param name="longParameter">LONG 매개 변수</param>
/// <param name="handled">처리 여부</param>
/// <returns>핸들</returns>
protected override IntPtr WndProc(IntPtr windowHandle, int message, IntPtr wordParameter, IntPtr longParameter, ref bool handled)
{
handled = false;
return IntPtr.Zero;
}
#endregion
#region 윈도우 코어 제거하기 - DestroyWindowCore(windowHandleRef)
/// <summary>
/// 윈도우 코어 제거하기
/// </summary>
/// <param name="windowHandleRef">윈도우 핸들 참조</param>
protected override void DestroyWindowCore(HandleRef windowHandleRef)
{
DestroyWindow(windowHandleRef.Handle);
}
#endregion
}
}
▶ MainWindow.xaml
<Window x:Class="TestProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="800"
Height="600"
Title="TestProject"
FontFamily="나눔고딕코딩"
FontSize="16">
<DockPanel
Margin="10"
Background="LightGreen">
<Border Name="hostBorder" DockPanel.Dock="Right"
HorizontalAlignment="Right"
VerticalAlignment="Top"
Margin="10"
Width="200"
Height="250"
BorderThickness="3"
BorderBrush="LightGray" />
<StackPanel>
<Label
HorizontalAlignment="Center"
Margin="0 10 0 0"
FontWeight="Bold">
Control the Control
</Label>
<TextBlock Margin="10 10 10 10">
Selected Text : <TextBlock Name="selectedTextBlock" />
</TextBlock>
<TextBlock Margin="10 10 10 10">
Number of Items : <TextBlock Name="itemCountTextBlock" />
</TextBlock>
<Line
HorizontalAlignment="Center"
Margin="0 20 0 0"
X1="0"
X2="500"
StrokeThickness="2"
Stroke="LightYellow" />
<Label
HorizontalAlignment="Center"
Margin="10 10 10 10">
Append an Item to the List
</Label>
<StackPanel Orientation="Horizontal">
<Label
HorizontalAlignment="Left"
Margin="10 10 10 10">
Item Text
</Label>
<TextBox Name="appendTextBox"
HorizontalAlignment="Left"
Margin="10 10 10 10"
Width="200"
VerticalContentAlignment="Center" />
</StackPanel>
<Button Name="appendButton"
HorizontalAlignment="Left"
Margin="10 10 10 10"
Width="100"
Height="30">
Append
</Button>
<Line
HorizontalAlignment="Center"
Margin="0 20 0 0"
X1="0"
X2="500"
StrokeThickness="2"
Stroke="LightYellow" />
<Label
HorizontalAlignment="Center"
Margin="10 10 10 10">
Delete the Selected Item
</Label>
<Button Name="deleteButton"
HorizontalAlignment="Left"
Margin="10 10 10 10"
Width="100"
Height="30">
Delete
</Button>
</StackPanel>
</DockPanel>
</Window>
▶ MainWindow.xaml.cs
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
namespace TestProject
{
/// <summary>
/// 메인 윈도우
/// </summary>
public partial class MainWindow : Window
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Import
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Private
#region 메시지 보내기 - SendMessage(windowHandle, message, wordParameter, longParameter)
/// <summary>
/// 메시지 보내기
/// </summary>
/// <param name="windowHandle">윈도우 핸들</param>
/// <param name="message">메시지</param>
/// <param name="wordParameter">WORD 매개 변수</param>
/// <param name="longParameter">LONG 매개 변수</param>
/// <returns>처리 결과</returns>
[DllImport("user32", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
private static extern int SendMessage(IntPtr windowHandle, int message, IntPtr wordParameter, IntPtr longParameter);
#endregion
#region 메시지 보내기 - SendMessage(windowHandle, message, wordParameter, longParameter)
/// <summary>
/// 메시지 보내기
/// </summary>
/// <param name="windowHandle">윈도우 핸들</param>
/// <param name="message">메시지</param>
/// <param name="wordParameter">WORD 매개 변수</param>
/// <param name="longParameter">LONG 매개 변수</param>
/// <returns>처리 결과</returns>
[DllImport("user32", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
private static extern int SendMessage(IntPtr windowHandle, int message, int wordParameter, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder longParameter);
#endregion
#region 메시지 보내기 - SendMessage(windowHandle, message, wordParameter, longParameter)
/// <summary>
/// 메시지 보내기
/// </summary>
/// <param name="windowHandle">윈도우 핸들</param>
/// <param name="message">메시지</param>
/// <param name="wordParameter">WORD 매개 변수</param>
/// <param name="longParameter">LONG 매개 변수</param>
/// <returns>처리 결과</returns>
[DllImport("user32", EntryPoint = "SendMessage", CharSet = CharSet.Unicode)]
private static extern IntPtr SendMessage(IntPtr windowHandle, int message, IntPtr wordParameter, string longParameter);
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// WM_COMMAND
/// </summary>
private const int WM_COMMAND = 0x00000111;
/// <summary>
/// LB_SEL_CHANGE
/// </summary>
private const int LB_SEL_CHANGE = 0x00000001;
/// <summary>
/// LB_GET_CUR_SEL
/// </summary>
private const int LB_GET_CUR_SEL = 0x00000188;
/// <summary>
/// LB_GET_TEXT_LEN
/// </summary>
private const int LB_GET_TEXT_LEN = 0x0000018A;
/// <summary>
/// LB_ADD_STRING
/// </summary>
private const int LB_ADD_STRING = 0x00000180;
/// <summary>
/// LB_GET_TEXT
/// </summary>
private const int LB_GET_TEXT = 0x00000189;
/// <summary>
/// LB_DELETE_STRING
/// </summary>
private const int LB_DELETE_STRING = 0x00000182;
/// <summary>
/// LB_GET_COUNT
/// </summary>
private const int LB_GET_COUNT = 0x0000018B;
/// <summary>
/// 리스트 박스 핸들
/// </summary>
private IntPtr listBoxHandle;
/// <summary>
/// 항목 수
/// </summary>
private int itemCount;
/// <summary>
/// 컨트롤 호스트
/// </summary>
private ControlHost controlHost;
/// <summary>
/// 선택 항목 인덱스
/// </summary>
private int selectedItemIndex;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - MainWindow()
/// <summary>
/// 생성자
/// </summary>
public MainWindow()
{
InitializeComponent();
Loaded += Window_Loaded;
this.appendButton.Click += appendButton_Click;
this.deleteButton.Click += deleteButton_Click;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Private
#region 윈도우 로드시 처리하기 - Window_Loaded(sender, e)
/// <summary>
/// 윈도우 로드시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void Window_Loaded(object sender, EventArgs e)
{
this.controlHost = new ControlHost(this.hostBorder.ActualWidth, this.hostBorder.ActualHeight);
this.hostBorder.Child = this.controlHost;
this.controlHost.MessageHook += controlHost_MessageHook;
this.listBoxHandle = this.controlHost.ListBoxWindowHandle;
for(int i = 0; i < 15; i++)
{
string itemText = "Item" + i;
SendMessage(this.listBoxHandle, LB_ADD_STRING, IntPtr.Zero, itemText);
}
this.itemCount = SendMessage(this.listBoxHandle, LB_GET_COUNT, IntPtr.Zero, IntPtr.Zero);
this.itemCountTextBlock.Text = this.itemCount.ToString();
}
#endregion
#region 컨트롤 호스트 메시지 후킹 처리하기 - controlHost_MessageHook(windowHandle, message, wordParameter, longParameter, handled)
/// <summary>
/// 컨트롤 호스트 메시지 후킹 처리하기
/// </summary>
/// <param name="windowHandle">윈도우 핸들</param>
/// <param name="message">메시지</param>
/// <param name="wordParameter">WORD 매개 변수</param>
/// <param name="longParameter">LONG 매개 변수</param>
/// <param name="handled">처리 여부</param>
/// <returns>핸들</returns>
private IntPtr controlHost_MessageHook(IntPtr windowHandle, int message, IntPtr wordParameter, IntPtr longParameter, ref bool handled)
{
int textLength;
handled = false;
if(message == WM_COMMAND)
{
switch((uint) wordParameter.ToInt32() >> 16 & 0xffff)
{
case LB_SEL_CHANGE :
this.selectedItemIndex = SendMessage(this.controlHost.ListBoxWindowHandle, LB_GET_CUR_SEL, IntPtr.Zero, IntPtr.Zero);
textLength = SendMessage(this.controlHost.ListBoxWindowHandle, LB_GET_TEXT_LEN, IntPtr.Zero, IntPtr.Zero);
StringBuilder stringBuilder = new StringBuilder();
SendMessage(this.listBoxHandle, LB_GET_TEXT, this.selectedItemIndex, stringBuilder);
this.selectedTextBlock.Text = stringBuilder.ToString();
handled = true;
break;
}
}
return IntPtr.Zero;
}
#endregion
#region Append 버튼 클릭시 처리하기 - appendButton_Click(sender, e)
/// <summary>
/// Append 버튼 클릭시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void appendButton_Click(object sender, EventArgs e)
{
if(this.appendTextBox.Text != string.Empty)
{
SendMessage(this.listBoxHandle, LB_ADD_STRING, IntPtr.Zero, this.appendTextBox.Text);
}
this.itemCount = SendMessage(this.listBoxHandle, LB_GET_COUNT, IntPtr.Zero, IntPtr.Zero);
this.itemCountTextBlock.Text = this.itemCount.ToString();
}
#endregion
#region Delete 버튼 클릭시 처리하기 - deleteButton_Click(sender, e)
/// <summary>
/// Delete 버튼 클릭시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void deleteButton_Click(object sender, EventArgs e)
{
this.selectedItemIndex = SendMessage(this.controlHost.ListBoxWindowHandle, LB_GET_CUR_SEL, IntPtr.Zero, IntPtr.Zero);
if(this.selectedItemIndex != -1)
{
SendMessage(this.listBoxHandle, LB_DELETE_STRING, (IntPtr) this.selectedItemIndex, IntPtr.Zero);
}
this.itemCount = SendMessage(this.listBoxHandle, LB_GET_COUNT, IntPtr.Zero, IntPtr.Zero);
this.itemCountTextBlock.Text = this.itemCount.ToString();
}
#endregion
}
}
728x90
반응형
그리드형(광고전용)
'C# > WPF' 카테고리의 다른 글
[C#/WPF] Dispatcher 클래스 : Run 정적 메소드를 사용해 다중 윈도우 다중 스레드 사용하기 (0) | 2023.01.08 |
---|---|
[C#/WPF] 백그라운드 스레드로 차단 작업 처리하기 (0) | 2023.01.08 |
[C#/WPF] DispatcherObject 클래스 : Dispatcher 속성을 사용해 장기 실행 계산이 포함된 단일 스레드 애플리케이션 만들기 (0) | 2023.01.07 |
[C#/WPF] ObjectCache 클래스 : 응용 프로그램 데이터 캐싱하기 (0) | 2023.01.06 |
[C#/WPF] RenderOptions 클래스 : SetCachingHint 정적 메소드를 사용해 캐싱 힌트 옵션 설정하기 (0) | 2023.01.04 |
[C#/WPF] WindowsFormsHost 클래스 : PropertyMap 속성을 사용해 WinForm 컨트롤 속성 매핑 설정하기 (0) | 2023.01.02 |
[C#/WPF] WindowsFormsHost 엘리먼트 : 하이브리드 애플리케이션에서 데이터 바인딩하기 (0) | 2022.12.31 |
[C#/WPF] WindowsFormsHost 엘리먼트 : WinForm 레이아웃 컨트롤 사용하기 (0) | 2022.12.29 |
[C#/WPF] WindowsFormsHost 엘리먼트 : Margin/Padding 속성 사용하기 (0) | 2022.12.29 |
[C#/WPF] WindowsFormsHost 엘리먼트 : 회전 설정하기 (0) | 2022.12.29 |
댓글을 달아 주세요