了解IDataObject接口

WPF中的拖放和剪切板操作都是建立在IDataObject的操作的,那么我们先来仔细研修一下IDataObject接口。

此接口在Windows Forms中就已经有,这里就只讨论WPF中的(System.Windows.IDataObject接口)

 

IDataObject保存这一个数据的多种表现形式,比如用这个对象保存我的信息,如果输出文字的话,可能是我的名字,如果输出图像的话,就是我的照片,输出数字的话,就是我的身份证号……当然数据本身和数据类型都是可以自定义的也不是不需要有联系的。

 

 

IDataObject用字符串来表示一种类型的名称,当然也可以使用Type类,最终Type.FullName属性会被用作类型字符串。

其次类型之间可以存在转换功能,比如UnicodeText和System.String类型都是一样的,那么如果查询UnicodeText但IDataObject中包含有System.String类型,那么应该可以返回System.String类型的值。

 

来这样看IDataObject的成员函数分析:

 

interface IDataObject

    object GetData(string/Type); 

    //返回指定类型名称的数据

    bool GetDataPresent(string/Type);

    //检查是否存在指定类型

    void SetData(string/Type, object) (object);

    //添加新的数据:类型名称:stringType.FullNameobject.GetType().FullName

    string[] GetFormats();

    //返回所有存在的数据类型名称,字符串数组

 

//所有函数还包含一个bool参数,来指定是否考虑数据类型之间可能存在的转换关系

下面是IDataObject类的全部成员

image

 

 

 

使用DataObject类

了解完了IDataObject接口,怎么使用啊?同样在System.Windows命名空间下,有一个类继承了IDataObject接口:DataObject类,我们先利用DataObject,演示下IDataObject的使用:

            IDataObject ido = new DataObject();

 

            ido.SetData(23f);

            //添加一个float,类型名称是System.Single

            ido.SetData(typeof(Guid), Guid.NewGuid());

            //添加一个System.Guid

            ido.SetData("我的类型", new Button() { Content = "hehe" });

            //添加一个自定义类型(自定义名称),存储一个WPF按钮

 

            //查询

            bool hasGuid = ido.GetDataPresent("System.Guid");

            bool hasSingle = ido.GetDataPresent(typeof(Single));

            bool hasDouble = ido.GetDataPresent(typeof(Double));

 

            Guid guid = (Guid)ido.GetData(typeof(Guid));

            Button btn = (Button)ido.GetData("我的类型");

 

            string[] types = ido.GetFormats();

 

            //输出

            Trace.WriteLine(hasGuid, "存在Guid");

            Trace.WriteLine(hasSingle, "存在float");

            Trace.WriteLine(hasDouble, "存在double");

            Trace.WriteLine(guid, "Guid");

            Trace.WriteLine(btn, "Button");

            Trace.WriteLine(String.Join(" <=> ", types), "IDataObject所有类型");

 

 

输出:

存在Guid: True

存在float: True

存在double: False

Guid: 0687001c-dc0d-4c87-8d27-ff3b6655bab0

Button: System.Windows.Controls.Button: hehe

IDataObject所有类型: 我的类型 <=> System.Guid <=> System.Single

 

通过DataObject,我们可以使用IDataObject提供的接口函数,同时注意如果自动转换参数没有指定的话,DataObject的执行是默认为true。

从例子里可以看出来,IDataObject的类型名称和数据都是完全可以自定义的,没有任何限制。

 

 

DataFormats类

虽然IDataObject的类型名称可以自定义,但是在一般情况下我们可以直接利用DataFormats类已经提供的预定义字段来进行IDataObject的数据操作,这些字段的值也是跟操作系统所紧密联系的。

 

DataFormats包含的数据类型名称有:图像格式,HTML格式,文件格式,声音格式……这里就不一一列举了。

在后面具体讲“剪切板操作”和“拖放操作”中,我们都会用到DataFormats类。

 

 

 

再议DataObject类

上面提到过DataObject类,但仅仅为了演示IDataObject接口的使用,这里了解了DataFormats类后,我们可以彻底研究一下这个DataObject类,这个类成员函数看起来比较多,实际上很好理解,大致是这样一个结构:

class DataObject : IDataObject

/*

    省略继承了所有IDataObject的成员函数,同时如果自动转换参数没有指定的话,默认为true

*/

//这里提供一些常用数据类型的操作辅助函数,本质上是DataFormats类中的字段来做数据类型名,然后调用IDataObject的操作函数

    Contains/Set/Get/Audio/FileDrop...

//附加事件,当指定对象进行拖放或剪切板操作

    Copy, Pasting, SettingData;

 

 

辅助函数为了调用方便,但不是必须的,比如是用GetImage等效于是用GetData(DataFormats.Bitmap)。

 

 

 

剪贴板操作

掌握了IDataObject后,拖放和剪切板操作就简单多了,我们先看剪切板操作,不得不提Clipboard类

static class Clipboard

void Clear();

//清空剪切板

 

IDataObject GetDataObject();

//返回当前剪切板的IDataObject

void SetDataObject(object) (object);

//设置剪切板的IDataObject

    bool IsCurrent(IDataObject);

//判断IDataObject是否等于当前剪切板的内容

 

//下面的这些函数就像DataObject类的辅助函数一样,利用DataFormats的字段做一些常用数据操作

    bool ContainsXXX();

    xxx GetXXX();

    void SetXXX(xxx);

 

 

其中核心操作清空,进剪切板和出剪切板分别是Clear, SetDataObject, GetDataObject,其他所有其它函数围绕着这3大核心功能,整个Clipboard类和DataObject类很像(毕竟都是在操作IDataObject)。

下面我们来利用Clipboard类做一个非常有意思的WPF程序:

 

功能是:在TextBox中输入3行文字,第一行是普通字符串文本,第二行是数字,第三行是一个图像文件的路径。

程序输入正确后,会把字符串,数字和图像复制进剪切板,然后把它们粘贴下面。

image

另外,这个程序最有意思的地方是:由于对剪切板设置的不同组数据,因此程序运行后,在记事本里粘贴会显示文字”Mgen!”,而在画图里粘贴的话会显示上面那张图片。利用Windows剪贴板管理器可以直观看到这些数据:

image

 

 

其实,所谓的复制粘贴代码就是Clipboard类中的IDataObject的操作,下面是程序的主逻辑代码:

XAML

    <StackPanel>

        <TextBox Name="tbx"

                 AcceptsReturn="True"

                 VerticalScrollBarVisibility="Visible"

                 Height="100"/>

        <Button Click="btn_Click">复制并粘贴</Button>

        <TextBlock Text="下面是剪切板内信息"/>

        <StackPanel Name="view"/>

    </StackPanel>

C#

    public partial class Window1 : Window

    {

        public Window1()

        {

            InitializeComponent();

        }

 

        private void btn_Click(object sender, RoutedEventArgs e)

        {

            try

            {

                var strs = tbx.Text.Split(new string[] { Environment.NewLine },

                    StringSplitOptions.None);

                var s = strs[0];

                var i = Convert.ToInt32(strs[1]);

                var img = new BitmapImage(new Uri(strs[2]));

 

                var data = new DataObject();

                data.SetData(DataFormats.UnicodeText, s);

                data.SetData(typeof(int), i);

                data.SetData(DataFormats.Bitmap, img);

 

                Clipboard.SetDataObject(data, true);

                Paste();

            }

            catch

            {

            }

        }

 

        void Paste()

        {

            view.Children.Clear();

            view.Children.Add(new TextBlock() {

                Text = "文字:" + Clipboard.GetText() });

            view.Children.Add(new TextBlock() {

                Text = "数字:" + Clipboard.GetData(typeof(int).FullName).ToString() });

            view.Children.Add(new Image() { Source = Clipboard.GetImage() });

        }

    }

 

 

拖放操作

拖放操作,顾名思义分“拖”和“放”两个操作,是数据从源头到终点的传递操作:

image

 

需要用到两个类的成员:DragDrop类和UIElement类

class DragDrop

    //针对数据终点

DragEnter, Leave, Over //进入,离开,移动

 

//针对数据源

    GiveFeedBack

    QueryContinueDrag

 

 

//开始拖放操作

    DragDropEffects DoDragDrop(DependencyObject datasource, object data, DragDropEffects allowedEffects);

  

class UIElement

//允许数据被拖放至此:指定数据终点

    AllowDrop: bool;

 

 

 

“拖放”中拖的操作主要就是在鼠标按下事件中调用DoDragDrop方法来开始拖放操作。

放的操作就是(首先AllowDrop=true)在DragEnter,DragLeave,DragOver以及Drop事件中对数据进行处理。

 

比如下面这个程序,当往ListBox拖放控件后,会显示控件类型,同时也可以从系统资源管理器中拖入文件或文件夹,路径会被显示在ListBox中。

image

 

XAML:

    <DockPanel>

        <Button Name="btn">Button</Button>

        <Label Name="label">Label</Label>

        <ListBox Name="list"

                 AllowDrop="True"

                 DragEnter="ListBox_DragEnter"

                 Drop="ListBox_Drop"></ListBox>

    </DockPanel>

 

代码:

    public partial class Window1 : Window

    {

        public Window1()

        {

            InitializeComponent();

            ApplyDragEvents(btn);

            ApplyDragEvents(label);

        }

 

 

        private void ListBox_DragEnter(object sender, DragEventArgs e)

        {

            if (e.Data.GetDataPresent("MyControl") || e.Data.GetDataPresent(DataFormats.FileDrop))

                e.Effects = DragDropEffects.Copy;

        }

 

        private void ListBox_Drop(object sender, DragEventArgs e)

        {

            var list = e.Source as ListBox;

            if (e.Data.GetDataPresent("MyControl"))

            {

                var con = e.Data.GetData("MyControl") as Control;

                AddItem(con.ToString());

            }

            else

            {

                string[] files = e.Data.GetData(DataFormats.FileDrop) as string[];

                foreach (var s in files)

                    AddItem(s);

            }

 

        }

 

        void AddItem(string s)

        {

            list.Items.Add(new ListBoxItem() { Content = s });

        }

 

        void ApplyDragEvents(UIElement ele)

        {

            Point staPoint = new Point();

            ele.PreviewMouseDown += (sender, e) =>

                {

                    staPoint = e.GetPosition((IInputElement)e.Source);

                };

 

            ele.PreviewMouseMove += (sender, e) =>

                {

                    if (e.LeftButton == MouseButtonState.Pressed)

                    {

                        Point position = e.GetPosition(null);

 

                        if (Math.Abs(position.X - staPoint.X) > SystemParameters.MinimumHorizontalDragDistance ||

                            Math.Abs(position.Y - staPoint.Y) > SystemParameters.MinimumVerticalDragDistance)

                        {

                            var dataobj = new DataObject("MyControl", ele);

                            DragDrop.DoDragDrop(ele, dataobj, DragDropEffects.Move);

                        }

                    }

                };

        }

    }

作者: _Mgen 发表于 2011-07-11 14:02 原文链接

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"