【问题标题】:Drag and Drop in UWP in list of bank accounts在银行帐户列表中的 UWP 中拖放
【发布时间】:2018-01-04 11:16:34
【问题描述】:

我有一个本地银行的通用 Windows 应用程序,我正在处理汇款视图,他们需要使用 UWP 应用程序中的拖放功能从一个帐户转移到另一个帐户。

我已经制作了动画部分,但是在将列表项放到“Account To”列表后我需要帮助。

我会附上一个截图来说明清楚。

正如您在图片中看到的,我需要从“来自帐户”列表中拖动一项并将其仅放在“至帐户”列表中的一项上。我怎样才能做到这一点?

【问题讨论】:

  • 您应该对下面的答案提供反馈...有了反馈,我们也许能够真正帮助您解决问题。

标签: c# uwp drag-and-drop win-universal-app windows-10-universal


【解决方案1】:

我创建了 a small sample,它显示了两个 ListViews 之间的拖放,其中填充了一些 Accounts。我将跳过 UserControls 的实现 - Page xaml 如下所示:

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <Grid.RowDefinitions>
        <RowDefinition Height="200"/>
        <RowDefinition Height="200"/>
    </Grid.RowDefinitions>

    <ListView Header="Source" Margin="10" Grid.Row="0" CanDragItems="True" ItemsSource="{x:Bind Accounts}" SelectionMode="None">
        <ListView.ItemsPanel>
            <ItemsPanelTemplate>
                <ItemsStackPanel Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </ListView.ItemsPanel>
        <ListView.ItemTemplate>
            <DataTemplate>
                <controls:AccountControl CanDrag="True" DragStarting="AccountControl_DragStarting"/>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>

    <ListView Header="Targets" Margin="10" Grid.Row="1" ItemsSource="{x:Bind Accounts}" SelectionMode="None">
        <ListView.ItemsPanel>
            <ItemsPanelTemplate>
                <ItemsStackPanel Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </ListView.ItemsPanel>
        <ListView.ItemTemplate>
            <DataTemplate>
                <controls:AccountControl AllowDrop="True" DragEnter="AccountControl_DragEnter" Drop="AccountControl_Drop"/>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</Grid>

如您所见,有一个 Source 列表,其中控件在被拖动时触发了一个事件。

private void AccountControl_DragStarting(UIElement sender, DragStartingEventArgs args)
{
    if ((sender as AccountControl)?.DataContext is Account account)
    {
        args.AllowedOperations = DataPackageOperation.Link;
        args.Data.SetData(accountId, account.Id);
    }
}

Account 类除了 name 和 balance 之外还有一个 Guid 标识符,因此我可以使用它来传递在 transfer 方法中使用了哪个源帐户的信息。

第二个列表中的项目(Targets)只接受放置操作,并为此触发两个事件:

private void AccountControl_DragEnter(object sender, DragEventArgs e)
{
    e.AcceptedOperation = DataPackageOperation.Link;
    e.DragUIOverride.Caption = "Transfer";
}

private async void AccountControl_Drop(object sender, DragEventArgs e)
{
    if ((e.OriginalSource as AccountControl)?.DataContext is Account targetAccount)
        if (await (e.DataView.GetDataAsync(accountId)) is Guid sourceAccountId)
        {
            var sourceAccount = Accounts.First(x => x.Id == sourceAccountId);
            sourceAccount.Balance -= 1000;
            targetAccount.Balance += 1000;
        }
}

第一个为用户设置接受的操作和一些信息。第二个将一些钱从一个帐户“转移”到第二个帐户。

一切看起来像这样:

您可以在MS directlyother articleMS samples repository 中找到更多帮助。

【讨论】:

  • 谢谢罗马兹!现在像魅力一样工作,这就是我想要的。
【解决方案2】:

我对我将提供的“解决方案”并不完全满意。它们很可能与理想的实现相去甚远,但是...

我创建的XAML 代码试图轻松地复制,但也始终如一地复制您的对象,包括一组可拖动的RectanglesStackPanel 控件内,加上另一个StackPanel 控件可以在哪里被拖进去。

     <Grid>
        <Grid.Resources>
            <Style TargetType="Rectangle">
                <Setter Property="Width" Value="300"/>
                <Setter Property="Height" Value="300"/>
                <Setter Property="CanDrag" Value="True"/>
            </Style>
        </Grid.Resources>

        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <StackPanel Name="StackPanelRectangles"  Grid.Row="0" Orientation="Horizontal">
            <Rectangle x:Name="RedRect" Fill="Red" DragStarting="Rectangle_DragStarting"/>
            <Rectangle x:Name="GreenRect" Fill="Green" DragStarting="Rectangle_DragStarting"/>
            <Rectangle x:Name="BlueRect" Fill="Blue" DragStarting="Rectangle_DragStarting"/>
        </StackPanel>

        <StackPanel Name="StackPanelDropArea" Background="Azure" AllowDrop="True" 
                    DragOver="StackPanel_DragOver" Drop="StackPanel_Drop" 
                    Grid.Row="2" Orientation="Horizontal"
                    HorizontalAlignment="Center">
            <TextBlock>Drop anywhere in this area area</TextBlock>
        </StackPanel> 
    </Grid>

第一种解决方案:

我将多个Rectangles 的每个DragStarting 事件路由到同一个EventHandler。在这个 EventHandler 中,我们可以访问被拖动的 UIElement,因此在您的 Page 类中使用 UIElement 类型的公开属性,您可以在需要删除它时简单地克隆必要的属性,就像这样:

UIElement dragItem;
private void Rectangle_DragStarting(UIElement sender, DragStartingEventArgs args)
{
   dataPackage.RequestedOperation = DataPackageOperation.Copy;
   dragItem = sender;
}

然后,当项目被删除时调用 EventHandler,我只需将它添加到我的 DropArea 中。

 private void StackPanel_Drop(object sender, DragEventArgs e)
 {
    Rectangle newElement = new Rectangle();
    newElement.Width =  (dragItem as Rectangle).Width;
    newElement.Height = (dragItem as Rectangle).Height;
    newElement.Fill = (dragItem as Rectangle).Fill;
    StackPanelDropArea.Children.Add(newElement);
 }

您不能通过设置引用被拖动的对象来添加新控件,因为当您尝试将控件添加到不同的容器时,有一些属性(例如相应的 Parent)会引发异常。

第二个解决方案: 我非常专注于使用DataPackage 对象及其支持的默认格式之一,但我认为它们中的任何一个都不能真正保存对象的数据,例如我们的 UIElement。

但是每个DataPackage 实例都支持一组属性,对应一个字典。我们可以将 Dictionary 设置为在其中保存 UIElement,只要我们稍后指定一个键来引用同一个对象。

private void Rectangle_DragStarting(UIElement sender, DragStartingEventArgs args)
{
   dataPackage.RequestedOperation = DataPackageOperation.Copy;
   args.Data.Properties.Add("myRectangle", sender);
}

在drop Event Handler中,可以获取UIElement,如下:

private async void StackPanel_Drop(object sender, DragEventArgs e)
{
   Rectangle element = e.DataView.Properties["myRectangle"] as Rectangle;
                       ......
                       ......
}

第三种解决方案:

此解决方案使用DataPackage 公开的方法SetText(String) 来保存被拖动的UIElement 的Name 属性的值。

private void Rectangle_DragStarting(UIElement sender, DragStartingEventArgs args)
{
    dataPackage = new DataPackage();
    dataPackage.RequestedOperation = DataPackageOperation.Copy;
    Rectangle rectangle = sender as Rectangle;
    dataPackage.SetText(rectangle.Name);
    Clipboard.SetContent(dataPackage);
}

通过知道被拖动的UIElementName 属性的值,通过使用VisualTreeHelper 类来查找它,如下所示:

private async void StackPanel_Drop(object sender, DragEventArgs e)
{
    DataPackageView dataPackageView = Clipboard.GetContent();
    if (dataPackageView.Contains(StandardDataFormats.Text))
    {
        draggedObject = await dataPackageView.GetTextAsync();
    }

    // Dragged objects come from another one of our Parent's Children
    DependencyObject parent = VisualTreeHelper.GetParent(StackPanelDropArea);
    int count = VisualTreeHelper.GetChildrenCount(parent);

    for(int i=0; i< count; i++)
    {
        DependencyObject child = VisualTreeHelper.GetChild(parent, i);
        if(child.GetType().Equals(typeof(StackPanel)))
        {
            StackPanel currentStackPanel = child as StackPanel;
            if(currentStackPanel.Name == "StackPanelRectangles")
            {
                int numberOfRectangles = VisualTreeHelper.GetChildrenCount(currentStackPanel);
                for(int j=0; j<numberOfRectangles; j++)
                {
                    if(VisualTreeHelper.GetChild(currentStackPanel,j).GetType().Equals(typeof(Rectangle)))
                    {
                        Rectangle currentRectangle = VisualTreeHelper.GetChild(currentStackPanel, j) as Rectangle;
                        if (draggedObject != string.Empty && currentRectangle.Name.Equals(draggedObject))
                        {
                            Rectangle newRectangle = new Rectangle();
                            newRectangle.Width = currentRectangle.Width;
                            newRectangle.Height = currentRectangle.Height;
                            newRectangle.Fill = currentRectangle.Fill;

                            StackPanelDropArea.Children.Add(newRectangle);
                        }
                    }

                }
            }
        }
    } */
}

结果:

【讨论】:

    【解决方案3】:

    在放弃并使用第三方库之前,我通常会尝试多次解决这个问题。我通常使用的是:

    https://github.com/punker76/gong-wpf-dragdrop

    【讨论】:

      【解决方案4】:

      您可以在DataTemplate 中订阅PointerPressed 事件并提取您需要的所有内容。

      XAML:

          <DataTemplate x:Name="DataTemplate">
              <Grid Background="Transparent" PointerPressed="Grid_OnPointerPressed"/>
          </DataTemplate>
      

      代码:

          private void Grid_OnPointerPressed(object sender, PointerRoutedEventArgs e)
          {
              //your FrameworkElement
              var frameworkElement = sender as FrameworkElement;
              //global position of your element
              var itemPosition = frameworkElement.TransformToVisual(Window.Current.Content).TransformPoint(new Point(0, 0)).ToVector2();
              //your data
              var selectedItemData = frameworkElement.DataContext as ItemData;
          }
      

      保存您的数据,使用 UWP Drag'n'Drop。在拖放时加载您的数据。

      【讨论】:

        猜你喜欢
        • 2021-12-24
        • 2012-01-09
        • 2019-04-14
        • 2017-06-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多