๐ฑ๏ธ WPF TreeView์์ Drag & Drop์ผ๋ก ๋ ธ๋ ์ด๋ํ๊ธฐ
์ด๋ฒ ํฌ์คํธ์์๋ WPF TreeView์์ ๋
ธ๋๋ฅผ ๋๋๊ทธ ์ค ๋๋กญ(Drag & Drop)์ผ๋ก ์ด๋ํ๋ ๋ฐฉ๋ฒ์ ์๊ฐํฉ๋๋ค.
๋จ์ํ UI ์ด๋ฒคํธ๋ง ์ฒ๋ฆฌํ๋ ๊ฒ์ด ์๋๋ผ MVVM ๊ตฌ์กฐ ์์์ ์ฒ๋ฆฌํ๋ ํ๋ ํจ๊ป ์๋ ค๋๋ฆฝ๋๋ค.
๋ชฉ์ฐจ
1. Category ๋ชจ๋ธ ํด๋์ค
public class Category
{
public string Name { get; set; }
public ObservableCollection<Category> Children { get; set; } = new();
}
2. ViewModel ๊ตฌ์ฑ
public class MainViewModel
{
public ObservableCollection<Category> Categories { get; set; }
public MainViewModel()
{
Categories = new ObservableCollection<Category>
{
new Category
{
Name = "๋ฐฑ์๋",
Children = new ObservableCollection<Category>
{
new Category { Name = "ASP.NET" },
new Category { Name = "Node.js" }
}
},
new Category
{
Name = "ํ๋ก ํธ์๋",
Children = new ObservableCollection<Category>
{
new Category { Name = "React" },
new Category { Name = "Vue" }
}
}
};
}
}
3. XAML์์ Drag & Drop ๊ตฌํ
<TreeView ItemsSource="{Binding Categories}" AllowDrop="True"
PreviewMouseLeftButtonDown="TreeView_PreviewMouseLeftButtonDown"
DragOver="TreeView_DragOver"
Drop="TreeView_Drop" Margin="10">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
๐ Code-behind ์ด๋ฒคํธ ์ฒ๋ฆฌ
private Category? _draggedItem;
private void TreeView_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
TreeViewItem item = FindAncestor<TreeViewItem>((DependencyObject)e.OriginalSource);
if (item?.DataContext is Category category)
{
_draggedItem = category;
DragDrop.DoDragDrop(item, category, DragDropEffects.Move);
}
}
private void TreeView_DragOver(object sender, DragEventArgs e)
{
e.Effects = DragDropEffects.Move;
e.Handled = true;
}
private void TreeView_Drop(object sender, DragEventArgs e)
{
if (_draggedItem == null)
return;
TreeViewItem item = FindAncestor<TreeViewItem>((DependencyObject)e.OriginalSource);
if (item?.DataContext is Category targetCategory && targetCategory != _draggedItem)
{
// ๋ถ๋ชจ ๋
ธ๋์์ ์ ๊ฑฐ
RemoveFromParent(DataContext as MainViewModel, _draggedItem);
// ๋์ ๋
ธ๋์ ์์์ผ๋ก ์ถ๊ฐ
targetCategory.Children.Add(_draggedItem);
}
_draggedItem = null;
}
private void RemoveFromParent(MainViewModel vm, Category child)
{
foreach (var parent in vm.Categories.SelectMany(GetAllCategories))
{
if (parent.Children.Contains(child))
{
parent.Children.Remove(child);
return;
}
}
}
private IEnumerable<Category> GetAllCategories(Category cat)
{
yield return cat;
foreach (var child in cat.Children.SelectMany(GetAllCategories))
yield return child;
}
// Visual Tree Helper
private static T? FindAncestor<T>(DependencyObject current) where T : DependencyObject
{
while (current != null)
{
if (current is T desired)
return desired;
current = VisualTreeHelper.GetParent(current);
}
return null;
}
4. ๋๋๊ทธ ์ค ๋๋กญ ๋์ ์ค๋ช
- ๋ ธ๋๋ฅผ ํด๋ฆญ ํ ๋๋๊ทธํ๋ฉด DragDrop ์์
- ๋๋กญ ๋์ ๋ ธ๋์ ์์์ผ๋ก ์ด๋
- ๊ธฐ์กด ๋ถ๋ชจ์์ ํด๋น ๋ ธ๋๋ฅผ ์ฐพ์ ์ ๊ฑฐ ํ ์ด๋
- MVVM ๊ตฌ์กฐ์ ๋ง์ถฐ ViewModel ๋ฐ์ดํฐ๋ฅผ ์์
์์ ํ MVVM ๊ตฌ์กฐ๋ก ํ๋ ค๋ฉด Behavior ๋๋ Attached Property๋ฅผ ์ฌ์ฉํ๋ ๋ฐฉ์๋ ์์ต๋๋ค.
ํ์ง๋ง ๊ธฐ๋ณธ ๊ตฌ์กฐ์์๋ code-behind๋ ์ถฉ๋ถํ ์์ ํ๊ฒ ์ฌ์ฉํ ์ ์์ต๋๋ค.
5. ๋ง๋ฌด๋ฆฌ ๋ฐ ๋ค์ ํธ ์๊ณ
TreeView์์ ๋๋๊ทธ ์ค ๋๋กญ์ ์ฌ์ฉ์ ๊ฒฝํ์ ํฌ๊ฒ ํฅ์์ํฌ ์ ์์ต๋๋ค.
์ด๋ฒ ๊ธ์์๋ ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ํํ์ ๋
ธ๋ ์ด๋์ ๊ตฌํํด๋ดค์ต๋๋ค.
- ViewModel์ ๊ตฌ์กฐ๋ฅผ ์ ์งํ๋ฉด์๋ DragDrop ๊ตฌํ
- TreeViewItem ์ฐพ๊ธฐ ๋ฐ VisualTree ํ์
- ์์ ์ด๋ ๋ฐ ์ ๊ฑฐ ๋ก์ง ์บก์ํ
๋ค์ ์๋ฆฌ์ฆ์์๋ TreeView์ ์ฒดํฌ๋ฐ์ค(Checkbox) ๋
ธ๋ ์ฒ๋ฆฌ์ ๋ํด ๋ค๋ค๋ณผ ์์ ์
๋๋ค.
๊ฐ์ฌํฉ๋๋ค! ๐ ๋ ๋ค๋ค๋ณด๊ณ ์ถ์ TreeView ๊ธฐ๋ฅ์ด ์๋ค๋ฉด ๋๊ธ๋ก ์๋ ค์ฃผ์ธ์!
'C#' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
WPF TreeView ๊ฒ์ ๊ธฐ๋ฅ ๊ตฌํ (0) | 2025.04.12 |
---|---|
WPF TreeView ์ฒดํฌ๋ฐ์ค ๋ ธ๋ ๊ตฌํ (0) | 2025.04.11 |
WPF TreeView ContextMenu ๋ ธ๋ ์ถ๊ฐ/์ญ์ (0) | 2025.04.10 |
WPF TreeView์ MVVM Command ๋ฐ์ธ๋ฉ (0) | 2025.04.09 |
WPF TreeView์ HierarchicalDataTemplate ์์ ์ ๋ณต (0) | 2025.04.08 |