有没有试过给ASP.Net Web Control实现Left和Top属性?就像WinForm的control一样,当在Designer里拖动control的时候,Properties window里的Left和Top跟着一起改变。当在Properties window里修改Left和Top时,control在Designer里位置跟着移动。
看似简单的问题,真的要做的话就会发现其难度了……
How to interact with Style["LEFT"] and Style["TOP"] of the WebControl during design time
http://blog.joycode.com/tingwang/articles/39633.aspx
Someone would like to implement the Left and Top properties for his WebControl. The two properties should appear in the Properties window during design time. When the user drags the control in the designer, the two properties should change automatically in the Properties window. When the users input some values for the two properties in the Properties window, the control should move in the designer. In short, the two properties should work in the same way as they work for the WinForm controls
This simple request in fact is not a trivial task. You will encounter difficulties when you try to get Style["LEFT"] and Style["TOP"] of the control in the designer. The solution is to implement a ControlDesigner:
public class LeftTopControlDesigner : ControlDesigner
{
[Category("Appearance"),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
Description("Sets the Top style attribute.")]
public string Top
{
get
{
return (string)Behavior.GetStyleAttribute("TOP", false, true);
}
set
{
Behavior.SetStyleAttribute("TOP", false, value, true);
}
}
[Category("Appearance"),
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
Description("Sets the Left style attribute.")]
public string Left
{
get
{
return (string)Behavior.GetStyleAttribute("LEFT", false, true);
}
set
{
Behavior.SetStyleAttribute("LEFT", false, value, true);
}
}
protected override void PreFilterProperties(System.Collections.IDictionary properties)
{
base.PreFilterProperties(properties);
properties["Top"] = TypeDescriptor.CreateProperty(GetType(), "Top", typeof(string));
properties["Left"] = TypeDescriptor.CreateProperty(GetType(), "Left", typeof(string));
}
}
由于Remoting的TcpChannel没有提供内建的认证机制,许多人希望能通过IP地址来辨认Remoting Client。我们可以在Remoting Server端注册上自定义的Server Channel Sink,通过Transport Headers来获取request的IP。详见:
How to get the IP address of the Remoting Client on Remoting Server
http://blog.joycode.com/tingwang/articles/39610.aspx
另外,下面的MSDN文章提供了一个基于SSPI认证的例子:
.NET Remoting Authentication and Authorization Sample - Part I
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndotnet/html/remsspi.asp?frame=true
In short, the Remoting Server can make use of Sink and Sink Provider to retrieve the IP address of the incoming request. The IP address is available in the Transport Headers of the incoming message. After retrieving the IP address in the Sink, we save the IP address to the CallContext, so that it will be available later in the code execution path as well. The followings are some background information regarding the topics:
Sinks and Sink Chains
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconsinkssinkchains.asp
Using CallContext
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconusingcallcontext.asp
Code modified from the sample by Ingo Rammer, author of “Advanced .Net Remoting”:
using System;
using System.Collections;
using System.IO;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging ;
using System.Runtime.Remoting.Channels;
using System.Threading;
using System.Net;
namespace ClassLibRemotingIPSink
{
public class ClientIPServerSinkProvider: IServerChannelSinkProvider
{
private IServerChannelSinkProvider next = null;
public ClientIPServerSinkProvider(IDictionary properties, ICollection providerData)
{
}
public void GetChannelData (IChannelDataStore channelData)
{
}
public IServerChannelSink CreateSink (IChannelReceiver channel)
{
IServerChannelSink nextSink = null;
if (next != null)
{
nextSink = next.CreateSink(channel);
}
return new ClientIPServerSink(nextSink);
}
public IServerChannelSinkProvider Next
{
get { return next; }
set { next = value; }
}
}
public class ClientIPServerSink : BaseChannelObjectWithProperties, IServerChannelSink, IChannelSinkBase
{
private IServerChannelSink _next;
public ClientIPServerSink (IServerChannelSink next)
{
_next = next;
}
public void AsyncProcessResponse ( System.Runtime.Remoting.Channels.IServerResponseChannelSinkStack sinkStack , System.Object state , System.Runtime.Remoting.Messaging.IMessage msg , System.Runtime.Remoting.Channels.ITransportHeaders headers , System.IO.Stream stream )
{
}
public Stream GetResponseStream ( System.Runtime.Remoting.Channels.IServerResponseChannelSinkStack sinkStack , System.Object state , System.Runtime.Remoting.Messaging.IMessage msg , System.Runtime.Remoting.Channels.ITransportHeaders headers )
{
return null;
}
public System.Runtime.Remoting.Channels.ServerProcessing ProcessMessage(System.Runtime.Remoting.Channels.IServerChannelSinkStack sinkStack, System.Runtime.Remoting.Messaging.IMessage requestMsg, System.Runtime.Remoting.Channels.ITransportHeaders requestHeaders, System.IO.Stream requestStream, out System.Runtime.Remoting.Messaging.IMessage responseMsg, out System.Runtime.Remoting.Channels.ITransportHeaders responseHeaders, out System.IO.Stream responseStream)
{
if (_next != null)
{
IPAddress ip = requestHeaders[CommonTransportKeys.IPAddress] as IPAddress;
Console.WriteLine(ip.ToString());
CallContext.SetData("ClientIPAddress",ip);
ServerProcessing spres = _next.ProcessMessage (sinkStack,requestMsg, requestHeaders,requestStream,out responseMsg,out responseHeaders,out responseStream);
return spres;
}
else
{
responseMsg=null;
responseHeaders=null;
responseStream=null;
return new ServerProcessing();
}
}
public IServerChannelSink NextChannelSink
{
get {return _next;}
set {_next = value;}
}
}
}
如果要实现把文件从Explorer里拖到WinForm上做处理,可以参见下面的KB:
How To Provide File Drag-and-Drop Functionality in a Visual C# .NET Application
http://support.microsoft.com/?id=307966
如果要实现把WinForm item拖到Explorer里生成文件,复杂了一点……详见:
How to implement Drag and Drop from managed WinForm to Explorer
http://blog.joycode.com/tingwang/articles/38825.aspx
The following code will enable dragging ListView items and dropping in Explorer to generate files. Temporary files will be generated and deferred rendering will be used...
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.IO;
namespace WinFormDragNDropExplorer
{
/// <summary>
/// Summary description for Form1.
/// </summary>
public class Form1 : System.Windows.Forms.Form
{
private System.Windows.Forms.ListView listView1;
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.Container components = null;
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
//
// TODO: Add any constructor code after InitializeComponent call
//
}
/// <summary>
/// Clean up any resources being used.
/// </summary>
protected override void Dispose( bool disposing )
{
if( disposing )
{
if (components != null)
{
components.Dispose();
}
}
base.Dispose( disposing );
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.Windows.Forms.ListViewItem listViewItem1 = new System.Windows.Forms.ListViewItem("Item 1");
System.Windows.Forms.ListViewItem listViewItem2 = new System.Windows.Forms.ListViewItem("Item 2");
System.Windows.Forms.ListViewItem listViewItem3 = new System.Windows.Forms.ListViewItem("Item 3");
this.listView1 = new System.Windows.Forms.ListView();
this.SuspendLayout();
//
// listView1
//
this.listView1.Items.AddRange(new System.Windows.Forms.ListViewItem[] {
listViewItem1,
listViewItem2,
listViewItem3});
this.listView1.Location = new System.Drawing.Point(40, 24);
this.listView1.Name = "listView1";
this.listView1.Size = new System.Drawing.Size(384, 280);
this.listView1.TabIndex = 0;
this.listView1.QueryContinueDrag += new System.Windows.Forms.QueryContinueDragEventHandler(this.listView1_QueryContinueDrag);
this.listView1.ItemDrag += new System.Windows.Forms.ItemDragEventHandler(this.listView1_ItemDrag);
//
// Form1
//
this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
this.ClientSize = new System.Drawing.Size(472, 333);
this.Controls.Add(this.listView1);
this.Name = "Form1";
this.Text = "Form1";
this.ResumeLayout(false);
}
#endregion
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Form1());
}
private ShellDataObject dataObj = null;
private void listView1_QueryContinueDrag(object sender, System.Windows.Forms.QueryContinueDragEventArgs e)
{
//ESC pressed
if (e.EscapePressed)
{
e.Action = DragAction.Cancel;
return;
}
//Drop!
if (e.KeyState == 0)
{
dataObj.SetData(ShellClipboardFormats.CFSTR_INDRAGLOOP, 0);
e.Action = DragAction.Drop;
return;
}
e.Action = DragAction.Continue;
}
private void listView1_ItemDrag(object sender, System.Windows.Forms.ItemDragEventArgs e)
{
String[] fileNames;
//Create the data object used for the drag drop operation
dataObj = new ShellDataObject();
//Create an array of strings to hold the filename(s)
fileNames = new String[listView1.SelectedItems.Count];
//Create temporary files that the user can drag.
for (int i = 0; i < listView1.SelectedItems.Count; i++)
{
fileNames[i] = CreateTemporaryFileName(listView1.SelectedItems[i].Text + ".txt");
}
//Add the list of files to the data object
dataObj.SetData(DataFormats.FileDrop, fileNames);
//Set the preferred drop effect
dataObj.SetData(ShellClipboardFormats.CFSTR_PREFERREDDROPEFFECT, DragDropEffects.Move);
//Indicate that we are in a drag loop
dataObj.SetData(ShellClipboardFormats.CFSTR_INDRAGLOOP, 1);
//Initiate the drag operation
listView1.DoDragDrop(dataObj, DragDropEffects.Move | DragDropEffects.Copy);
//Free the data object
dataObj = null;
}
private String CreateTemporaryFileName(String name)
{
String path;
String fileName;
System.IO.StreamWriter file;
//Build a path for the temporary file
path = System.IO.Path.GetTempPath();
fileName = System.IO.Path.Combine(path, name);
//Create the temporary file to make sure it exists
file = new System.IO.StreamWriter(fileName);
file.Close();