I'm converting an application I wrote in WinForms to UWP and as far as I can tell, the Drag n Drop functionality is slightly different. Here is my code from my WinForms application that I used to get the 'dragged' object, which is a Control called FunctionButton;
private void flowLayoutPanel_ActiveGroup_DragDrop(object sender, DragEventArgs e)
{
Function_Button draggedItem;
/* Check if the dragged item is one of the allowed dragged item TYPES. */
draggedItem = (Function_Button)e.Data.GetData(type);
if (draggedItem != null)
{
//DO STUFF
}
}
I'm currently setting my own StringDataFormats when the Drag Starts for the information i need, which I read using DataView.GetDataAsync(), although how can I get direct access to the dragged UIElement object in UWP?
I am not sure if this is the best way but it works.
First you need to handle the DragStarting event and store the UIElement that will be dragged inside the DataPackage that is exposed by the Data property. The DataPackage type seems to be very conditioned on files and file formats but fortunately it has a general purpose dictionary exposed by property Properties.
<local:YourElement CanDrag="True" DragStarting="dragStarting">
</local:YourElement>
private void dragStarting(UIElement sender, DragStartingEventArgs args)
{
args.Data.Properties.Add("anykeyworks", sender);
}
Next you handle the Drop event as follows:
<local:YourOtherElement AllowDrop="True" DragOver="dragOver" Drop="drop">
</<local:YourOtherElement>
private void drop(object sender, DragEventArgs e)
{
UIElement element = e.DataView.Properties["anykeyworks"] as UIElement;
}
private void dragOver(object sender, DragEventArgs e)
{
e.AcceptedOperation = DataPackageOperation.Copy;
}
If you do not implement the DragOver handler, the Drop event won't be fired.
As you've known, the drag and drop function in UWP is different from what in WinForms. In UWP apps, what we are dragging and dropping is not the UIElement but the DataPackage. So we can't get direct access to the dragged UIElement object.
I'm not sure why you want to get the dragged or dropped UIElement object. If you want to do some checks while dropping, I think you can check the content of the DataPackageView class which is exposed by DataView property.
For more information about drag and drop function in UWP, please see Drag and drop and also the official Drag and drop sample on GitHub.
Related
With WinForms, is there a way to be alerted to a control changing location with respect to the screen?
Say you have a Form with a button on it, and you would like to know when the button is moved from its current pixel location on the screen. If the button is moved to a different location on its parent Form you could obviously use the LocationChanged event, but if the Form is moved by the user, how do you know the button has visually moved?
In this simplified case the quick answer is to monitor the Form's LocationChanged and SizeChanged events, but there can be an arbitrary number of levels of nesting so monitoring those events for each parent up the chain to the primary form is not feasible. Using a timer to check if the location changed also seems like cheating (in a bad way).
Short version:
Given only an arbitrary Control object, is there a way to know when that Control's location changes on the screen, without knowledge of the control's parent hierarchy?
An illustration, by request:
Note that this "pinning" concept is an existing capability but it currently requires knowledge of the parent form and how the child control behaves; this is not the problem I am trying to solve. I would like to encapsulate this control tracking logic in an abstract Form that "pin-able" Forms can inherit from. Is there some message pump magic I can tap into to know when a control moves on the screen without having to deal with all the complicated parent tracking?
I'm not sure why you would say tracking the parent chain "is not feasible". Not only is it feasible, it's the right answer and the easy answer.
Just a quick hack at a solution:
private Control _anchorControl;
private List<Control> _parentChain = new List<Control>();
private void BuildChain()
{
foreach(var item in _parentChain)
{
item.LocationChanged -= ControlLocationChanged;
item.ParentChanged -= ControlParentChanged;
}
var current = _anchorControl;
while( current != null )
{
_parentChain.Add(current);
current = current.Parent;
}
foreach(var item in _parentChain)
{
item.LocationChanged += ControlLocationChanged;
item.ParentChanged += ControlParentChanged;
}
}
void ControlParentChanged(object sender, EventArgs e)
{
BuildChain();
ControlLocationChanged(sender, e);
}
void ControlLocationChanged(object sender, EventArgs e)
{
// Update Location of Form
if( _anchorControl.Parent != null )
{
var screenLoc = _anchorControl.Parent.PointToScreen(_anchorControl.Location);
UpdateFormLocation(screenLoc);
}
}
I need to drop an image file into my WPF application. I currently have a event firing when I drop the files in, but I don't know how what to do next. How do I get the Image? Is the sender object the image or the control?
private void ImagePanel_Drop(object sender, DragEventArgs e)
{
//what next, dont know how to get the image object, can I get the file path here?
}
This is basically what you want to do.
private void ImagePanel_Drop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
// Note that you can have more than one file.
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
// Assuming you have one file that you care about, pass it off to whatever
// handling code you have defined.
HandleFileOpen(files[0]);
}
}
Also, don't forget to actually hook up the event in XAML, as well as setting the AllowDrop attribute.
<StackPanel Name="ImagePanel" Drop="ImagePanel_Drop" AllowDrop="true">
...
</StackPanel>
The image file is contained in the e parameter, which is an instance of the DragEventArgs class.
(The sender parameter contains a reference to the object that raised the event.)
Specifically, check the e.Data member; as the documentation explains, this returns a reference to the data object (IDataObject) that contains the data from the drag event.
The IDataObject interface provides a number of methods for retrieving the data object that you're after. You'll probably want to start by calling the GetFormats method in order to find out the format of the data that you're working with. (For example, is it an actual image or simply the path to an image file?)
Then, once you've identified the format of the file being dragged in, you'll call one of the specific overloads of the GetData method to actually retrieve the data object in a particular format.
Additionally to answer of A.R. please note that if you want to use TextBox to drop you have to know following stuff.
TextBox seems to have already some default handling for DragAndDrop. If your data object is a String, it simply works. Other types are not handled and you get the Forbidden mouse effect and your Drop handler is never called.
It seems like you can enable your own handling with e.Handled to true in a PreviewDragOver event handler.
XAML
<TextBox AllowDrop="True" x:Name="RtbInputFile" HorizontalAlignment="Stretch" HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible" />
C#
RtbInputFile.Drop += RtbInputFile_Drop;
RtbInputFile.PreviewDragOver += RtbInputFile_PreviewDragOver;
private void RtbInputFile_PreviewDragOver(object sender, DragEventArgs e)
{
e.Handled = true;
}
private void RtbInputFile_Drop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
// Note that you can have more than one file.
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
var file = files[0];
HandleFile(file);
}
}
if I add control in Microsoft Blend 4 without set Name to this control and I want to set name to it and use it in c# how ?
example I added button using Blend in my layout but without give it a name
I want to give it a name using c# without x:Name="" in xaml
In your place I would give LogicalTreeHelper.GetChildren (this) a chance. It returns a collection of children to Window (this is a handle to Window) Reference MSDN
From there you can try to find your control.
But I think it is easier to try to rewrite the control (or look for another component) so you can have names on the children. That was your problem from the start.
Hope it helps
Gorgen
First, why in the world would you want to do that?
If you do not set a name you have no easy way of accessing the control. However you can get access to the control via relationships to other controls or events that pass a reference, for example the loaded event.
e.g.
private void Menu_Loaded(object sender, RoutedEventArgs e)
{
(sender as Menu).Name = "MainMenu";
}
Or if the control is the child of another control:
(ControlStack.Children[0] as Menu).Name = "MainMenu";
But i cannot think of anything useful that could be achieved by that...
You probably just want to get a reference to the object which you can easily store in a class member. In some cases you can also slice up your XAML using resources.
e.g.
<local:SomethingIWouldLikeToReference x:Key="SomethingIWouldLikeToReference"/>
<local:UserControl x:Name="userControl">
<Stuff>
<MoreStuff Content="{StaticResource SomethingIWouldLikeToReference}"/>
</Stuff>
</local:UserControl>
public MainWindow()
{
InitializeComponent();
MyReference = FindResource("SomethingIWouldLikeToReference") as SomethingIWouldLikeToReference;
}
Example if I have ListView Control and I want to use it to add items and remove items
Make private ListView and initialize it
ListView temp_control_List = new ListView()
then make loaded Eventhandler from Blend so it will be in VS then
private void ListView_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
temp_control_List = sender as ListView;
}
Now you can add and remove to and from the list view control from temp_control_List
I'm trying to create a self-contained Winforms control called DragDropListView. It derives from ListView.
I have code that allows the user to sort list items within the control by dragging and dropping the items in the new location. I achieved that by overriding OnDragDrop, OnDragOver, OnDragEnter, OnItemDrag.
The issue I have is with dragging from one listview to a completely different listview. The event fires on the other list view as expected, but the method doesn't take a "sender" argument, so there's no good way to tell where the items are being dragged from, and no way I can figure out to actually grab the items being dragged. The current code works with stuff like "this.SelectedItems," but I'd like it to be "sender.SelectedItems".
I guess the reason there is no sender argument is that the control isn't supposed to responsible for knowing that much about its environment, and the host Form should handle the interaction between two controls, but I'm trying to build self contained controls that have this functionality, so letting it bleed onto the form isn't going to work.
Ideas?
I think you can know the ListView from the Items by listViewItem.ListView property, Check it.
I didn't test the code:
private void listView1_DragEnter(object sender, DragEventArgs e)
{
if (!e.Data.GetDataPresent(typeof(ListView.ListViewItemCollection)))
{
e.Effect = DragDropEffects.None;
return;
}
var items = (ListView.ListViewItemCollection)e.Data.GetData(typeof(ListView.ListViewItemCollection));
if (items.Count > 0 && items[0].ListView != listView1)
{
e.Effect = DragDropEffects.None;
return;
}
}
Check DragEventArgs , this sample in CodeProject [VB.Net]
Good luck!
I created Drag&Drop mechanism for my TreeView. I added DragEnter, DragDrop and ItemDrag methods and everything works fine.
But when you are doing D&D with standard Windows controls, destination node is highlighted.
Image is worth 1000 words, video probably even more:
http://www.youtube.com/watch?v=PlltSiihHPo
I mean such highlight effect like you can see in this video on Recycle Bin.
That's not a TreeView, it's a ListView with View = LargeIcons. TreeView isn't a great control as a drop target since it hides sub-nodes. But you can solve both problems by implementing the DragOver event. Test where the mouse is at and expand and select the node:
void treeView1_DragOver(object sender, DragEventArgs e) {
var pos = treeView1.PointToClient(new Point(e.X, e.Y));
var hit = treeView1.HitTest(pos);
if (hit.Node != null) {
hit.Node.Expand();
treeView1.SelectedNode = hit.Node;
}
}