2013-07-16 22 views
5

Ich portiere mein Programm von WinForms zu WPF und habe Probleme mit Drag & Drop bekommen. Es sollte das Ziehen von einem TreeView (es ist wie ein Datei-Explorer) zu einem Textfeld, das die Datei öffnet, ermöglichen. Die WPF-Version fungiert jedoch wie ein Kopieren und Einfügen des Header-Texts TreeViewItem automatisch. Ich denke, ich habe gerade etwas durcheinander gebracht? Möglicherweise die DataObject Sachen.Portierung von WinForms Drag & Drop auf Drag & Drop von WPF Drag & Drop

Die voll funktionsfähig, relevant WinForms-Code:

private void treeView1_MouseMove(object sender, MouseEventArgs e) 
{ 
    if (e.Button != MouseButtons.Left) return; 
    TreeNode node = treeView1.GetNodeAt(e.Location); 
    if (node != null) treeView1.DoDragDrop(node, DragDropEffects.Move); 
} 

textbox[i].DragDrop += (o, ee) => 
{ 
    if (ee.Data.GetDataPresent(typeof(TreeNode))) 
    { 
     TreeNode node = (TreeNode)ee.Data.GetData(typeof(TreeNode)); 
     ((Textbox)o).Text = File.ReadAllLines(pathRoot + node.Parent.FullPath); 
     ... 

Der Code WPF, die das gleiche tun sollten:

private void TreeView_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) 
{ 
    TreeViewItem item = e.Source as TreeViewItem; 
    if (item != null) 
    { 
     DataObject dataObject = new DataObject(); 
     dataObject.SetData(DataFormats.StringFormat, GetFullPath(item)); 
     DragDrop.DoDragDrop(item, dataObject, DragDropEffects.Move); 
    } 
} 

//textbox[i].PreviewDrop += textbox_Drop; 
private void textbox_Drop(object sender, DragEventArgs e) 
{ 
    TreeViewItem node = (TreeViewItem)e.Data.GetData(typeof(TreeViewItem)); //null? 
    ((Textbox)sender).Text = ""; 
    //this is being executed BUT then the node's header text is being pasted 
    //also, how do I access the DataObject I passed? 
} 

Problem: In meiner WPF-Version, ich bin die Textbox der Einstellung Text zu leeren (als Test), der auftritt, aber danach wird der TreeViewItem-Header-Text eingefügt, was nicht das ist, was ich will.

Fragen: Was ist der richtige Weg, um diesen WinForms-Code nach WPF zu portieren? Warum wird der Text in der WPF-Version eingefügt? Wie verhindere ich das? Benutze ich die richtigen Ereignisse? Wie kann ich auf die DataObject in textbox_Drop zugreifen, damit ich die Datei wie in der WinForms-Version öffnen kann? Warum ist der TreeViewItem-Knoten in der WPF-Version immer null?

+0

Diese relevant erscheint: http://msdn.microsoft.com/en-us/library/hh144798.aspx EDIT: Das wichtige Bit ist hier, dass 'TextBox 'und alle seine Verwandten haben" Standard "-Implementierungen für DragDrop, und es wird empfohlen, dass Sie nur auf diese schrauben, anstatt Ihre eigenen zu drehen. – JerKimball

Antwort

1

Problem: In meiner WPF-Version, ich bin die Textbox Text Einstellung (als Test) zu entleeren, die auftritt, dann aber die TreeViewItem Header Text eingefügt wird, das ist, nicht was ich will.

Ich denke, ein übergeordnete UI-Element ist der Umgang mit (und daher zwingend) dem Drop Ereignisse so dass Sie nicht immer die Ergebnisse, die Sie erwarten. In der Tat, wenn ich versuche, Ihr Problem neu zu erstellen, konnte ich nicht einmal mein TextBox.Drop-Ereignis auslösen. Mit dem PreviewDrop-Ereignis der TextBox konnte ich jedoch das erwartete Ergebnis erzielen.Versuchen Sie folgendes:

private void textBox1_PreviewDrop(object sender, DragEventArgs e) 
    { 
     TextBox tb = sender as TextBox; 
     if (tb != null) 
     { 
      // If the DataObject contains string data, extract it. 
      if (e.Data.GetDataPresent(DataFormats.StringFormat)) 
      { 
       string fileName = e.Data.GetData(DataFormats.StringFormat) as string; 
       using (StreamReader s = File.OpenText(fileName)) 
       { 
        ((TextBox)sender).Text = s.ReadToEnd(); 
       } 
      } 
     } 
     e.Handled = true; //be sure to set this to true 
    } 

denke ich, dass Code-Schnipsel die meisten Fragen beantworten, sollten Sie bis auf diese gestellt ein:

Warum ist TreeViewItem Knoten immer null in der WPF-Version?

Die DataObject Sie im Drag & Drop-Ereignis vorbei unterstützt keine TreeViewItem vorbei. In Ihrem Code (und meins) geben wir an, dass das Datenformat DataFormats.StringFormat ist, das nicht in eine TreeViewItem umgewandelt werden kann.

+0

Danke, ich brauchte 'e.Handled = true' und um zu beheben, wie ich' e.Data' überprüfte. –

-1

Verwende ich die richtigen Ereignisse ?: Ich denke, dass Sie die richtigen Ereignisse verwenden, aber ich denke, dass Sie mehrere Probleme in Ihrem Code haben. Ich nehme an, Sie haben den DataContext Ihrer Treeview auf die realen Elemente gesetzt und Sie verwenden Binding.

  1. Wie kann ich auf das DataObject in textbox_Drop zugreifen? -> für das Erhalten des Datenobjekts müssen Sie das reale Einzelteil erhalten durch Rekursion (andere Lösungen möglich)

    DependencyObject k = VisualTreeHelper.HitTest(tv_treeView, DagEventArgs.GetPosition(lv_treeView)).VisualHit; 
    
    while (k != null) 
        { 
         if (k is TreeViewItem) 
         { 
          TreeViewItem treeNode = k as TreeViewItem; 
    
          // Check if the context is your desired type 
          if (treeNode.DataContext is YourType) 
          { 
           // save the item 
           targetTreeViewItem = treeNode; 
    
           return; 
          } 
         } 
         else if (k == tv_treeview) 
         { 
          Console.WriteLine("Found treeview instance"); 
          return; 
         } 
    
         // Get the parent item if no item from YourType was found 
         k = VisualTreeHelper.GetParent(k); 
        } 
    
  2. Warum wird der Text in der WPF-Version eingefügt werden? -> Der Header wird angezeigt, weil (ich nehme an) es ist wie die Tostring-Methode auf Ihre Artikel. Wenn die Bindung für ein komplexes Element nicht angegeben ist, wird die ToString-Methode ausgeführt. Versuchen Sie, den Text nicht direkt im Handler des Ablageereignisses festzulegen. Setzen Sie den Datenkontext auf Ihr Element (auf das Element, das Sie in Punkt 1 gefunden haben) und geben Sie den Bindungspfad über XAML an. (Zur Anzeige)

+0

Das beantwortet meine Fragen nicht und der Code, den Sie kopieren und einfügen, ist nicht hilfreich. –

+0

Ich habe zu meinen Antworten Ihre Fragen hinzugefügt. – Tintenfiisch

+0

Warum brauche ich die Eltern? Ich sehe nicht, wie der Code relevant ist, können Sie ihn auf meinen Code anwenden? Ich stelle den Text zum Testen manuell ein, wie Sie in meinem funktionierenden WinForms-Code sehen können. Ich sollte es genauso machen können, nur in WPF. –

0

GetFullPath scheint einen falschen Wert auszugeben. Was Sie ziehen/ablegen möchten, ist die Header und Sie können es direkt von item erhalten. Beachten Sie auch, dass die folgende Methode mit der MouseMove Event der TreeView verknüpft ist.

private void TreeView_MouseMove(object sender, MouseButtonEventArgs e) 
{ 
    if (e.LeftButton != MouseButtonState.Pressed) return; 
    TreeViewItem item = e.Source as TreeViewItem; 
    if (item != null) 
    { 
     DataObject dataObject = new DataObject(); 
     dataObject.SetData(DataFormats.StringFormat, item.Header); 
     DragDrop.DoDragDrop(item, dataObject, DragDropEffects.Move); 
    } 
} 

Ich habe den Fallteil eher auf textbasierte schaffen als auf den TreeViewItem (e.Data.GetData(typeof(string)).ToString()), aber das Erstaunlichste daran ist, dass es nicht einmal erforderlich ist. Wenn Sie ein neues C# -WPF-Projekt öffnen, setzen Sie eine TreeView und eine TextBox darauf (, aktualisieren Sie den XAML-Teil) und kopieren Sie den obigen Code, können Sie Text aus der TreeView in die TextBox ohne etwas anderes zu tun !! Der Text wird in die TextBox ohne Berücksichtigung der Drop handling kopiert.

4

Ach, was solls, ich werde meinen Kommentar zu einer Antwort erweitern:

Der Link zu lesen, wie bereits erwähnt, ist dies: http://msdn.microsoft.com/en-us/library/hh144798.aspx

Kurzgeschichte, die TextBox stammenden Kontrollen bereits Implementieren Sie die meisten "Eingeweide" für grundlegende Drag/Drop-Vorgänge, und es wird empfohlen, dass Sie das erweitern, anstatt explizite DragEnter/DragOver/Drop Handler bereitzustellen.

einen Baum „Daten“ Struktur wie Unter der Annahme:

public class TreeThing 
{ 
    public string Description { get; set; } 
    public string Path { get; set; } 
} 

die Handler wie folgt aussehen könnte:

this.tb.AddHandler(UIElement.DragOverEvent, new DragEventHandler((sender, e) => 
    { 
     e.Effects = !e.Data.GetDataPresent("treeThing") ? 
      DragDropEffects.None : 
      DragDropEffects.Copy;      
    }), true); 

this.tb.AddHandler(UIElement.DropEvent, new DragEventHandler((sender, e) => 
{ 
    if (e.Data.GetDataPresent("treeThing")) 
    { 
     var item = e.Data.GetData("treeThing") as TreeThing; 
     if (item != null) 
     { 
      tb.Text = item.Path; 
      // TODO: Actually open up the file here 
     } 
    } 
}), true); 

Und nur für kichert, hier eine schnelle und unsaubere Test-Anwendung, die ist reine showboating Verwendung der Reactive Extensions (Rx) für den Schleppstart Zeug auf:

XAML:

<Window x:Class="WpfApplication1.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="350" Width="525"> 
    <Grid> 
     <Grid.ColumnDefinitions> 
      <ColumnDefinition/> 
      <ColumnDefinition/> 
     </Grid.ColumnDefinitions> 
     <TreeView x:Name="tree" Grid.Column="0" ItemsSource="{Binding TreeStuff}" DisplayMemberPath="Description"/> 
     <TextBox x:Name="tb" Grid.Column="1" AllowDrop="True" Text="Drop here" Height="30"/> 
    </Grid> 
</Window> 

Nasty Code-Behind (zu faul, das MVVM):

using System; 
using System.Collections.ObjectModel; 
using System.ComponentModel; 
using System.Reactive.Linq; 
using System.Runtime.CompilerServices; 
using System.Windows; 
using System.Windows.Controls; 
using System.Windows.Input; 
using System.Windows.Media; 

namespace WpfApplication1 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window, INotifyPropertyChanged 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
      TreeStuff = new ObservableCollection<TreeThing>() 
       { 
        new TreeThing() { Description="file 1", Path = @"c:\temp\test.txt" }, 
        new TreeThing() { Description="file 2", Path = @"c:\temp\test2.txt" }, 
        new TreeThing() { Description="file 3", Path = @"c:\temp\test3.txt" }, 
       }; 

      var dragStart = 
       from mouseDown in 
        Observable.FromEventPattern<MouseButtonEventHandler, MouseEventArgs>(
         h => tree.PreviewMouseDown += h, 
         h => tree.PreviewMouseDown -= h) 
       let startPosition = mouseDown.EventArgs.GetPosition(null) 
       from mouseMove in 
        Observable.FromEventPattern<MouseEventHandler, MouseEventArgs>(
         h => tree.MouseMove += h, 
         h => tree.MouseMove -= h) 
       let mousePosition = mouseMove.EventArgs.GetPosition(null) 
       let dragDiff = startPosition - mousePosition 
       where mouseMove.EventArgs.LeftButton == MouseButtonState.Pressed && 
        (Math.Abs(dragDiff.X) > SystemParameters.MinimumHorizontalDragDistance || 
        Math.Abs(dragDiff.Y) > SystemParameters.MinimumVerticalDragDistance) 
       select mouseMove; 

      dragStart.ObserveOnDispatcher().Subscribe(start => 
       { 
        var nodeSource = this.FindAncestor<TreeViewItem>(
         (DependencyObject)start.EventArgs.OriginalSource); 
        var source = start.Sender as TreeView; 
        if (nodeSource == null || source == null) 
        { 
         return; 
        } 
        var data = (TreeThing)source 
         .ItemContainerGenerator 
         .ItemFromContainer(nodeSource); 
        DragDrop.DoDragDrop(nodeSource, new DataObject("treeThing", data), DragDropEffects.All); 
       }); 

      this.tb.AddHandler(UIElement.DragOverEvent, new DragEventHandler((sender, e) => 
       { 
        e.Effects = !e.Data.GetDataPresent("treeThing") ? 
         DragDropEffects.None : 
         DragDropEffects.Copy;      
       }), true); 

      this.tb.AddHandler(UIElement.DropEvent, new DragEventHandler((sender, e) => 
      { 
       if (e.Data.GetDataPresent("treeThing")) 
       { 
        var item = e.Data.GetData("treeThing") as TreeThing; 
        if (item != null) 
        { 
         tb.Text = item.Path; 
         // TODO: Actually open up the file here 
        } 
       } 
      }), true); 
      this.DataContext = this; 
     } 


     private T FindAncestor<T>(DependencyObject current) 
      where T:DependencyObject 
     { 
      do 
      { 
       if (current is T) 
       { 
        return (T)current; 
       } 
       current = VisualTreeHelper.GetParent(current); 
      } 
      while (current != null); 
      return null; 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 

     public ObservableCollection<TreeThing> TreeStuff { get; set; } 

     protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) 
     { 
      PropertyChangedEventHandler handler = PropertyChanged; 
      if (handler != null) 
      { 
       handler(this, new PropertyChangedEventArgs(propertyName)); 
      } 
     } 
    } 

    public class TreeThing 
    { 
     public string Description { get; set; } 
     public string Path { get; set; } 
    } 
} 
3

Sie haben mehr als ein Problem, genug, um diese schwer zu machen. Das erste Problem ist, dass Sie das Ziehobjekt falsch gelesen haben. Sie ziehen eine Zeichenfolge, suchen aber immer noch nach einem TreeViewItem. Verwenden Sie einfach denselben Ansatz wie in Winforms und ziehen Sie den Knoten. Das zweite Problem ist, dass TextBox bereits die D + D-Unterstützung implementiert und das Ihrem Code im Weg steht. Und der Grund, warum du den Text gesehen hast, zeigt sich nach dem Drop.

Lassen Sie uns den Beginn des Ziehens zuerst angehen. Sie müssen etwas mehr Arbeit verrichten, da die Art und Weise, wie Sie mit dem Ziehen begonnen haben, die normale Verwendung von TreeView beeinträchtigt. Es wird sehr schwierig, einen Knoten auszuwählen. Nur die Drag starten, wenn die Maus weit genug bewegt wurde:

private Point MouseDownPos; 

    private void treeView1_PreviewMouseDown(object sender, MouseButtonEventArgs e) { 
     MouseDownPos = e.GetPosition(treeView1); 
    } 

    private void treeView1_PreviewMouseMove(object sender, MouseEventArgs e) { 
     if (e.LeftButton == MouseButtonState.Released) return; 
     var pos = e.GetPosition(treeView1); 
     if (Math.Abs(pos.X - MouseDownPos.X) >= SystemParameters.MinimumHorizontalDragDistance || 
      Math.Abs(pos.Y - MouseDownPos.Y) >= SystemParameters.MinimumVerticalDragDistance) { 
      TreeViewItem item = e.Source as TreeViewItem; 
      if (item != null) DragDrop.DoDragDrop(item, item, DragDropEffects.Copy); 
     } 
    } 

nun den Tropfen, müssen Sie die Dragenter, Dragover and Drop Event-Handler implementieren, um den Standard zu vermeiden D + D-Unterstützung in TextBox gebaut davon ab, in der Weg. Einstellen der e.Handled Eigenschaft auf true ist notwendig:

private void textBox1_PreviewDragEnter(object sender, DragEventArgs e) { 
     if (e.Data.GetDataPresent(typeof(TreeViewItem))) e.Effects = e.AllowedEffects; 
     e.Handled = true; 
    } 

    private void textBox1_PreviewDrop(object sender, DragEventArgs e) { 
     var item = (TreeViewItem)e.Data.GetData(typeof(TreeViewItem)); 
     textBox1.Text = item.Header.ToString(); // Replace this with your own code 
     e.Handled = true; 
    } 

    private void textBox1_PreviewDragOver(object sender, DragEventArgs e) { 
     e.Handled = true; 
    }