简体   繁体   中英

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

I am stuck with a C# wpf drag drop issue. 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). 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. 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 )

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

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

<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

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

<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

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. 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. 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. Check the below link will be useful for your scenario. https://www.telerik.com/blogs/adding-drag-and-drop-to-wpf-listboxes-thanks-telerik !

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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