2013-07-16 14 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バージョンに貼り付けられるのはなぜですか?それをどうやって防ぐのですか?正しいイベントを使用していますか? textbox_DropDataObjectにアクセスして、私がWinFormsバージョンで行ったようにファイルを開くことができますか? WPFバージョンでTreeViewItemノードが常にnullになるのはなぜですか?

+0

:EDIT http://msdn.microsoft.com/en-us/library/hh144798.aspx:ここで重要なのビットがあること 'テキストボックスであります'と、そのすべての親はDragDropのための"デフォルト "の実装を持っており、あなた自身のスピンではなくむしろそれらにボルトで固定することをお勧めします。 – JerKimball

答えて

1

問題:私のWPFのバージョンでは、私は(テストのように)空にするために、テキストボックスのテキストを設定しています、発生したが、その後ツリービューアイテムのヘッダーテキストがある貼り付けられています私が望むものではありません。

私は、親のUI要素がDropイベントを処理している(オーバーライドしている)ので、期待した結果が得られないと思います。実際問題として、問題を再現しようとすると、TextBox.Dropイベントを起動することさえできませんでした。しかし、TextBoxのPreviewDropイベントを使用すると、私はあなたの期待した結果が得られました。これを試してみてください:

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 
    } 

私はそのコードスニペットは、あなたはこの1つを除いて提起した質問のほとんどに答えるべきだと思う:

なぜツリービューアイテムのノードは、WPFのバージョンでは常にnullですか?

DataObjectあなたがTreeViewItemを渡すことをサポートしていませんDragDropイベントに渡しています。あなたのコード(と私)で、データ形式がDataFormats.StringFormatで、TreeViewItemにキャストできないことを指定します。

+0

ありがとう、私は 'e.Handled = true'が必要で、' e.Data'をどうチェックしていたのかを修正しました。 –

-1

正しいイベントを使用していますか?: あなたは正しいイベントを使用していると思いますが、コードにいくつか問題があると思います。 あなたのツリービューのDataContextを実際のアイテムに設定し、バインディングを使用しているとします。

  1. textbox_DropでDataObjectにアクセスするにはどうすればよいですか? - > のDataObjectを取得するためのテキストは、WPFのバージョンに貼り付けられているのはなぜ再帰(他のソリューションも可能)

    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. により、実際のアイテムを取得する必要がありますか? - > あなたのアイテムのtostringメソッドのようなものなので、ヘッダーが表示されます。複合アイテムの場合、バインディングが指定されていない場合、ToStringメソッドが実行されます。 ドロップイベントのハンドラにテキストを直接設定しないでください。アイテムにデータコンテキストを設定し(1ポイントで見つかったアイテムに)、XAML経由でバインディングパスを指定します。 (表示用)

+0

これは私の質問には答えず、コピーして貼り付けたコードは役に立ちません。 –

+0

私は私の答えにあなたの質問を追加しました。 – Tintenfiisch

+0

なぜ親を取得する必要がありますか?コードがどのように関係しているのかわかりません。コードに適用できますか?私はテスト用に手動でテキストを設定しています。これは、作業中のWinFormsコードで確認できます。 WPFだけで、同じように動作させることができるはずです。 –

0

GetFullPathが間違った値を出力しているようです。ドラッグ/ドロップする項目はHeaderで、itemから直接取得できます。また、以下の方法は、TreeViewMouseMove Eventに関連付けられていることにも注意してください。

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

私は、テキスト上ではなく、TreeViewIteme.Data.GetData(typeof(string)).ToString())に基づいて、ドロップ部分を作成しましたが、最も驚くべきことは、それがさえ必要とされていないということです。新しいC#WPFプロジェクトを開き、TreeViewTextBoxを入れて(XAML部分を更新して)上記のコードをコピーすると、TreeViewのテキストをTextBoxにドロップすることができます。テキストはDrop handlingを考慮せずにTextBoxにコピーされます。

4

ああ、一体何、私は答えに私のコメントを広げます: http://msdn.microsoft.com/en-us/library/hh144798.aspx

短編小説、すでにTextBox由来のコントロール:述べたように、読むために

リンク、これです基本的なドラッグ&ドロップ操作のための大部分の "勇気"を実装しています。明示的な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)の使用で純粋なshowboatingです:

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で使用したのと同じアプローチを使用して、ノードをドラッグします。 2番目の問題は、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); 
     } 
    } 

ドロップし、あなたがになってからのTextBoxに組み込まれているデフォルトD + Dサポートを避けるために、DragEnterイベント、DragOver DragOverはドロップイベントハンドラを実装する必要があります道。 trueに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; 
    } 
関連する問題