Как показать TreeView на ComboBox.SelectedItem! = Null - PullRequest
1 голос
/ 04 апреля 2019

Я пишу Проводник каталогов, используя MVVM-Pattern.

Мой пользовательский интерфейс состоит из ComboBox, который содержит путь к каталогу, и TreeView, который действует как проводник каталога.

Я борюсь с тем, чтобы показывать TreeView только тогда, когда выбран элемент ComboBox, но я не знаю, как этого добиться.

Вот мой код.

MainWindow.xaml

<StackPanel>

        <TextBlock TextWrapping="Wrap" Text="Select a Repository Directory" Margin="10,0" FontSize="16"/>
        <ComboBox x:Name="cmbx" Margin="10,30,10,0" ItemsSource="{Binding CmbxItems}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}"/>

        <TreeView x:Name="FolderView" Height="250" Margin="10,50,10,0" ItemsSource="{Binding Items}" Visibility="{Binding IsItemSelected}">

            <TreeView.ItemContainerStyle>
                <Style TargetType="{x:Type TreeViewItem}">
                    <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}"/>
                </Style>
            </TreeView.ItemContainerStyle>

            <TreeView.ItemTemplate>
                <HierarchicalDataTemplate ItemsSource="{Binding Children}">
                    <StackPanel Orientation="Horizontal">
                        <Image Name="img" Width="20" Margin="5" 
                                           Source="{Binding Type,
                                                Converter={x:Static local:HeaderToImageConverter.ConverterInstance}}"/>
                        <TextBlock VerticalAlignment="Center" Text="{Binding Name}"/>
                    </StackPanel>

                </HierarchicalDataTemplate>
            </TreeView.ItemTemplate>

        </TreeView>

    </StackPanel>

ViewModel.cs

/// <summary>
    /// ViewModel for the main Directory view.
    /// </summary>
    class ViewModel : BaseViewModel
    {

        #region Properties

        public ObservableCollection<DirectoryStructureViewModel> Items { get; set; }

        public ObservableCollection<string> CmbxItems { get; set; }

        public bool _itemIsSelected = false;

        public bool ItemIsSelected
        {
            get
            {
                return _itemIsSelected;
            }
            set
            {
                _itemIsSelected = value;
            }
        }

        public string _selectedItem;

        public string SelectedItem
        {
            get
            {
                return _selectedItem;
            }
            set
            {               
                ItemIsSelected = true;
                MessageBox.Show("Selection changed");
                _selectedItem = value;
            }
        }

        #endregion

        #region Constructor

        /// <summary>
        /// Constructor.
        /// </summary>
        public ViewModel()
        {

            CmbxItems = new ObservableCollection<string>(){};

            CmbxItems.Add(DirectoryItem.rootPath);

            //Get initial directory.
            var children = DirectoryStructure.GetInitialDirectory();

            //Create view models from data.
            this.Items = new ObservableCollection<DirectoryStructureViewModel>(children.Select(dir => new DirectoryStructureViewModel(dir.FullPath, NodeTypes.Folder)));

        }

        #endregion
    }

BaseViewModel.cs

/// <summary>
    /// Base ViewModel that fires PropertyChanged events.
    /// </summary>
    public class BaseViewModel : INotifyPropertyChanged
    {
        //Event that is fired when aa child property changes its value.
        public event PropertyChangedEventHandler PropertyChanged = (sender, e) => { };
    }

DirectoryStructureViewModel.cs

public class DirectoryStructureViewModel : BaseViewModel
    {
        #region Properties

        public NodeTypes Type { get; set; }

        public string FullPath { get; set; }

        public string Name { get { return DirectoryStructure.GetDirectoryOrFileName(this.FullPath); } }

        /// <summary>
        /// List of all children contained in this item.  
        /// </summary>
        public ObservableCollection<DirectoryStructureViewModel> Children { get; set; }

        /// <summary>
        /// Indicates that this item can be expanded.
        /// </summary>
        public bool CanExpand { get { return this.Type != NodeTypes.File; } }

        /// <summary>
        /// Indicates if the current item is expanded.
        /// </summary>
        public bool IsExpanded
        {
            get
            {
                return this.Children?.Count(chldrn => chldrn != null) > 0;
            }
            set
            {
                if (value == true)
                {
                    Expand();
                }
                else
                {
                    this.ClearChildren();
                }
            }
        }

        public bool IsItemSelected { get; set; }

        #endregion

        #region Commands

        public ICommand ExpandCommand { get; set; }

        #endregion

        #region Constructor

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="fullPath">Path of this item.</param>
        /// <param name="type">Type of this item.</param>
        public DirectoryStructureViewModel(string fullPath, NodeTypes type)
        {
            this.ExpandCommand = new TreeViewRelayCommand(Expand);

            this.FullPath = fullPath;
            this.Type = type;

            this.ClearChildren();
        }

        #endregion

        #region Helper Methods

        //Removes all children from the list.
        private void ClearChildren()
        {
            this.Children = new ObservableCollection<DirectoryStructureViewModel>();

            if (this.Type != NodeTypes.File)
            {
                //Adds a dummy item to show the expand arrow.
                this.Children.Add(null);
            }
        }

        #endregion

        #region Functions

        /// <summary>
        /// Expands this directory and finds all children.
        /// </summary>
        private void Expand()
        {
            if (this.Type != NodeTypes.File)
            {

                //Find all children
                var children = DirectoryStructure.GetDirectoryContents(this.FullPath);
                this.Children = new ObservableCollection<DirectoryStructureViewModel>(children.Select(content => new DirectoryStructureViewModel(content.FullPath, content.Type)));
            }
            else
            {
                return;
            }
        }

        #endregion
    }

Commands.cs

class TreeViewRelayCommand : ICommand
    {
        #region Members

        private Action mAction;

        #endregion

        #region Events

        /// <summary>
        /// Event that is executed, when <see cref="CanExecute(object)"/> value has changed.
        /// </summary>
        public event EventHandler CanExecuteChanged = (sender, e) => { };

        #endregion

        #region Constructor

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="action"></param>
        public TreeViewRelayCommand(Action action)
        {
            mAction = action;
        }

        #endregion

        #region Command Methods

        public bool CanExecute(object parameter)
        {
            return true;
        }

        /// <summary>
        /// Executes the commands action.
        /// </summary>
        /// <param name="parameter"></param>
        public void Execute(object parameter)
        {
            mAction();
        }

        #endregion

    }

Редактировать: я использую FodyWeavers

1 Ответ

2 голосов
/ 04 апреля 2019

Вы почти у цели:

<TreeView ... Visibility="{Binding IsItemSelected}">

Одна проблема заключается в том, что вы привязываете Visibility к bool. В окне Outputs должна быть ошибка привязки (проверяйте ее сейчас и всегда на предмет возможных проблем с приложениями WPF).

Вы можете использовать BoolToVisibilityConverter (используя этот ответ ):

<someContainer.Resources>
    <BooleanToVisibilityConverter x:Key="converter" />
</someContainer.Resources>
...
<TreeView ... Visibility="{Binding IsItemSelected, Converter={StaticResource converter}}">

Вспомогательные поля не должны быть открытыми.

Вам следует (обычно) поднимать уведомления для всех свойств, используемых в привязках. Обычно в конце сеттера.

Я бы лично использовал свойство только для получения:

string _selectedItem;
public string SelectedItem
{
    get => _selectedItem;
    set
    {
        _selectedItem = value;
        OnPropertyChanged();
        OnPropertyChanged(nameof(IsItemSelected));
    }
}

public bool IsItemSelected => SelectedItem != null;

Также вы пропускаете правильный метод повышения события:

public class BaseViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    // can be public if you want to rise event from outside
    protected void OnPropertyChanged([CallerMemberName] string property = "") =>
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));

}
...