๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
C#

WPF TreeView ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ ๊ตฌํ˜„

by samie 2025. 4. 12.
WPF TreeView ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ ๊ตฌํ˜„

๐Ÿ” WPF TreeView ๋…ธ๋“œ ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ ๊ตฌํ˜„ํ•˜๊ธฐ

์ด๋ฒˆ ํฌ์ŠคํŠธ์—์„œ๋Š” WPF์˜ TreeView์—์„œ ๋…ธ๋“œ ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ์„ MVVM ํŒจํ„ด์œผ๋กœ ๊ตฌํ˜„ํ•ด๋ด…๋‹ˆ๋‹ค.
์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ๊ฒ€์ƒ‰์–ด์— ๋”ฐ๋ผ ํŠน์ • ๋…ธ๋“œ๋งŒ ํ‘œ์‹œํ•˜๊ฑฐ๋‚˜ ๊ฐ•์กฐํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ UX๋ฅผ ๊ฐœ์„ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

1. Category ๋ชจ๋ธ ํด๋ž˜์Šค


public class Category : INotifyPropertyChanged
{
    public string Name { get; set; }
    public ObservableCollection<Category> Children { get; set; } = new();

    private bool _isVisible = true;
    public bool IsVisible
    {
        get => _isVisible;
        set { _isVisible = value; OnPropertyChanged(nameof(IsVisible)); }
    }

    public event PropertyChangedEventHandler? PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

2. ViewModel ๊ตฌ์„ฑ ๋ฐ ๊ฒ€์ƒ‰ ๋กœ์ง


public class MainViewModel : INotifyPropertyChanged
{
    public ObservableCollection<Category> Categories { get; set; }

    private string _searchText = string.Empty;
    public string SearchText
    {
        get => _searchText;
        set
        {
            _searchText = value;
            OnPropertyChanged(nameof(SearchText));
            ApplySearch();
        }
    }

    public MainViewModel()
    {
        Categories = new ObservableCollection<Category>
        {
            new Category
            {
                Name = "ํ”„๋กœ๊ทธ๋ž˜๋ฐ",
                Children = new ObservableCollection<Category>
                {
                    new Category { Name = "C#" },
                    new Category { Name = "Java" },
                    new Category { Name = "Python" }
                }
            },
            new Category
            {
                Name = "๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค",
                Children = new ObservableCollection<Category>
                {
                    new Category { Name = "Oracle" },
                    new Category { Name = "MySQL" },
                    new Category { Name = "PostgreSQL" }
                }
            }
        };
    }

    private void ApplySearch()
    {
        foreach (var category in Categories)
        {
            FilterCategory(category, SearchText);
        }
    }

    private bool FilterCategory(Category category, string search)
    {
        bool match = category.Name.Contains(search, StringComparison.OrdinalIgnoreCase);
        bool childMatch = false;

        foreach (var child in category.Children)
        {
            childMatch |= FilterCategory(child, search);
        }

        category.IsVisible = match || childMatch;
        return category.IsVisible;
    }

    public event PropertyChangedEventHandler? PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
        => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

3. XAML์—์„œ ๊ฒ€์ƒ‰ UI ๋ฐ ๋ฐ”์ธ๋”ฉ


<StackPanel Margin="10">
  <TextBox Width="300" Margin="0,0,0,10"
           Text="{Binding SearchText, UpdateSourceTrigger=PropertyChanged}" 
           PlaceholderText="๋…ธ๋“œ ๊ฒ€์ƒ‰..." />

  <TreeView ItemsSource="{Binding Categories}">
    <TreeView.ItemTemplate>
      <HierarchicalDataTemplate ItemsSource="{Binding Children}">
        <TextBlock Text="{Binding Name}" />
      </HierarchicalDataTemplate>
    </TreeView.ItemTemplate>

    <TreeView.ItemContainerStyle>
      <Style TargetType="TreeViewItem">
        <Setter Property="Visibility" Value="{Binding IsVisible, Converter={StaticResource BoolToVisibilityConverter}}" />
      </Style>
    </TreeView.ItemContainerStyle>
  </TreeView>
</StackPanel>

๐Ÿ“Œ BoolToVisibilityConverter ์ถ”๊ฐ€


public class BoolToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        return (bool)value ? Visibility.Visible : Visibility.Collapsed;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

4. ํ•„ํ„ฐ๋ง ๋ฐ ๊ฐ•์กฐ ๋กœ์ง ์„ค๋ช…

  • SearchText ํ”„๋กœํผํ‹ฐ ๋ณ€๊ฒฝ ์‹œ ํ•„ํ„ฐ๋ง ๋™์ž‘
  • ๋…ธ๋“œ ์ด๋ฆ„์ด ๊ฒ€์ƒ‰์–ด๋ฅผ ํฌํ•จํ•˜๊ฑฐ๋‚˜ ์ž์‹ ์ค‘ ํฌํ•จ๋œ ๋…ธ๋“œ๊ฐ€ ์žˆ๋‹ค๋ฉด IsVisible = true
  • TreeViewItem์˜ Visibility ์†์„ฑ์œผ๋กœ ํ•„ํ„ฐ๋ง ์ ์šฉ

5. ๋งˆ๋ฌด๋ฆฌ ๋ฐ ๋‹ค์Œ ํŽธ ์˜ˆ๊ณ 

TreeView์˜ ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ์€ ๊ณ„์ธตํ˜• ๋ฐ์ดํ„ฐ๋ฅผ ๋น ๋ฅด๊ฒŒ ํƒ์ƒ‰ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค๋‹ˆ๋‹ค.
MVVM ๊ตฌ์กฐ๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์œ ์—ฐํ•˜๊ณ  ์žฌ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ UI๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ์€ ์‚ฌ์šฉ์ž ์ž…๋ ฅ ์ฆ‰์‹œ ๋ฐ˜์‘
  • ์ž์‹ ๋…ธ๋“œ ์žฌ๊ท€ ํƒ์ƒ‰์œผ๋กœ ์ „์ฒด ํŠธ๋ฆฌ์—์„œ ๊ฒ€์ƒ‰ ๊ฐ€๋Šฅ
  • ๊ฐ„๋‹จํ•œ Visibility ์ปจํŠธ๋กค๋กœ UX ๊ฐœ์„ 

๋‹ค์Œ ํŽธ์—์„œ๋Š” TreeView ๋…ธ๋“œ ์„ ํƒ ์ƒํƒœ ์œ ์ง€ ๋ฐ ๋ณต์› ๊ธฐ๋Šฅ์„ ๊ตฌํ˜„ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค! ๐Ÿ˜Š
๋ฐ์ดํ„ฐ ์ €์žฅ ๋ฐ ๋‹ค์‹œ ๋ถˆ๋Ÿฌ์˜ฌ ๋•Œ ์„ ํƒ ์ƒํƒœ๋ฅผ ์œ ์ง€ํ•˜๋Š” ๊ฒƒ๋„ ์‚ฌ์šฉ์ž ํŽธ์˜์„ฑ์— ํฐ ๋„์›€์ด ๋ฉ๋‹ˆ๋‹ค.