๐พ WPF TreeView ์ ํ ์ํ ์ ์ฅ ๋ฐ ๋ณต์
์ด๋ฒ ํฌ์คํธ์์๋ WPF์ TreeView์์ ์ ํ๋ ๋
ธ๋์ ์ํ๋ฅผ ์ ์ฅํ๊ณ ๋ณต์ํ๋ ๋ฐฉ๋ฒ์ ๋ค๋ฃน๋๋ค.
MVVM ๊ตฌ์กฐ๋ก IsSelected
๋ฐ์ธ๋ฉ์ ๊ตฌํํ๊ณ , ์ ํ๋ ํญ๋ชฉ์ JSON์ผ๋ก ์ ์ฅํด ์ฑ ์ฌ์คํ ์ ์๋ ๋ณต์ํ๋ ์์ ๋ฅผ ์๊ฐํฉ๋๋ค.
๋ชฉ์ฐจ
1. Category ๋ชจ๋ธ์ IsSelected ์ถ๊ฐ
public class Category : INotifyPropertyChanged
{
public string Name { get; set; }
public ObservableCollection<Category> Children { get; set; } = new();
private bool _isSelected;
public bool IsSelected
{
get => _isSelected;
set { _isSelected = value; OnPropertyChanged(nameof(IsSelected)); }
}
public event PropertyChangedEventHandler? PropertyChanged;
protected void OnPropertyChanged(string propertyName)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
2. ViewModel์์ ์ ํ ํญ๋ชฉ ์ถ์ ๋ฐ ์ ์ฅ
public class MainViewModel
{
public ObservableCollection<Category> Categories { get; set; }
public MainViewModel()
{
Categories = LoadFromJson();
}
public List<string> GetSelectedPaths()
{
List<string> selected = new();
Traverse(Categories, "", selected);
return selected;
}
private void Traverse(IEnumerable<Category> list, string path, List<string> selected)
{
foreach (var item in list)
{
string currentPath = string.IsNullOrEmpty(path) ? item.Name : $"{path}/{item.Name}";
if (item.IsSelected)
selected.Add(currentPath);
Traverse(item.Children, currentPath, selected);
}
}
public void SaveSelection()
{
var selected = GetSelectedPaths();
File.WriteAllText("selection.json", JsonSerializer.Serialize(selected));
}
public ObservableCollection<Category> LoadFromJson()
{
var categories = new ObservableCollection<Category>
{
new Category
{
Name = "ํ๋ก๊ทธ๋๋ฐ",
Children = new ObservableCollection<Category>
{
new Category { Name = "C#" },
new Category { Name = "Java" }
}
},
new Category
{
Name = "๋ฐ์ดํฐ๋ฒ ์ด์ค",
Children = new ObservableCollection<Category>
{
new Category { Name = "Oracle" },
new Category { Name = "MySQL" }
}
}
};
if (File.Exists("selection.json"))
{
var saved = JsonSerializer.Deserialize<List<string>>(File.ReadAllText("selection.json"));
ApplySelection(categories, "", saved);
}
return categories;
}
private void ApplySelection(IEnumerable<Category> list, string path, List<string>? selected)
{
foreach (var item in list)
{
string currentPath = string.IsNullOrEmpty(path) ? item.Name : $"{path}/{item.Name}";
if (selected?.Contains(currentPath) == true)
item.IsSelected = true;
ApplySelection(item.Children, currentPath, selected);
}
}
}
3. XAML์์ TreeViewItem ์คํ์ผ ์ ์
<TreeView ItemsSource="{Binding Categories}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
<Button Content="์ ํ ์ ์ฅ" Command="{Binding SaveCommand}" Margin="10" />
TreeViewItem์ IsSelected
์์ฑ์ ๋ฐ์ธ๋ฉํ๋ฉด MVVM ๊ตฌ์กฐ์์๋ ์ ํ ์ํ๋ฅผ ์ ์ดํ ์ ์์ต๋๋ค.
4. ์ ํ ํญ๋ชฉ JSON ์ ์ฅ ๋ฐ ๋ณต์
- ์ ํ ํญ๋ชฉ์
๊ฒฝ๋ก(path)
ํํ๋ก ์ ์ฅ๋จ - ์์:
"ํ๋ก๊ทธ๋๋ฐ/C#"
,"๋ฐ์ดํฐ๋ฒ ์ด์ค/Oracle"
- ์ ์ฅ๋ JSON ํ์ผ์ ํตํด ์ฑ ์ฌ์คํ ์ ์ํ ๋ณต์
์ค์ ์ ์ฅ ์์ (selection.json
):
[
"ํ๋ก๊ทธ๋๋ฐ/C#",
"๋ฐ์ดํฐ๋ฒ ์ด์ค/Oracle"
]
5. ๋ง๋ฌด๋ฆฌ ๋ฐ ๋ค์ ํธ ์๊ณ
TreeView์ ์ ํ ์ํ ์ ์ฅ ๊ธฐ๋ฅ์ UX ์ธก๋ฉด์์ ์ค์ํ ์ญํ ์ ํฉ๋๋ค.
์ฌ์ฉ์์ ์ ํ์ ๊ธฐ์ตํ๊ณ ๋ณต์ํ๋ ๊ธฐ๋ฅ์ ์ฑ์ ์์ฑ๋๋ฅผ ๋์ฌ์ค๋๋ค.
- MVVM ๊ตฌ์กฐ๋ก๋ IsSelected ๋ฐ์ธ๋ฉ ๊ฐ๋ฅ
- ๊ฒฝ๋ก ๊ธฐ๋ฐ ์ ์ฅ์ผ๋ก ํธ๋ฆฌ ๊ตฌ์กฐ ์ ์ฒด ์ง์
- JSON ํ์ผ์ ํตํด ๊ฐํธํ ๋ณต์ ์ฒ๋ฆฌ
๋ค์ ํธ์์๋ TreeView ๋๋๊ทธ ์ค ๋๋กญ(Drag & Drop) ๊ธฐ๋ฅ์ ๊ตฌํํด๋ณด๊ฒ ์ต๋๋ค! ๐ฑ๏ธ ๋ ธ๋ ์์น๋ฅผ ์ง์ ๋ฐ๊พธ๋ ์ ์ ์ธํฐ๋์ ์ ๋ค๋ค๋ณด์์.
'C#' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
WPF TreeView ContextMenu๋ก ๋ ธ๋ ์ญ์ ๋ฐ ์ด๋ฆ ๋ณ๊ฒฝ (0) | 2025.04.15 |
---|---|
WPF TreeView ๋๋๊ทธ ์ค ๋๋กญ ๊ธฐ๋ฅ ๊ตฌํ (0) | 2025.04.14 |
WPF TreeView ๊ฒ์ ๊ธฐ๋ฅ ๊ตฌํ (0) | 2025.04.12 |
WPF TreeView ์ฒดํฌ๋ฐ์ค ๋ ธ๋ ๊ตฌํ (0) | 2025.04.11 |
WPF TreeView Drag & Drop ๊ตฌํ (0) | 2025.04.10 |