Reputation: 116
I have two list-boxes, where both of them are bound to two different XML files. The purpose is to drag XmlElements from one file into another file (ListBox).
When I drag from one populated ListBox to another populated ListBox the code in the target ListBox is fairly simple. But when the target ListBox is empty, it is difficult to get any XmlElements since the ListBox does not contain any items.
Since the target is not populated the code will fail at:
XmlElement targetXmlElement = (XmlElement)parent.Items.GetItemAt(0);
So the question is:
How do i get a hold of the XmlDataProvider or XmlDocument from the ListBox-target : ListBox parent = (ListBox)sender;
Another problem is that the target-listbox should contain a list of child-nodes, the target for our dragged element. How do I get access to the parent element?
ListBox dragSource = null;
private void FoodListBox_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
ListBox parent = (ListBox)sender;
dragSource = parent;
object data = GetDataFromListBox(dragSource, e.GetPosition(parent));
if (data != null)
{
DragDrop.DoDragDrop(parent, data, DragDropEffects.Copy);
}
}
#region GetDataFromListBox(Listbox, Point)
private static object GetDataFromListBox(ListBox source, Point point)
{
UIElement element = source.InputHitTest(point) as UIElement;
if(element != null)
{
object data = DependencyProperty.UnsetValue;
while(data == DependencyProperty.UnsetValue)
{
data = source.ItemContainerGenerator.ItemFromContainer(element);
if (data == DependencyProperty.UnsetValue)
{
element = VisualTreeHelper.GetParent(element) as UIElement;
}
if (element == source)
{
return null;
}
}
if(data != DependencyProperty.UnsetValue)
{
return data;
}
}
return null;
}
#endregion
//This listbox is bound to Dataprovider2, Objects dragged into will access the XML target
private void ListBox_Drop(object sender, DragEventArgs e)
{
ListBox parent = (ListBox)sender;
//Get access to the element from the source XML
XmlElement sourceXmlElement = (XmlElement)e.Data.GetData(typeof(XmlElement));
//Get the position of the parent to any Element in the the target list (e.g the zero element)
XmlElement targetXmlElement = (XmlElement)parent.Items.GetItemAt(0);
AppendXmlNode(sourceXmlElement, targetXmlElement);
}
Upvotes: 2
Views: 91
Reputation: 116
Thank you for a good answer bab7lon !
The code snippet did return a source
as null though:
BindingExpression bindingExpression = parent.GetBindingExpression(ListBox.ItemsSourceProperty);
Binding parentBinding = bindingExpression.ParentBinding;
XmlDataProvider source = (XmlDataProvider)parentBinding.Source;
But I realized after a while that the object sender
actually contained a DataContext.
The following code solved my problem:
private void ListBox_Drop(object sender, DragEventArgs e)
{
ListBox parent = (ListBox)sender;
//Get access to the element from the source XML
XmlElement sourceXmlElement = (XmlElement)e.Data.GetData(typeof(XmlElement));
XmlDataProvider l_context = (XmlDataProvider)parent.DataContext;
XmlDocument l_XmlDoc = l_context.Document;
string l_Xpath = l_context.XPath;
XmlNode l_node = l_XmlDoc.SelectSingleNode(l_Xpath.Replace("/foodlist/foodtype",""));
XmlElement targetXmlElement = (XmlElement)l_node.SelectSingleNode("foodlist");
AppendXmlNode(sourceXmlElement, targetXmlElement);
}
private void AppendXmlNode(XmlElement source, XmlElement target)
{
XmlElement l_source = source;
XmlElement l_target = target;
//Append first the Parent
XmlNode l_nodeToCopy = l_target.OwnerDocument.ImportNode(l_source, true);
l_target.AppendChild(l_nodeToCopy);
XmlDocument l_doc = l_target.OwnerDocument;
Save(l_doc);
}
Upvotes: 1
Reputation: 343
In WPF you're not binding directly to a collection, but to the default view for that collection.
All collections have a default CollectionView. WPF always binds to a view rather than a collection. If you bind directly to a collection, WPF actually binds to the default view for that collection. This default view is shared by all bindings to the collection, which causes all direct bindings to the collection to share the sort, filter, group, and current item characteristics of the one default view.
Rather than fetching the filtered view, you can extract the XmlDocument
from the binding source.
private void targetListBox_Drop(object sender, DragEventArgs e)
{
ListBox parent = (ListBox)sender;
//Get access to the element from the source XML
XmlElement sourceXmlElement = (XmlElement)e.Data.GetData(typeof(XmlElement));
//Get access to the document from the target XML
BindingExpression bindingExpression =
parent.GetBindingExpression(ListBox.ItemsSourceProperty);
Binding parentBinding = bindingExpression.ParentBinding;
XmlDataProvider source = (XmlDataProvider)parentBinding.Source;
XmlDocument targetXmldocument = source.Document;
AppendXmlNode(sourceXmlElement, targetXmldocument);
}
Once you get ahold of the doc, modifying is a breeze.
private static void AppendXmlNode(XmlElement element, XmlDocument doc)
{
//Get access to the root node to append child
XmlNode root = doc.SelectSingleNode("/recipelist/recipe[contains(.,'name')]/foodlist");
//Detach element from source XmlDocument
XmlNode clone = doc.ImportNode(element, true);
root.AppendChild(clone);
}
Upvotes: 1