WPF C#: 끌어서 놓기를 통해 목록 상자의 항목을 재정렬합니다.
미리 채워진 목록 상자의 항목을 마우스 끌기를 통해 위아래로 이동하는 방법을 알아보려고 합니다.
통제부를 살펴봤습니다Microsoft api에서 DoDragDrop 메서드를 사용하지만 여전히 아무 것도 할 수 없습니다.
저는 비주얼 스튜디오 환경에 익숙하지 않기 때문에 도움을 주시면 감사하겠습니다.
ObservableCollection을 사용하여 하나를 만들어 보았습니다.보세요.
ObservableCollection<Emp> _empList = new ObservableCollection<Emp>();
public Window1()
{
InitializeComponent();
_empList .Add(new Emp("1", 22));
_empList .Add(new Emp("2", 18));
_empList .Add(new Emp("3", 29));
_empList .Add(new Emp("4", 9));
_empList .Add(new Emp("5", 29));
_empList .Add(new Emp("6", 9));
listbox1.DisplayMemberPath = "Name";
listbox1.ItemsSource = _empList;
Style itemContainerStyle = new Style(typeof(ListBoxItem));
itemContainerStyle.Setters.Add(new Setter(ListBoxItem.AllowDropProperty, true));
itemContainerStyle.Setters.Add(new EventSetter(ListBoxItem.PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(s_PreviewMouseLeftButtonDown)));
itemContainerStyle.Setters.Add(new EventSetter(ListBoxItem.DropEvent, new DragEventHandler(listbox1_Drop)));
listbox1.ItemContainerStyle = itemContainerStyle;
}
끌어서 놓기 프로세스입니다.
void s_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is ListBoxItem)
{
ListBoxItem draggedItem = sender as ListBoxItem;
DragDrop.DoDragDrop(draggedItem, draggedItem.DataContext, DragDropEffects.Move);
draggedItem.IsSelected = true;
}
}
void listbox1_Drop(object sender, DragEventArgs e)
{
Emp droppedData = e.Data.GetData(typeof(Emp)) as Emp;
Emp target = ((ListBoxItem)(sender)).DataContext as Emp;
int removedIdx = listbox1.Items.IndexOf(droppedData);
int targetIdx = listbox1.Items.IndexOf(target);
if (removedIdx < targetIdx)
{
_empList.Insert(targetIdx + 1, droppedData);
_empList.RemoveAt(removedIdx);
}
else
{
int remIdx = removedIdx+1;
if (_empList.Count + 1 > remIdx)
{
_empList.Insert(targetIdx, droppedData);
_empList.RemoveAt(remIdx);
}
}
}
참고:
- 이 구현에서 한 가지 아쉬운 점은 다음과 같습니다.
PreviewMouseLeftButtonDown
이벤트, 끌어온 항목이 선택한 항목처럼 보이지 않습니다. - 또한 보다 쉽게 구현하기 위해 드롭 대상은 목록 상자 항목이지 목록 상자 자체가 아닙니다. 이를 위해서는 더 나은 솔루션이 필요할 수 있습니다.
dnr3의 답변을 사용하여 선택 문제가 수정된 버전을 만들었습니다.
창 1.xaml입니다.
<Window x:Class="ListBoxReorderDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="ListBoxReorderDemo" Height="300" Width="300"
WindowStartupLocation="CenterScreen">
<Grid>
<ListBox x:Name="listBox"/>
</Grid>
</Window>
Window1.xaml.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
namespace ListBoxReorderDemo
{
public class Item
{
public string Name { get; set; }
public Item(string name)
{
this.Name = name;
}
}
public partial class Window1 : Window
{
private Point _dragStartPoint;
private T FindVisualParent<T>(DependencyObject child)
where T : DependencyObject
{
var parentObject = VisualTreeHelper.GetParent(child);
if (parentObject == null)
return null;
T parent = parentObject as T;
if (parent != null)
return parent;
return FindVisualParent<T>(parentObject);
}
private IList<Item> _items = new ObservableCollection<Item>();
public Window1()
{
InitializeComponent();
_items.Add(new Item("1"));
_items.Add(new Item("2"));
_items.Add(new Item("3"));
_items.Add(new Item("4"));
_items.Add(new Item("5"));
_items.Add(new Item("6"));
listBox.DisplayMemberPath = "Name";
listBox.ItemsSource = _items;
listBox.PreviewMouseMove += ListBox_PreviewMouseMove;
var style = new Style(typeof(ListBoxItem));
style.Setters.Add(new Setter(ListBoxItem.AllowDropProperty, true));
style.Setters.Add(
new EventSetter(
ListBoxItem.PreviewMouseLeftButtonDownEvent,
new MouseButtonEventHandler(ListBoxItem_PreviewMouseLeftButtonDown)));
style.Setters.Add(
new EventSetter(
ListBoxItem.DropEvent,
new DragEventHandler(ListBoxItem_Drop)));
listBox.ItemContainerStyle = style;
}
private void ListBox_PreviewMouseMove(object sender, MouseEventArgs e)
{
Point point = e.GetPosition(null);
Vector diff = _dragStartPoint - point;
if (e.LeftButton == MouseButtonState.Pressed &&
(Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
{
var lb = sender as ListBox;
var lbi = FindVisualParent<ListBoxItem>(((DependencyObject)e.OriginalSource));
if (lbi != null)
{
DragDrop.DoDragDrop(lbi, lbi.DataContext, DragDropEffects.Move);
}
}
}
private void ListBoxItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_dragStartPoint = e.GetPosition(null);
}
private void ListBoxItem_Drop(object sender, DragEventArgs e)
{
if (sender is ListBoxItem)
{
var source = e.Data.GetData(typeof(Item)) as Item;
var target = ((ListBoxItem)(sender)).DataContext as Item;
int sourceIndex = listBox.Items.IndexOf(source);
int targetIndex = listBox.Items.IndexOf(target);
Move(source, sourceIndex, targetIndex);
}
}
private void Move(Item source, int sourceIndex, int targetIndex)
{
if (sourceIndex < targetIndex)
{
_items.Insert(targetIndex + 1, source);
_items.RemoveAt(sourceIndex);
}
else
{
int removeIndex = sourceIndex + 1;
if (_items.Count + 1 > removeIndex)
{
_items.Insert(targetIndex, source);
_items.RemoveAt(removeIndex);
}
}
}
}
}
제네릭 및 데이터 바인딩을 지원하는 버전입니다.
창 1.xaml입니다.
<Window x:Class="ListBoxReorderDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ListBoxReorderDemo"
Title="ListBoxReorderDemo" Height="300" Width="300"
WindowStartupLocation="CenterScreen">
<Grid>
<local:ItemDragAndDropListBox x:Name="listBox" ItemsSource="{Binding}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</local:ItemDragAndDropListBox>
</Grid>
</Window>
Window1.xaml.cs
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
namespace ListBoxReorderDemo
{
public class DragAndDropListBox<T> : ListBox
where T : class
{
private Point _dragStartPoint;
private P FindVisualParent<P>(DependencyObject child)
where P : DependencyObject
{
var parentObject = VisualTreeHelper.GetParent(child);
if (parentObject == null)
return null;
P parent = parentObject as P;
if (parent != null)
return parent;
return FindVisualParent<P>(parentObject);
}
public DragAndDropListBox()
{
this.PreviewMouseMove += ListBox_PreviewMouseMove;
var style = new Style(typeof(ListBoxItem));
style.Setters.Add(new Setter(ListBoxItem.AllowDropProperty, true));
style.Setters.Add(
new EventSetter(
ListBoxItem.PreviewMouseLeftButtonDownEvent,
new MouseButtonEventHandler(ListBoxItem_PreviewMouseLeftButtonDown)));
style.Setters.Add(
new EventSetter(
ListBoxItem.DropEvent,
new DragEventHandler(ListBoxItem_Drop)));
this.ItemContainerStyle = style;
}
private void ListBox_PreviewMouseMove(object sender, MouseEventArgs e)
{
Point point = e.GetPosition(null);
Vector diff = _dragStartPoint - point;
if (e.LeftButton == MouseButtonState.Pressed &&
(Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
{
var lb = sender as ListBox;
var lbi = FindVisualParent<ListBoxItem>(((DependencyObject)e.OriginalSource));
if (lbi != null)
{
DragDrop.DoDragDrop(lbi, lbi.DataContext, DragDropEffects.Move);
}
}
}
private void ListBoxItem_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
_dragStartPoint = e.GetPosition(null);
}
private void ListBoxItem_Drop(object sender, DragEventArgs e)
{
if (sender is ListBoxItem)
{
var source = e.Data.GetData(typeof(T)) as T;
var target = ((ListBoxItem)(sender)).DataContext as T;
int sourceIndex = this.Items.IndexOf(source);
int targetIndex = this.Items.IndexOf(target);
Move(source, sourceIndex, targetIndex);
}
}
private void Move(T source, int sourceIndex, int targetIndex)
{
if (sourceIndex < targetIndex)
{
var items = this.DataContext as IList<T>;
if (items != null)
{
items.Insert(targetIndex + 1, source);
items.RemoveAt(sourceIndex);
}
}
else
{
var items = this.DataContext as IList<T>;
if (items != null)
{
int removeIndex = sourceIndex + 1;
if (items.Count + 1 > removeIndex)
{
items.Insert(targetIndex, source);
items.RemoveAt(removeIndex);
}
}
}
}
}
public class Item
{
public string Name { get; set; }
public Item(string name)
{
this.Name = name;
}
}
public class ItemDragAndDropListBox : DragAndDropListBox<Item> { }
public partial class Window1 : Window
{
private IList<Item> _items = new ObservableCollection<Item>();
public Window1()
{
InitializeComponent();
_items.Add(new Item("1"));
_items.Add(new Item("2"));
_items.Add(new Item("3"));
_items.Add(new Item("4"));
_items.Add(new Item("5"));
_items.Add(new Item("6"));
listBox.DataContext = _items;
}
}
}
저는 공솔루션이라고 하는 끌어서 놓기 동작을 사용할 것을 제안합니다.WPF.DragDrop입니다.연결된 속성 설정기를 사용하여 MVVM 스타일 사용 사례를 사용할 수 있습니다. 보기에서 코드를 사용할 필요가 없습니다.간단한 예제를 위해 링크를 확인해야 합니다.
dnr3의 답변을 XAML에 구현하기 위해 변경했습니다. 같은 결과, 코드 배후에 있는 것보다 XAML에서 가능한 작업을 수행하는 것이 더 좋습니다.
코드 뒤에 있는 코드 대신 다음을 수행합니다.
Style itemContainerStyle = new Style(typeof(ListBoxItem));
itemContainerStyle.Setters.Add(new Setter(AllowDropProperty, true));
itemContainerStyle.Setters.Add(new EventSetter(PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(s_PreviewMouseLeftButtonDown)));
itemContainerStyle.Setters.Add(new EventSetter(DropEvent, new DragEventHandler(listbox1_Drop)));
listbox1.ItemContainerStyle = itemContainerStyle;
다음을 XAML에 넣습니다.
<Window.Resources>
<Style x:Key="ListBoxDragDrop" TargetType="{x:Type ListBoxItem}">
<Setter Property="AllowDrop" Value="true"/>
<EventSetter Event="PreviewMouseMove" Handler="s_PreviewMouseMoveEvent"/>
<EventSetter Event="Drop" Handler="listbox1_Drop"/>
</Style>
</Window.Resources>
<Grid>
<ListBox x:Name="listbox1" ItemContainerStyle="{StaticResource ListBoxDragDrop}" HorizontalAlignment="Left" Height="299" Margin="10,10,0,0" VerticalAlignment="Top" Width="224"/>
</Grid>
XAML의 코드 뒤에 배치된 마우스 핸들러입니다.
void s_PreviewMouseMoveEvent(object sender, MouseEventArgs e)
{
if (sender is ListBoxItem && e.LeftButton == MouseButtonState.Pressed)
{
ListBoxItem draggedItem = sender as ListBoxItem;
DragDrop.DoDragDrop(draggedItem, draggedItem.DataContext, DragDropEffects.Move);
draggedItem.IsSelected = true;
}
}
복구 코드:
private void listbox1_Drop(object sender, DragEventArgs e)
{
if (sender is ListBoxItem)
{
Emp droppedData = e.Data.GetData(typeof(Emp)) as Emp;
Emp target = ((ListBoxItem)(sender)).DataContext as Emp;
int removedIdx = listbox1.Items.IndexOf(droppedData);
int targetIdx = listbox1.Items.IndexOf(target);
if (removedIdx < targetIdx)
{
_empList.Insert(targetIdx + 1, droppedData);
_empList.RemoveAt(removedIdx);
}
else
{
int remIdx = removedIdx + 1;
if (_empList.Count + 1 > remIdx)
{
_empList.Insert(targetIdx, droppedData);
_empList.RemoveAt(remIdx);
}
}
}
}
이것은 당신에게 큰 도움이 되었습니다.특히 제네릭 버전이죠.
다음과 같이 수정했습니다.
ListBox의 DataContext를 설정하지 않았기 때문입니다(항목만).출처)를 사용합니다.
var items = this.ItemsSource as IList<T>;
Move 메서드에서 선택합니다.
그리고 "이동" 끝에 다음과 같이 덧붙였습니다.
this.SelectedItem = source;
사용자가 이동된 항목을 현재 선택 항목으로 갖기를 원하기 때문입니다.
비스와프 숄테스가 dnr3의 답변을 수정하여 쉽게 재사용할 수 있는 클래스로 추출하여 코드 몇 줄만으로 여러 목록을 설정할 수 있도록 했습니다.보기 쉽도록 각 항목이 두 가지 배경색을 번갈아 표시하는 기능도 추가했습니다(원하지 않으면 쉽게 제거할 수 있습니다).
F.Y.I: 만약 당신이 'var'의 애호가라면 미안하지만, 저는 절대 그렇지 않습니다. 그리고 그것들은 제 IDE에 의해 제거되었습니다. 그리고 저는 그것들을 원래대로 되돌릴 생각이 없습니다.물론 이렇게 해도 실제 프로그램 동작은 변경되지 않으며 컴파일 시간이 개선될 것입니다.이겼어요! :p
클래스는 다음과 같습니다.
/// <typeparam name="IT">The item type to be stored in this list</typeparam>
internal class ReorderableList<IT> where IT : class
{
private readonly SolidColorBrush m_alternator1, m_alternator2; // Background colours for the list items to alternate between
private readonly ListBox m_ListBox; // The target ListBox we're modifying
private readonly string m_displayMemberPath; // The name of the member in to display
private readonly IList<IT> m_items = new ObservableCollection<IT>();
private Point m_cursorStartPos;
/// <summary>
/// Initializes the list (this must be done after components are initialized and loaded!).
/// </summary>
/// <param name="resourceProvider">Pass 'this' for this parameter</param>
/// <param name="listBox">The target ListBox control to modify</param>
/// <param name="displayMemberPath">The name of the member in the generic type contained in this list, to be displayed</param>
public ReorderableList(ListBox listBox, string displayMemberPath, SolidColorBrush alternator1, SolidColorBrush alternator2)
{
m_ListBox = listBox;
m_displayMemberPath = displayMemberPath;
m_alternator1 = alternator1;
m_alternator2 = alternator2;
Initialize();
}
private void Initialize()
{
// Set the list box's items source and tell it what member in the IT class to use for the display name
// Add an event handler for preview mouse move
m_ListBox.DisplayMemberPath = m_displayMemberPath;
m_ListBox.ItemsSource = m_items;
m_ListBox.PreviewMouseMove += OnListPreviewMouseMove;
// Create the item container style to be used by the listbox
// Add mouse event handlers to the style
Style style = new Style(typeof(ListBoxItem));
style.Setters.Add(new Setter(UIElement.AllowDropProperty, true));
style.Setters.Add(new EventSetter(UIElement.PreviewMouseLeftButtonDownEvent, new MouseButtonEventHandler(OnListPreviewMouseLeftButtonDown)));
style.Setters.Add(new EventSetter(UIElement.DropEvent, new DragEventHandler(OnListDrop)));
// Add triggers to alternate the background colour of each element based on its alternation index
// (Remove this, as well as the two SolidColorBrush resources if you don't want this feature)
Trigger trigger1 = new Trigger()
{
Property = ItemsControl.AlternationIndexProperty,
Value = 0
};
Setter setter1 = new Setter()
{
Property = Control.BackgroundProperty,
Value = m_alternator1
};
trigger1.Setters.Add(setter1);
style.Triggers.Add(trigger1);
Trigger trigger2 = new Trigger()
{
Property = ItemsControl.AlternationIndexProperty,
Value = 1
};
Setter setter2 = new Setter()
{
Property = Control.BackgroundProperty,
Value = m_alternator2
};
trigger2.Setters.Add(setter2);
style.Triggers.Add(trigger2);
// Set the item container style
m_ListBox.ItemContainerStyle = style;
}
/// <summary>
/// Adds an item to the list. If [ignoreDuplicates] is false and the item is already in the list,
/// the item won't be added.
/// </summary>
/// <param name="item">The item to add</param>
/// <param name="ignoreDuplicates">Whether or not to add the item regardless of whether it's already in the list</param>
/// <returns>Whether or not the item was added</returns>
public bool Add(IT item, bool ignoreDuplicates = true)
{
if (!ignoreDuplicates && Contains(item)) return false;
m_items.Add(item);
return true;
}
/// <summary>
/// Removes an item from the list.
/// </summary>
/// <param name="item">The item to remove</param>
/// <returns>Whether or not the item was removed from the list. This will be false if the item was not in the list to begin with.</returns>
public bool Remove(IT item)
{
if (Contains(item)) return false;
m_items.Remove(item);
return true;
}
/// <summary>
/// Returns whether or not the list contains the given item.
/// </summary>
/// <param name="item">The item to check for</param>
/// <returns>Whether or not the list contains the given item.</returns>
public bool Contains(IT item)
{
return m_items.Contains(item);
}
private void OnListPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
m_cursorStartPos = e.GetPosition(null);
}
private void OnListPreviewMouseMove(object sender, MouseEventArgs e)
{
Point currentCursorPos = e.GetPosition(null);
Vector cursorVector = m_cursorStartPos - currentCursorPos;
if (e.LeftButton == MouseButtonState.Pressed
&&(Math.Abs(cursorVector.X) > SystemParameters.MinimumHorizontalDragDistance
|| Math.Abs(cursorVector.Y) > SystemParameters.MinimumVerticalDragDistance))
{
ListBoxItem targetItem = FindVisualParent<ListBoxItem>(((DependencyObject)e.OriginalSource));
if (targetItem != null)
{
DragDrop.DoDragDrop(targetItem, targetItem.DataContext, DragDropEffects.Move);
}
}
}
private void OnListDrop(object sender, DragEventArgs e)
{
if (sender is ListBoxItem item)
{
IT source = e.Data.GetData(typeof(IT)) as IT;
IT target = item.DataContext as IT;
int sourceIndex = m_ListBox.Items.IndexOf(source);
int targetIndex = m_ListBox.Items.IndexOf(target);
Move(source, sourceIndex, targetIndex);
}
}
private void Move(IT source, int sourceIndex, int targetIndex)
{
if (sourceIndex < targetIndex)
{
m_items.Insert(targetIndex + 1, source);
m_items.RemoveAt(sourceIndex);
}
else
{
int removeIndex = sourceIndex + 1;
if (m_items.Count + 1 > removeIndex)
{
m_items.Insert(targetIndex, source);
m_items.RemoveAt(removeIndex);
}
}
}
private T FindVisualParent<T>(DependencyObject child) where T : DependencyObject
{
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
if (parentObject == null) return null;
if (parentObject is T parent) return parent;
return FindVisualParent<T>(parentObject);
}
}
다음은 IT(항목 유형) 클래스의 예입니다(원래 답변과 동일).
public class ExampleItem
{
public string Name { get; set; }
public ExampleItem(string name)
{
Name = name;
}
}
마지막으로, 사용법은 다음과 같습니다.
public partial class MainWindow : Window
{
private readonly ReorderableList<ExampleItem> ExampleList;
public MainWindow()
{
InitializeComponent();
ExampleList = new ReorderableList<ExampleItem>(myXamlListBoxControl, "Name",
FindResource("AlternatingBG1") as SolidColorBrush,
FindResource("AlternatingBG2") as SolidColorBrush);
ExampleList.Add(new ExampleItem("Test Item 1"));
ExampleList.Add(new ExampleItem("Test Item 2"));
ExampleList.Add(new ExampleItem("Test Item 3"));
ExampleList.Add(new ExampleItem("Test Item 4"));
ExampleList.Add(new ExampleItem("Test Item 5"));
ExampleList.Add(new ExampleItem("Test Item 6"));
}
}
언급URL : https://stackoverflow.com/questions/3350187/wpf-c-rearrange-items-in-listbox-via-drag-and-drop 입니다.
'source' 카테고리의 다른 글
Linux python3 - lib 'SQL Server'를 열 수 없습니다. (0) | 2023.04.25 |
---|---|
WPF 창의 핸들을 찾는 중입니다. (0) | 2023.04.25 |
Eclipse git 체크아웃(일명 되돌리기)입니다. (0) | 2023.04.25 |
bash에서 noop [:]의 사용 예는 무엇입니까? (0) | 2023.04.25 |
체크아웃하지 않고 분기 포인터를 다른 커밋으로 이동합니다. (0) | 2023.04.25 |