2015-04-02 2 views
2

Я создаю приложение для Windows Store для приложений/универсального приложения для Windows 8.1 и Windows 10, и я хотел бы иметь возможность перетаскивать элементы между ListViews и иметь возможность позиционировать элемент в конкретное место в ListView. Основная проблема, с которой я сталкиваюсь, заключается в том, что я не могу найти хороший способ определить индекс списка, в котором элемент был удален.Перетаскивание приложения в Windows Store между ListViews

Я нашел образец (XAML ListView reorder), но важным отличием является то, что элементы в моем списке имеют переменную высоту, поэтому простой расчет, который использует этот примерный проект для вывода индекса, не будет работать для меня.

Я могу получить положение x, y, где в ListView элемент был удален, но у меня возникли проблемы с использованием этой позиции, чтобы выяснить индекс. Я нашел упоминания людей, использующих ListView.GetItemAt (x, y) или ListView.HitTest (x, y), но, как выяснили другие, эти методы, похоже, не существуют в приложениях Windows Universal. Я также пытался использовать VisualTreeHelper.FindElementsInHostCoordinates(), но я либо не использую его правильно, либо я не понимаю его цели, потому что я не могу заставить его возвращать результаты.

Вот некоторые пример кода, который я пробовал:

private void ListView_OnDrop(object sender, DragEventArgs e) 
{ 
    var targetListView = (ListView)sender; 

    var positionRelativeToTarget = e.GetPosition(targetListView); 

    var rect = new Rect(positionRelativeToTarget, new Size(10, 15)); 
    var elements = VisualTreeHelper.FindElementsInHostCoordinates(rect, targetListView); 

    // Trying to get the index in the list where the item was dropped 
    // 'elements' is always empty 
} 

Для справки, я использую C#, XAML и Visual Studio 2013.

спасибо!

ответ

1

Я нашел решение, которое достаточно хорошо для моих целей. По сути дела, что я закончил делать, это обрабатывать событие drop как в ListView, так и в элементе списка, потому что легко определить индекс, если падение происходит в элементе списка. Мне все еще приходилось обрабатывать падение в ListView, но для того, когда элемент упал между ними.

Вот код, который я закончил с:

MyView.xaml

<UserControl.Resources>  
    <DataTemplate x:Key="MyItemTemplate"> 
     <local:MyControl AllowDrop="True" Drop="ListItem_OnDrop" /> 
    </DataTemplate> 
</UserControl.Resources> 

<Grid> 
    <ListView ItemTemplate="{StaticResource MyItemTemplate}" 
       CanDragItems="True" AllowDrop="True" 
       DragItemsStarting="ListView_OnDragItemsStarting" 
       DragOver="ListView_OnDragOver" 
       DragLeave="ListView_OnDragLeave" 
       Drop="ListView_OnDrop" /> 
</Grid> 

MyView.xaml.cs

public sealed partial class MyView 
{ 
    private readonly SolidColorBrush listViewDragOverBackgroundBrush = new SolidColorBrush(Color.FromArgb(255, 247, 247, 247)); 

    public MyView() 
    { 
     InitializeComponent(); 
    } 

    private IMyViewModel ViewModel 
    { 
     get { return DataContext as IMyViewModel; } 
    } 

    private void ListView_OnDragItemsStarting(object sender, DragItemsStartingEventArgs e) 
    { 
     e.Data.Properties.Add("dataItem", e.Items[0] as IMyItemViewModel); 
    } 

    private void ListView_OnDragOver(object sender, DragEventArgs e) 
    { 
     var dropTarget = sender as ListView; 
     if (dropTarget == null) 
     { 
      return; 
     } 

     dropTarget.Background = listViewDragOverBackgroundBrush; 
    } 

    private void ListView_OnDragLeave(object sender, DragEventArgs e) 
    { 
     var dropTarget = sender as ListView; 
     if (dropTarget == null) 
     { 
      return; 
     } 

     dropTarget.Background = null; 
    } 

    private void ListView_OnDrop(object sender, DragEventArgs e) 
    { 
     var draggedItem = e.Data.Properties["dataItem"] as IMyItemViewModel; 
     var targetListView = sender as ListView; 

     if (targetListView == null || draggedItem == null) 
     { 
      return; 
     } 

     targetListView.Background = null; 

     var droppedPosition = e.GetPosition(targetListView); 
     var itemsSource = targetListView.ItemsSource as IList; 
     const double extraHeightThatImNotSureWhereItCameFrom = 8d; 
     var highWaterMark = 3d; // This list starts with 3px of padding 
     var dropIndex = 0; 
     var foundDropLocation = false; 

     for (int i = 0; i < itemsSource.Count && !foundDropLocation; i++) 
     { 
      var itemContainer = (ListViewItem)targetListView.ContainerFromIndex(i); 

      highWaterMark = highWaterMark + itemContainer.ActualHeight - extraHeightThatImNotSureWhereItCameFrom; 

      if (droppedPosition.Y <= highWaterMark) 
      { 
       dropIndex = i; 
       foundDropLocation = true; 
      } 
     } 

     if (foundDropLocation) 
     { 
      // Handle the drag/drop at a specific location 
      // DropPosition is an enumeration I made that has Before & After 
      ViewModel.CompleteDragDrop(draggedItem, DropPosition.Before, dropIndex); 
     } 
     else 
     { 
      // Add to the end of the list. Also works for an empty list. 
      ViewModel.CompleteEvidenceDragDrop(draggedItem, DropPosition.After, itemsSource.Count - 1); 
     } 
    } 

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

     var draggedItem = e.Data.Properties["dataItem"] as IMyItemViewModel; 
     var dropTarget = sender as MyControl; 

     if (dropTarget == null || draggedItem == null) 
     { 
      return; 
     } 

     var parentList = dropTarget.Closest<ListView>(); 
     var dropPosition = dropTarget.GetDropPosition(e); 

     parentList.Background = null; 

     ViewModel.CompleteDragDrop(draggedItem, dropPosition, dropTarget.DataContext as IMyItemViewModel); 
    } 
} 

ExtensionMethods

public static class ExtensionMethods 
{ 
    public static T Closest<T>(this DependencyObject obj) where T : DependencyObject 
    { 
     if (obj == null) 
     { 
      return null; 
     } 

     while (true) 
     { 
      var parent = VisualTreeHelper.GetParent(obj); 

      if (parent == null) 
      { 
       return null; 
      } 

      if (parent.GetType() == typeof(T)) 
      { 
       return (T)parent; 
      } 

      obj = parent; 
     } 
    } 

    public static DropPosition GetDropPosition(this FrameworkElement dropTarget, DragEventArgs e) 
    { 
     var positionRelativeToTarget = e.GetPosition(dropTarget); 

     var dropBefore = positionRelativeToTarget.Y < (dropTarget.ActualHeight/2); 

     return dropBefore ? DropPosition.Before : DropPosition.After; 
    } 
} 
5

Я нашел решение. Я разработал Info-класс, чтобы восстановить индекс, где я уронил новый элемент.

public class Info 
{ 
    public int index { get; set; } 
    public string color { get; set; } 
} 

Я тогда определен мой наблюдаемым:

ObservableCollection<Info> c = new ObservableCollection<Info>(); 
c.Add(new Info { color = "#d9202b", index = 0 }); c.Add(new Info { color = "#ffffff", index = 1 }); 
c.Add(new Info { color = "#15c23c", index = 2 }); c.Add(new Info { color = "#c29b8f", index = 3 }); 
c.Add(new Info { color = "#0000ff", index = 4 }); c.Add(new Info { color = "#deba83", index = 5 }); 

Я также определил другую коллекцию (c2) таким же образом.Для этого Senario я перетащить элемент из второго набора (с2) и я ее падением в первом (с) Так что для dragstarted я использовал это:

private void x2_DragItemsStarting(object sender, DragItemsStartingEventArgs e) 
{ 
    strin = e.Items.FirstOrDefault() as Info; 

    e.Data.Properties.Add("item", strin); 
    e.Data.Properties.Add("gridSource", sender); 
} 

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

private void x_Drop(object sender, DragEventArgs e) 
{ 
    object gridSource; 
    e.Data.Properties.TryGetValue("gridSource", out gridSource); 
    if (gridSource == sender) 
     return; 
    object sourceItem; 
    e.Data.Properties.TryGetValue("item", out sourceItem); 
    //recuperate Info about place of dropped item 
    Info p = ((FrameworkElement)e.OriginalSource).DataContext as Info; 

    if(p==null) 
    { 
     //its not dropped over an item, lets add it in the end of the collection 
     c2.Remove(sourceItem as Info); 
     c.Add(sourceItem as Info); 
    } 
    else 
    { 
     //here we have information that we need 
     c2.Remove(sourceItem as Info); 
     c.Insert(p.index, sourceItem as Info); 
     //c.Add(strin); 
    } 
    Reorder(); 
} 

Тогда мы должны установить индекс для новых элементов в методе Упорядочивание

private void Reorder() 
{ 
    for (int i = 0; i < c.Count; i++) 
     c[i].index = i; 
    for (int i = 0; i < c2.Count; i++) 
     c2[i].index = i; 
} 
+0

Благодарим за отправку решения, но это не решит мою проблему. Основная проблема, с которой я столкнулся, заключалась в том, что при переходе между элементами списка, над которыми вы работали, он сказал, что «он не упал над элементом, не добавил его в конец коллекции» – joelfp

+0

В любом случае, вы сохранили мой день. Благодаря! –

0

I е вы используете DataContext как часть вашего процесса список населения, то вы можете просто сделать следующее:

private void x_Drop(object sender, DragEventArgs e) 
{ 
    MyDataModel model = (sender as FrameworkElement).DataContext as MyDataModel; 
    // ... 
0

Я делаю это, чтобы получить целевой ListViewItem на сопротивление по событию. Он должен работать как по спискам, так и по сетке.

private YOURITEMCLASS _dragTarget; 

private void ItemListView_OnDragOver(object sender, DragEventArgs e) 
{ 
    var pos = e.GetPosition(this); 
    // Offset position by left and top borders if in split view control 

    var elements = VisualTreeHelper.FindElementsInHostCoordinates(pos, this); 
    foreach (var element in elements) 
    { 
     var cellItem = element as ContentControl; 
     var item = cellItem?.Content as YOURITEMCLASS; 
     if (item == null) continue; 
     _dragTarget = item; 
     break; 
    } 
} 
Смежные вопросы