WPF, Проблема с Drag&Drop в TreeView (криво забайндил?)

3

Добрый день! Сел осваивать drag&drop в WPF. Нашел неплохой пример: http://www.wpftutorial.net/DragAndDrop.html Итак, у меня есть коллекция объектов типа Dimension, внутри каждого находится другая коллекция объектов типа Measure. Написал следующий XAML:

<TreeView PreviewMouseLeftButtonDown="treeView1_PreviewMouseLeftButtonDown" PreviewMouseMove="treeView1_PreviewMouseMove">
  <TreeView.Resources>
    <HierarchicalDataTemplate DataType="{x:Type qb:Dimension}" ItemsSource="{Binding Measures}">
        <TextBlock Margin="10,0,0,0" Text="{Binding Name}"></TextBlock>
    </HierarchicalDataTemplate>
    <HierarchicalDataTemplate DataType="{x:Type qb:Measure}">
      <TextBlock Margin="10,0,0,0" Text="{Binding Name}"></TextBlock>
    </HierarchicalDataTemplate>
  </TreeView.Resources>
</TreeView>

Далее, в соответствии с примером написал следующую функцию:

private void treeView1_PreviewMouseMove(object sender, MouseEventArgs e)
{
  Point mousePos = e.GetPosition(null);
  Vector diff = _DragStart - mousePos;

  if (e.LeftButton == MouseButtonState.Pressed &&
    Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance &&
    Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance)
  {
    TreeView treeView = sender as TreeView;
    TreeViewItem treeViewItem =
      FindAnchestor<TreeViewItem>((DependencyObject)e.OriginalSource);

    if (treeViewItem != null)
    {
      // если перетащить корневые элементы, то получаем, как и ожидалось, объект Dimension
      // если перетащить элементы второго уровня, то здесь оказывается DependencyProperty.UnsetValue
      object item = treeView.ItemContainerGenerator.
        ItemFromContainer(treeViewItem);

      if (item is Measure)
      {
        DataObject dragData = new DataObject("Measure", item);
        DragDrop.DoDragDrop(treeViewItem, dragData, DragDropEffects.Move);
      }
    }
  }

Выходит, что когда я пытаюсь перетащить элементы первого уровня (корневые), то в переменной item получаю соответствующий объект типа Dimension. Но если попробовать перетянуть объект второго уровня, то в item лежит DependencyProperty.UnsetValue. Подозреваю, что я криво забайндил, хотя визуально все хорошо.

Демо-проект находится тут: http://www.sendspace.com/file/l1rdw9

GarF1eld2

не могу зайти с OpenId как GarF1eld, получаю ошибку 500.

Help

Спасибо за замечание с OpenID. Оказавается, с недавних пор (после изменений на стороне Yahoo) наша OpenID-библиотека перестала корректно работать с Yahoo-провайдером. В ближайшие дни это будет исправлено.

Лучший ответ:

3

На самом деле все оказалось довольно логично: Дело в том, что ItemsSource.ItemContainerGenerator.ItemFromContainer производит поиск в своих элементах. Он не знает, что может содержать элементы, также производные от ItemsControl (как TreeViewItem). Поэтому для решения задачи нужно вызывать ItemContainerGenerator.ItemFromContainer у родительского элемента первого уровня.

private void treeView1_PreviewMouseMove(object sender, MouseEventArgs e)
{
  Point mousePos = e.GetPosition(null);
  Vector diff = _DragStart - mousePos;

  if (e.LeftButton == MouseButtonState.Pressed &&
    Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance &&
    Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance)
  {
    TreeView treeView = sender as TreeView;
    TreeViewItem treeViewItem =
      FindAnchestor<TreeViewItem>((DependencyObject)e.OriginalSource);

    if (treeViewItem != null)
    {
      // ищем родительский элемент
      ItemsControl parent = FindParent<ItemsControl>(treeViewItem);

      object item = parent.ItemContainerGenerator.
        ItemFromContainer(treeViewItem);

      if (item is Measure)
      {
        DataObject dragData = new DataObject("Measure", item);
        DragDrop.DoDragDrop(treeViewItem, dragData, DragDropEffects.Move);
      }
    }
  }

И еще один хелпер

private static T FindParent<T>(FrameworkElement current)
      where T : FrameworkElement
    {
        do
        {
            current = VisualTreeHelper.GetParent(current) as FrameworkElement;
            if (current is T)
            {
                return (T)current;
            }
        }
        while (current != null);
        return null;
    }

Новые ответы

Уникальное предложение: Hiblow 40 на сайте EkoTopas.ru
Новые Лучшие

1

Обратите внимания на данный D&D фреймворк. Так же прицепляю архив с примером использования.

3

В свое время тоже искал туториал по организации drag&drop'а в WPF и, если не ошибаюсь, сталкивался с той же проблемой, что и у вас. С байндингом у вас вроде все нормально, вы же видите в дереве элементы второго уровня. В итоге я использовал данную статью. Тут рассматривается более гибкое повторно используемое решение. С некоторыми модификациями использую его и сейчас. Взгляните, быть может это вариант понравится вам больше.


v1.7.123.556
© 2009—2010 CodeHelper FAQ | О сайте | Обратная связь | История изменений | Статьи
Creative Commons LicenseМатериалы сайта распространяются под лицензией Creative Commons Attribution-Share Alike 3.0 Unported.