2013-07-16 3 views
5

Я переношу свою программу из WinForms в WPF и столкнулся с некоторыми проблемами с перетаскиванием. Это должно позволить перетаскивать из TreeView (это как проводник файла) в текстовое поле, которое открывает файл. Тем не менее, версия WPF будет автоматически копироваться и вставляться в заголовок TreeViewItem. Кажется, я просто что-то перепутал? Возможно, материал DataObject.Портирование WinForms перетаскивание в WPF перетаскивание

Полнофункциональный, соответствующий WinForms код:

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); 
     ... 

код WPF, который должен сделать то же самое:

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? 
} 

Проблема: В моей WPF версии, я устанавливаю текстовое поле-х текст в пустой (как тест), который происходит, но затем вставляется текст заголовка TreeViewItem, который не является тем, что я хочу.

Вопросы: Каков правильный способ переноса этого кода WinForms в WPF? Почему текст вставляется в версию WPF? Как я могу это предотвратить? Я использую правильные события? Как мне получить доступ к DataObject в textbox_Drop, чтобы я мог открыть файл, как я сделал в версии WinForms? Почему узел TreeViewItem всегда имеет значение null в версии WPF?

+0

Это кажется уместным: http://msdn.microsoft.com/en-us/library/hh144798.aspx EDIT: важный бит здесь является то, что 'TextBox 'и все его родственники имеют« дефолтные »реализации для DragDrop, и рекомендуется, чтобы вы просто болтали на них, а не прятали свои собственные. – JerKimball

ответ

1

Проблема: В моем WPF версии, я устанавливаю текст в текстовое поле, чтобы очистить (как испытание), которое происходит, но после этого текст заголовка в TreeViewItem является наклеиванием, который не то, что я хочу.

Я думаю, что элемент родительского интерфейса обрабатывает (и, следовательно, переопределяет) событие Drop, чтобы вы не получали ожидаемые результаты. На самом деле, пытаясь воссоздать вашу проблему, я даже не смог запустить событие TextBox.Drop. Однако, используя событие PreviewDrop TextBox, я смог получить то, что (я думаю) - ваш ожидаемый результат.Попробуйте это:

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 
    } 

Я думаю, что фрагмент кода должен ответить на большинство вопросов, которые вы позировали это один, за исключением:

Почему TreeViewItem узел всегда нуль в версии WPF?

DataObject вы передаете в случае DragDrop не поддерживает прохождение TreeViewItem. В вашем коде (и моем) мы указываем, что формат данных будет DataFormats.StringFormat, который нельзя отнести на TreeViewItem.

+0

Спасибо, мне нужно 'e.Handled = true' и исправить, как я проверял' e.Data'. –

-1

Я использую правильные события ?: Я думаю, что вы используете правильные события, но я думаю, что у вас есть несколько проблем в коде. Предполагаю, что вы установили DataContext своего дерева в реальные предметы, и вы используете привязку.

  1. Как получить доступ к DataObject в textbox_Drop? -> Для получения DataObject вы должны получить реальный предмет рекурсии (других решениями возможных)

    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. Почему текст, вставленный в версии WPF? -> Заголовок отображается, потому что (я предполагаю) это похоже на метод tostring на ваших элементах. Если для сложного элемента привязка не указана, выполняется метод ToString. Попробуйте не устанавливать текст непосредственно в обработчик события перетаскивания. Задайте контекст данных для вашего элемента (к элементу, найденному в 1. точке), а затем укажите путь привязки через XAML. (Для отображения)

+0

Это не отвечает на мои вопросы, и код, который вы копируете и вставляете, не помогает. –

+0

Я добавил к своим ответам на ваши вопросы. – Tintenfiisch

+0

Зачем мне нужен родитель? Я не вижу, как этот код имеет значение, можете ли вы применить его к моему коду? Я устанавливаю текст вручную для тестирования, как вы можете видеть в моем рабочем коде WinForms. Я должен уметь заставить его работать точно так же, как в WPF. –

0

GetFullPath, похоже, выводит неправильное значение. То, что вы хотите перетащить, - Header, и вы можете получить его прямо с item. Также имейте в виду, что приведенный ниже метод связан с MouseMove Event из TreeView.

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); 
    } 
} 

я сделал создать раскрывающееся часть, основываясь на тексте, а не на TreeViewItem (e.Data.GetData(typeof(string)).ToString()), но самое удивительное, что это даже не требуется. Если вы откроете новый проект C# WPF, поместите на нем TreeView и TextBox (, обновите часть XAML) и скопируйте приведенный выше код, вы можете отбросить текст с TreeView в TextBox, не делая ничего! Текст скопирован в TextBox без учета Drop handling.

4

Ах, что, черт возьми, я буду расширять свой комментарий к ответу:

по ссылке, чтобы прочитать, как уже упоминалось, это: http://msdn.microsoft.com/en-us/library/hh144798.aspx

Короткий рассказ, то TextBox -derived управляет уже реализовать большинство «кишок» для основных операций перетаскивания, и рекомендуется, чтобы вы расширили это, а не предоставляли явные обработчики DragEnter/DragOver/Drop.

Предполагая, что дерево «данных» структуры, как:

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

обработчиков может выглядеть примерно так:

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); 

И просто для смеха, вот быстрый и грязный тест приложение, которое это чистое шоу-бои в использовании реактивных расширений (Rx) для перетаскивания:

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> 

Насти код-за (слишком ленив, чтобы 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

У вас есть более чем одна проблема, достаточно, чтобы сделать это трудно. Первая проблема заключается в том, что вы неправильно используете объект перетаскивания, вы перетаскиваете строку, но все еще проверяете TreeViewItem. Просто используйте тот же подход, что и в Winforms, перетаскивая узел. Вторая проблема заключается в том, что TextBox уже реализует поддержку D + D и мешает вашему коду. И причина, по которой вы видели текст, появляется после падения.

Давайте сначала рассмотрим начало перетаскивания. Вам нужно будет сделать дополнительную работу, так как вы начали перетаскивание, препятствуя нормальному использованию TreeView, очень сложно выбрать узел. начать только лобовое сопротивление, когда мышь перемещается достаточно далеко:

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); 
     } 
    } 

Теперь падение, вам нужно будет реализовать обработчик событий DragEnter, DragOver и падения, чтобы избежать по умолчанию D + D поддержка встроена в TextBox от попадания в путь. Установка e.Handled свойство верно необходимо:

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; 
    } 
Смежные вопросы