하나의 패널 속에 여러개의 패널을 삽입이 가능하다. 이걸 가지고 패널 속에 Visio 와 같은 에디팅 화면을 구현할 수 있을거 같다. 물론 Panel이 아닌 ScrollView 등 다양한 뷰에도 구현이 가능해 진다.
특정 이벤트에 따라서 Panel에 여러개의 Panel 자식 컨트롤을 추가 한 후에 자식 Panel의 이벤트를 사용해서 각각의 패널을 동작시킬 수 있다.
우선 이벤트에 따른 자식 Panel로 등록하는 코드 이다.
private void button2_Click(object sender, EventArgs e) { Panel childPanel = new Panel(); childPanel.BackColor = System.Drawing.Color.Gainsboro; childPanel.Location = new System.Drawing.Point(67, 44); childPanel.Name = "panel4"; childPanel.Size = new System.Drawing.Size(71, 78); childPanel.MouseDown += new MouseEventHandler(childPanel_MouseDown); childPanel.MouseMove += new MouseEventHandler(childPanel_MouseMove); childPanel.MouseUp += new MouseEventHandler(childPanel_MouseUp); panel2.Controls.Add(childPanel); } - 여러개의 패널이 등록 되지만 이벤트를 처리 하는 함수는 동일하게 가져 가게 되면 공통적으로 Panel을 처리할 수 있다. (childPanel_MouseDown, Move, Up)
이제 Drag a Panel을 구현하기 위한 코드이다.
private bool dragging = false; private Point offset;
웹의 경로에 위치한 이미지를 가져오는 모듈입니다. 원래 이미지 사이즈를 구하기 위해서 사용했던건데요. 파일 다운로드 쪽으로 활용해도 가능성이 있을듯 하네요.
try { byte[] data = new System.Net.WebClient().DownloadData(url); System.IO.MemoryStream ms = new System.IO.MemoryStream(data); Image img = Image.FromStream(ms);
Size size = new Size(img.Width, img.Height); return size; } catch(Exception ex) { return new Size(-1, -1); }
.Net WinForm에서 작업을 하다 보면 MFC의 확장성과는 비교도 되지 않을 만큼 좋은 컨트롤들로 무장되어 있는거 같다. 아직까지 다양한 확장 기능을 사용하고 .Net 의 좋은 기능들에 대해서 많이 모르지만 어느정도 선까지는 MFC의 생산성과는 비교가 안되는거 같다.
오늘 ListView 두개를 사용해서 드래그앤 드롭을 처리해 봤다. 참 쉽다고 느껴 진다. 코드 내용을 우선 정리해 볼려고 한다.
드래그 앤 드롭에는 몇가지 용어가 나오게 된다.
1. 소스(Source) 2. 타깃(Target) 3. 드롭 소스, 드롭 타깃
더 있지만. 큰 주제를 놓고 본다면 이것이 있을 것이다.
여기서 말을 풀어 보면 아래와 같을 것이다. 에플리케이션에서 드래그 앤 드롭(drag and drop) 기능을 추가 하려면 드롭 타깃(drop target)과 드롭 소스(drop source)를 고려 해야 한다. 우선, "무언가를 끌어다(drag) 놓을(drop)컨트롤" 이 하나 있어야 한다. 이런 종류의 컨트롤을 드롭 타깃(drop target)이라고 한다. 컨트롤의 AllowDrop 속성을 true로 설정하면 해당 컨트롤을 드롭 타깃으로 만들 수 있다.
작업 순서를 보겠다. 1. 두개의 ListView를 추가 2. 타깃이 될 ListView에 AllowDrop를 true로 설정 3. 소스 ListView에 MouseDown() 이벤트 등록 4. 타깃 ListView에 DragDrop(), DragEnter() 이벤트 등록 5. 코드 작성 6. 테스트
// private void listView1_MouseDown(object sender, MouseEventArgs e) { try { // 전달할 객체를 난 구조체를 사용하였다. EffectInfo ef = new EffectInfo();
// 멀티 선택이 될수 있기 때문에 collection 객체로 받도록 한다. ListView.SelectedListViewItemCollection breakfast = this.listView1.SelectedItems;
// 하나만 사용하기 위해서 개수를 검사 하고 throw로 던진다. if (breakfast.Count != 1) throw new Exception("멀티 선택 되었네요");
private void listView2_DragEnter(object sender, DragEventArgs e) { // 타입을 검사 해서 커서 모양을 선택해 준다. if (e.Data.GetDataPresent(typeof(EffectInfo))) e.Effect = DragDropEffects.Copy; else e.Effect = DragDropEffects.None; }
private void listView2_DragDrop(object sender, DragEventArgs e) { // 드롭 했을때 값을 처리 한다. EffectInfo ef = (EffectInfo)e.Data.GetData(typeof(EffectInfo)); ListViewItem listviewitem = new ListViewItem(ef.itemname, ef.imageidx); this.listView2.Items.Add(listviewitem); }
int count = 0; // Item 이름을 위한 임시 변수 foreach (string path in filepathlist) { // WebClient 를 사용해서 원격 이미지를 로드 하고 Stream에 Write 한다. WebClient client = new WebClient(); byte[] myDataBuffer = client.DownloadData(path); Stream stream = new MemoryStream(); stream.Write(myDataBuffer, 0, myDataBuffer.Length);
// Bitmap에 Stream을 입력 하고 ImageList에 등록한다. Bitmap bmp = new Bitmap(stream); this.imageList1.Images.Add(bmp); int idx = this.imageList1.Images.Keys.Count - 1; this.imageList1.Images.SetKeyName(idx, "");
// ListViewItem에 값을 채우고 ListView에 추가 한다. ListViewItem listviewitem = new ListViewItem("test" + count.ToString(), idx); this.listView1.Items.Add(listviewitem);
위와 같이 iis 쪽에 해당 이미지가 있다고 했을때 Winform 쪽에 PictureBox에 이미지를 Url 로 추가 하면 안되는거 같다.( 바로 적용이 가능한지 아시는분은 답변 부탁 ^^) 그래서 웹 브라우져 컨트롤을 써자니 좀 이상해서 PictureBox에 사용하기 위해서는 원격 주소에 있는 데이터를 클라이언트로 가져와야 하는걸 알았다.
이때 두가지 경우가 있는데 스트림, 파일 이 있다. 파일 보다는 스트림이 나을거 같아서 이것을 채택 했다.
그리고 위의 소스를 보면 아시겠지만 DB의 정보를 가져 와서 보여 주는 뷰어로 사용할려다 보니 DataSet와 연동을 하였다.
icon_img 정보를 가져 와서 iis의 주소에 맵핑 해서 해당 이미지를 보여 주도록 하였다. 그래서 바인딩 부분에서 몇가지 수정이 필요하다.
PictureBox에 바인딩이 있다고 가정한다. Binding binding = icon_imgPictureBox.DataBindings["Image"]; binding.Format += new ConvertEventHandler(binding_Format);
위와 같이 Format 이벤트를 하나 생성한다. 그리고 아래와 같이 작성합니다.
void binding_Format(object sender, ConvertEventArgs e) { if (e.Value != null) { try { //e.Value에 있는 값을 사용해서 파일 경로를 생성한다. string filepath = "http://local:8888/Icon/2000/" + e.Value + ".gif"; // WebClient 객체를 생성해서 스트림을 가져 오도록 한다. WebClient client = new WebClient(); byte[] myDataBuffer = client.DownloadData(filepath); // Stream 값을 가져 온다. Stream stream = new MemoryStream(); stream.Write(myDataBuffer, 0, myDataBuffer.Length); // PictureBox에 스트림으로 로드 한다. this.icon_imgPictureBox.Image = Image.FromStream(stream, true); } catch (WebException webex) { } } }
try, catch 를 사용한 이유는 e.Value 에 값이 파일 이름이 아닌 다른 정보가 들어 오는 경우가 있다. 그래서 catch로 넘기도록 해서 에러를 없애도록 한다.
if (openFileDialog1.ShowDialog() == DialogResult.OK) { // 컨트롤에 이름 설정하기 tbAzeName.Text = openFileDialog1.FileName;
try { if ((myStream = openFileDialog1.OpenFile()) != null) { using (myStream) { // 바이너리로 읽기 위해서 BinaryReader를 생성하고 myStream의 // 스트림 값을 넘겨 받는다. BinaryReader r = new BinaryReader(myStream); for (int i = 0; i < 5; i++) { Console.WriteLine(r.ReadInt32()); } } } } catch (Exception ex) { MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message); } } }
DirectX 코드를 사용해서 텍스처를 연결시켰다. 기존 코드 : http://www.iamgsi.com/entry/DirecrX-Managed-Create-a-Device-C 를 조금더 확장 시켰다. 이벤트 함수 OnResetDevice도 추가 했으며, 여기서 봐야 할 것은 윈도우 사이즈가 변할때 텍스처가 검정색으로 나올때가 있다. 이때 텍스처 함수 부분은 Managed로 해야 하는지는 잘 모르겠지만. 아래와 같은 인자를 사용하게 되는데 Managed를 할때는 몇개의 인자가 더 필요 하더라.
아래의 함수를 사이즈 변할때 호출해줘야 제대로 나오게 된다. private void directXBaseView1_OnResetDevice(object sender, DeviceEventArgs e) { ... if (texture != null) texture.Dispose(); // 이 코드를 넣지 않으면 메모리가 계속 누적된다. Texture가 계속해서 생성이 되는거 같다. texture = TextureLoader.FromFile(dev, @"d:\bbbb.jpg"); }
나머지는 타이머를 사용해서 화면은 계속해서 리프레쉬 해주었다.
----DirectXBaseView.cs---- using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Linq; using System.Text; using System.Windows.Forms;
// DirectX using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; using Direct3D = Microsoft.DirectX.Direct3D;
// namespace AzeViewerSimple3 { public delegate void CreateDeviceEventHandler(object sender, DeviceEventArgs e); public delegate void ResetDeviceEventHandler(object sender, DeviceEventArgs e); public delegate void RenderEventHandler(object sender, DeviceEventArgs e);
// public class DeviceEventArgs { public Device dev; public Device Device { get { return dev; } }
public DeviceEventArgs(Device device) { dev = device; } }
// public partial class DirectXBaseView : UserControl { #region Members; private Device _device = null; private bool pause = false; public bool Pause { get { return pause; } set { this.pause = value; } } public event CreateDeviceEventHandler OnCreateDevice; public event ResetDeviceEventHandler OnResetDevice; public event RenderEventHandler OnRender;
#endregion
#region ctor public DirectXBaseView() { InitializeComponent(); } #endregion
#region Properties private Color _deviceBackColor = Color.Blue; public Color DeviceBackColor { get { return _deviceBackColor; } set { _deviceBackColor = value; } } #endregion
#region Rendering public void Render() { if (Pause) return;
if (OnResetDevice != null) OnResetDevice(this, new DeviceEventArgs(_device)); } #endregion } }
----Form1.cs---- using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms;
// DirectX using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; using Direct3D = Microsoft.DirectX.Direct3D;
namespace AzeViewerSimple3 { public partial class Form1 : Form { #region Member VertexBuffer vertexBuffer = null; Texture texture = null; //bool pause = false; #endregion
C#에서 프로젝트를 하기 위해서 DirectX를 연동하고 있다. 현재 소스는 Tutorial 1: Create a Device를 사용해서 UserControl에 붙인 것과 동일합니다.
----DxViewer.cs---- using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Linq; using System.Text; using System.Windows.Forms;
// DirectX using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; using Direct3D = Microsoft.DirectX.Direct3D;
namespace Gsi.DxViewer { public delegate void RenderEventHandler(object sender, DeviceEventArgs e); public delegate void CreateDeviceEventHandler(object sender, DeviceEventArgs e);
public class DeviceEventArgs : EventArgs { private Device _device;
public Device Device { get { return _device; } }
public DeviceEventArgs(Device device) { _device = device; } }
public partial class DxViewer : UserControl { #region Members;
/// <summary> /// Extend this list of properties if you like /// </summary> private Color _deviceBackColor = Color.Black;
public Color DeviceBackColor { get { return _deviceBackColor; } set { _deviceBackColor = value; } } #endregion
#region Rendering
/// <summary> /// Rendering-method /// </summary> public void Render() { if (_device == null) return;
//Clear the backbuffer _device.Clear(ClearFlags.Target, _deviceBackColor, 1.0f, 0);
//Begin the scene _device.BeginScene();
// Render of scene here if (OnRender != null) OnRender(this, new DeviceEventArgs(_device));
//End the scene _device.EndScene(); _device.Present(); }
#endregion
#region Overrides
protected override void OnPaint(PaintEventArgs e) { // Render on each Paint this.Render(); } #endregion
} }
----Form1.cs---- using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms;
// DirectX using Microsoft.DirectX; using Microsoft.DirectX.Direct3D; using Direct3D = Microsoft.DirectX.Direct3D;
using Gsi.DxViewer;
namespace AzeViewerSimple2 { public partial class Form1 : Form { public Form1() { InitializeComponent(); }
----참고사항---- References 에 Microsoft.DirectX Microsoft.DirectX.Direct3D Microsoft.DirectX.Direct3DX 를 추가 한후에 빌더를 하고 나면 DxViewer 컨트롤이 Toolbox에 생깁니다. 그 컨트롤을 Form1의 디자인 화면으로 드래그 해서 넣은 후에 이름을 dxViewer1이라고 지정한후 코드를 참조 하시면 됩니다.