今天DebugLZQ在做WPF拖動總結的時候,遇到了這個問題。baidu了下,貌似沒有解決這個問題的權威答案,遂寫下這篇博文。
我想做的事情是:拖動一個窗體內的控件(Rectangle)到另一個容器控件內,而保留原來的控件。
為了更好地把問題說清楚,請看如下代碼片段:
void canvas1_Drop(object sender, DragEventArgs e) { IDataObject data = new DataObject(); data = e.Data; if (data.GetDataPresent(typeof(Rectangle))) { Rectangle rect = new Rectangle(); rect = data.GetData(typeof(Rectangle)) as Rectangle; canvas1.Children.Add(rect); } }
最后一行代碼報告這樣的運行時異常:
Specified element is already the logical child of another element. Disconnect it first
這是控件拷貝的問題。為了解決這個問題,我們可以這樣:
void canvas1_Drop(object sender, DragEventArgs e) { IDataObject data = new DataObject(); data = e.Data; if (data.GetDataPresent(typeof(Rectangle))) { Rectangle rect = new Rectangle(); rect = data.GetData(typeof(Rectangle)) as Rectangle;
canvas2.Children.Remove(rect);// canvas1.Children.Add(rect); } }
以上代碼,是能消除這個異常,但是,被拖動的控件也沒了。如果需求是不保留原來這個Rectangle,問題也就解決了,DebugLZQ也沒有必要寫這篇博文分享給各位。
既然控件直接拿過來行不通,那么這么解決呢?
容易想到的方法是復制這個控件。但是如你所見,上面的復制方法明顯行不通,屬於引用對象的拷貝,只拷貝了一個指針,其實是同一個對象。
深拷貝,好了:
傻x一點辦法如下:
if (data.GetDataPresent(typeof(Rectangle))) { Rectangle dataobj = data.GetData(typeof(Rectangle)) as Rectangle; Rectangle rect = new Rectangle(); rect.Height = dataobj.RenderSize.Height; rect.Width = dataobj.RenderSize.Width; rect.Fill = dataobj.Fill; rect.Stroke = dataobj.Stroke; rect.StrokeThickness = dataobj.StrokeThickness; canvas1.Children.Add(rect); rect.SetValue(Canvas.TopProperty, e.GetPosition(canvas1).Y); rect.SetValue(Canvas.LeftProperty, e.GetPosition(canvas1).X); }
問題是解決了,但這種代碼明顯丑陋!不堪入目~雖然是效果上實現了,但總感覺其中哪里影藏着一個定時炸彈,DebugLZQ惶惶不可終日;再退一步講,即使這樣沒有問題,但是1個rectangle就得如此大費周章,要是來個for循環怎么辦?!
因此這種解決方法絕非可接受!
You can clone a control by first serializing it using XamlWriter
and then create a new control by deserializing it using XamlReader.
英文就是好,本來中文啰嗦一大堆的東西,一句話就寫完了!
我們可以(深)拷貝這個控件采用序列化/反序列化的方式!實現如下:
if (data.GetDataPresent(typeof(Rectangle))) { Rectangle rect = new Rectangle(); rect = data.GetData(typeof(Rectangle)) as Rectangle; //canvas2.Children.Remove(rect); //canvas1.Children.Add(rect); //序列化Control,以深復制Control!!!! string rectXaml = XamlWriter.Save(rect); StringReader stringReader = new StringReader(rectXaml); XmlReader xmlReader = XmlReader.Create(stringReader); UIElement clonedChild = (UIElement)XamlReader.Load(xmlReader); canvas1.Children.Add(clonedChild); }
希望對你有幫助~
很久沒有把博文發到首頁了,這篇發一下吧,老鳥飛過,輕拍~
這篇博文說白了,就是序列化/反序列化。更一般的方法,請參考DebugLZQ的博文:總結.NET中的:賦值VS淺拷貝VS深拷貝[序列化/反序列化]
tips:今天在codeproject上看到一篇類似的文章,XAML Serialization ,覺得寫得沒有我的好,大家也可以看下~
沒什么高端的東西,老鳥繞過,輕拍~