简体   繁体   English

使用用户控件执行拖放操作时,C#WPF UI无响应

[英]C# WPF UI unresponsive when performing drag drop using user control

I am stuck with a C# wpf drag drop issue. 我遇到了C#wpf拖放问题。 I have created a very simple project that includes a User Control, a couple of classes to hold the data and a form to host multiple copies of the user control (using a bound ItemsControl). 我创建了一个非常简单的项目,其中包括一个用户控件,几个用于保存数据的类以及一个用于承载用户控件的多个副本的表单(使用绑定的ItemsControl)。 When I drag the control onto the form the drag drop is triggered, the observablecollection is updated but the UI doesn't reflect the change and future events don't seem to be working. 当我将控件拖动到窗体上时,将触发拖放操作,observablecollection已更新,但UI无法反映更改,以后的事件似乎也无法正常工作。 Rolling over the add item button doesn't even show the rollover effect. 将鼠标悬停在“添加项目”按钮上甚至不会显示滚动效果。 Sure I am doing something stupid but I can't seem to see what it is. 当然,我在做一些愚蠢的事情,但似乎看不到它是什么。

Code below (mainly from Microsoft Example ) 下面的代码(主要来自Microsoft Example

SimpleDataClass SimpleDataClass

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DragDropControl.Model
{
  public class SimpleDataClass : INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler PropertyChanged;

    private string _groupName = string.Empty;
    private ObservableCollection<SimpleSubDataClass> _titles = new ObservableCollection<SimpleSubDataClass>();

    public string GroupName
    {
      get { return _groupName; }
      set
      {
        if (_groupName != value)
        {
          _groupName = value;
          RaisePropertyChangedEvent("GroupName");
        }
      }
    }

    public ObservableCollection<SimpleSubDataClass> Titles
    {
      get { return _titles; }
      set
      {
        if (_titles != value)
        {
          _titles = value;
          RaisePropertyChangedEvent("Titles");
        }
      }
    }

    private void RaisePropertyChangedEvent(string propertyName)
    {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
  }
}

SimpleSubDataClass SimpleSubDataClass

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DragDropControl.Model
{
  public class SimpleSubDataClass : INotifyPropertyChanged
  {
    public event PropertyChangedEventHandler PropertyChanged;

    private string _title = string.Empty;

    public string Title
    {
      get { return _title; }
      set
      {
        if (_title != value)
        {
          _title = value;
          RaisePropertyChanged("Title");
        }
      }
    }

    private void RaisePropertyChanged(string propertyName)
    {
      PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    public SimpleSubDataClass(string title)
    {
      Title = title;
    }
  }
}

DDControl - XAML DDControl-XAML

<UserControl x:Class="DragDropControl.DDControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:DragDropControl"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300"
             x:Name="CurrentControl">
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="Auto"/>
    </Grid.RowDefinitions>
    <TextBox Name="txtGroupName" Grid.Row="0" Text="{Binding ElementName=CurrentControl, Path=ThisData.GroupName}"/>
    <ListBox Name="lstTitles" Grid.Row="1" ItemsSource="{Binding ElementName=CurrentControl, Path=ThisData.Titles}">
      <ListBox.ItemTemplate>
        <DataTemplate>
          <Label Name="lblTitle" Content="{Binding Title}"/>
        </DataTemplate>
      </ListBox.ItemTemplate>
    </ListBox>
  </Grid>
</UserControl>

DDControl - Code behind DDControl-背后的代码

using DragDropControl.Model;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace DragDropControl
{
  /// <summary>
  /// Interaction logic for UserControl1.xaml
  /// </summary>
  public partial class DDControl : UserControl
  {
    public static readonly DependencyProperty ThisDataProperty = DependencyProperty.Register( "ThisData",
                                                                                              typeof(SimpleDataClass),
                                                                                              typeof(DDControl),
                                                                                              new PropertyMetadata(new SimpleDataClass()));
    public SimpleDataClass ThisData
    {
      get { return (SimpleDataClass)GetValue(ThisDataProperty); }
      set { SetValue(ThisDataProperty, value); }
    }


    public DDControl()
    {
      InitializeComponent();
    }

    protected override void OnMouseMove(MouseEventArgs e)
    {
      base.OnMouseMove(e);
      if (e.LeftButton == MouseButtonState.Pressed)
      {
        DataObject data = new DataObject(this.ThisData);

        DragDrop.DoDragDrop(this, data, DragDropEffects.Move);
      }
    }

    protected override void OnGiveFeedback(GiveFeedbackEventArgs e)
    {
      base.OnGiveFeedback(e);
      if (e.Effects.HasFlag(DragDropEffects.Move))
        Mouse.SetCursor(Cursors.Pen);

      e.Handled = true;
    }
  }
}

MainWindow - Xaml MainWindow-Xaml

<Window x:Class="DragDropUserControlWithTextBox.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DragDropUserControlWithTextBox"
        xmlns:ddc="clr-namespace:DragDropControl;assembly=DragDropControl"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
  <Grid>
    <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto">
      <StackPanel Name="stkMain" Background="Gray" Orientation="Horizontal" Drop="stkMain_Drop" AllowDrop="true">
        <ItemsControl Name="icColumns" Background="Red">
          <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
              <StackPanel Name="stkItemsControlPanel" Orientation="Horizontal"/>
            </ItemsPanelTemplate>
          </ItemsControl.ItemsPanel>
          <ItemsControl.ItemTemplate>
            <DataTemplate>
              <ddc:DDControl Background="{x:Null}" ThisData="{Binding}"/><!-- MouseMove="DDControl_MouseMove" GiveFeedback="DDControl_GiveFeedback"/>-->
            </DataTemplate>
          </ItemsControl.ItemTemplate>
        </ItemsControl>
        <Button Name="btnAddData" Content="Add Data" Click="btnAddData_Click"/>
      </StackPanel>
    </ScrollViewer>
  </Grid>
</Window>

MainWindow - Code behind MainWindow-后面的代码

using DragDropControl.Model;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Input;

namespace DragDropUserControlWithTextBox
{
  /// <summary>
  /// Interaction logic for MainWindow.xaml
  /// </summary>
  public partial class MainWindow : Window
  {
    private ObservableCollection<SimpleDataClass> _data = new ObservableCollection<SimpleDataClass>();

    public MainWindow()
    {
      InitializeComponent();

      CreateTestData();
    }

    private void CreateTestData()
    {
      SimpleDataClass tempSDC1 = new SimpleDataClass();
      tempSDC1.GroupName = "First Item";
      tempSDC1.Titles.Add(new SimpleSubDataClass("Title 1_1"));
      tempSDC1.Titles.Add(new SimpleSubDataClass("Title 1_2"));
      tempSDC1.Titles.Add(new SimpleSubDataClass("Title 1_3"));
      tempSDC1.Titles.Add(new SimpleSubDataClass("Title 1_4"));
      tempSDC1.Titles.Add(new SimpleSubDataClass("Title 1_5"));

      SimpleDataClass tempSDC2 = new SimpleDataClass();
      tempSDC2.GroupName = "Second Item";
      tempSDC2.Titles.Add(new SimpleSubDataClass("Title 2_1"));
      tempSDC2.Titles.Add(new SimpleSubDataClass("Title 2_2"));
      tempSDC2.Titles.Add(new SimpleSubDataClass("Title 2_3"));
      tempSDC2.Titles.Add(new SimpleSubDataClass("Title 2_4"));
      tempSDC2.Titles.Add(new SimpleSubDataClass("Title 2_5"));

      _data.Add(tempSDC1);
      _data.Add(tempSDC2);

      this.icColumns.ItemsSource = _data;
    }

    private void stkMain_Drop(object sender, DragEventArgs e)
    {
      if (e.Handled == false)
      {
        if (e.Data.GetDataPresent(typeof(SimpleDataClass)))
        {
          SimpleDataClass tempData = (SimpleDataClass)e.Data.GetData(typeof(SimpleDataClass));

          _data.Add(tempData);
        }

        e.Effects.HasFlag(DragDropEffects.None);
        Mouse.SetCursor(Cursors.Arrow);
        e.Handled = true;
      }
    }

    private void btnAddData_Click(object sender, RoutedEventArgs e)
    {
      SimpleDataClass tempData = new SimpleDataClass();
      tempData.GroupName = "Amazing Test";
      tempData.Titles.Add(new SimpleSubDataClass("AT_1"));
      tempData.Titles.Add(new SimpleSubDataClass("AT_2"));
      tempData.Titles.Add(new SimpleSubDataClass("AT_3"));
      _data.Add(tempData);
    }
  }
}

I swapped to using the method described in this WPF tutorial on drag and drop. 我将拖放操作改为使用本WPF教程中描述的方法。 It still had an issue when you dragged and dropped a user control with a textbox on it (would assume it would be for any control that is hitenabled) where it would create a second instance of the item being dragged but that is pretty easy to work around, just set the enabled state to false when detecting the control is about to be dragged and enable it again when it is dropped. 当您拖放带有文本框的用户控件时,它仍然存在一个问题(将假定它适用于任何已启用命中的控件),它将在其中创建要拖动的项目的第二个实例,但是这很容易工作左右,只需在检测到控件将要拖动时将启用状态设置为false,然后在将其放下时再次将其启用即可。 Possibly a hack but one that works. 可能是一种骇客,但行之有效。

For drag and drop, create a Behavior and handle the events in the Behavior for the avoiding the UI thread freeze scenarios. 对于拖放操作,请创建一个Behavior并处理Behavior中的事件,以避免UI线程冻结情况。 Check the below link will be useful for your scenario. 检查以下链接将对您的情况有用。 https://www.telerik.com/blogs/adding-drag-and-drop-to-wpf-listboxes-thanks-telerik ! https://www.telerik.com/blogs/adding-drag-and-drop-to-wpf-listboxes-thanks-telerik

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM