c# wpf TreeView проблема с выбором элемента root - PullRequest
0 голосов
/ 09 января 2020

У меня странная проблема с WPF TreeView в C#. При запуске моего приложения я хочу развернуть и свернуть все узлы из выбранного узла TreeView. Когда я выбираю root узел, свойство IsSelected не обновляется. Когда я вручную раскрываю узел root и нажимаю на дочерний узел, свойство IsSelected обновляется. Проблема в том, что когда я использую контекстное меню, чтобы развернуть и свернуть дочерние узлы на узле root, узел root не раскрывается. Когда я раскрываю узел root после использования контекстного меню, я вижу, что все дочерние узлы были развернуты правильно.

Я много искал в inte rnet, но не смог найти аналогичный вопрос. Кроме того, я прочитал много уроков и инструкций по C# WPF TreeViews. Пока что моя реализация кажется мне правильной, поэтому я не понимаю, почему свойство IsSelected root не устанавливается при нажатии на узел.

Кто-нибудь может мне помочь? Я реализовал TreeView, ViewModel и Model следующим образом. Заранее спасибо

BR

Майкл


<TreeView
Grid.Row="1"
Grid.Column="0"
Margin="5"
ContextMenuOpening="FrameworkElement_OnContextMenuOpening"
ItemsSource="{Binding HierarchialTestObjects}"
SelectedItemChanged="TreeView_OnSelectedItemChanged">
<TreeView.ItemTemplate>
    <HierarchicalDataTemplate DataType="{x:Type vm:HierarchialTestObjectViewModel}" ItemsSource="{Binding Childs}">
        <HierarchicalDataTemplate.ItemContainerStyle>
            <Style TargetType="{x:Type TreeViewItem}">
                <Setter Property="IsSelected" Value="{Binding Path=IsSelected, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
                <Setter Property="IsExpanded" Value="{Binding Path=IsExpanded, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
            </Style>
        </HierarchicalDataTemplate.ItemContainerStyle>
        <StackPanel Orientation="Horizontal">
            <Label Content="{Binding Path=Name}" />
        </StackPanel>
    </HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ContextMenu>
    <ContextMenu>
        <MenuItem Command="{Binding ExpandAllTreeItemsCommand}" Header="Expand All" />
        <MenuItem Command="{Binding CollapseAllTreeItemsCommand}" Header="Collapse All" />
    </ContextMenu>
</TreeView.ContextMenu>

namespace mynamespace.subspace.report.data
{
// ... usings removed

public class HierarchialTestObject
{
    public string Name { get; set; }

    public List<HierarchialTestObject> Childs { get; set; } = new List<HierarchialTestObject>();

    public List<string> Path { get; set; } = new List<string>();

    public List<string> ParentPath => Path?.AsEnumerable()?.Reverse()?.Skip(1)?.Reverse()?.ToList();

    public IHierarchialItem Item { get; set; }

    public HierarchialTestObject(IHierarchialItem item)
    {
        Item = item;
        Name = item.ShortDescription;
    }

    public void SetChilds(IEnumerable<IHierarchialItem> childs)
    {
        childs.ToList().ForEach(c => Childs?.Add(new HierarchialTestObject(c)));
    }
}
}

ViewModel

namespace mynamespace.subspace.report.viewmodel
{
// ... usings removed

public class HierarchialTestObjectViewModel : ViewModelBase
{
    public delegate void NotifyTreeItemSelected(HierarchialTestObjectViewModel item);

    public event NotifyTreeItemSelected OnTreeItemSelected;

    private bool _isSelected;
    public bool IsSelected
    {
        get { return _isSelected; }
        set
        {
            if (_isSelected != value)
            {
                SetProperty(ref _isSelected, value, nameof(IsSelected));
                if (value)
                {
                    // this is a hack-ish workaround because i cannot use PRISM or other similar libs
                    OnTreeItemSelected?.Invoke(this);
                }
            }
        }
    }

    private bool _isExpanded;
    public bool IsExpanded
    {
        get { return _isExpanded; }
        set
        {
            if (_isExpanded != value)
            {
                SetProperty(ref _isExpanded, value, nameof(IsExpanded));
                RaisePropertyChanged(nameof(IconType));
            }
        }
    }



    private string _name;
    public string Name
    {
        get { return _name; }
        set
        {
            if (_name != value)
            {
                SetProperty(ref _name, value, nameof(Name));
            }
        }
    }



    private IHierarchialItem _item;
    public IHierarchialItem Item
    {
        get { return _item; }
        set
        {
            if (_item != value)
            {
                SetProperty(ref _item, value, nameof(Item));
            }
        }
    }


    private List<string> _path;
    public List<string> Path
    {
        get { return _path; }
        set
        {
            if (_path != value)
            {
                SetProperty(ref _path, value, nameof(Path));
            }
        }
    }

    private ObservableCollection<HierarchialTestObjectViewModel> _childs;
    public ObservableCollection<HierarchialTestObjectViewModel> Childs
    {
        get { return _childs; }
        set
        {
            if (_childs != value)
            {
                SetProperty(ref _childs, value, nameof(Childs));
            }
        }
    }

    public HierarchialTestObjectViewModel(HierarchialTestObject obj)
    {
        var childs = obj.Childs.Select(c => new HierarchialTestObjectViewModel(c)).ToList();
        Path = obj.Path ?? new List<string>();
        Childs = new ObservableCollection<HierarchialTestObjectViewModel>(childs);
        Item = obj.Item;
        Name = obj.Name;
    }

    public void ExpandRecursively()
    {
        IsExpanded = true;
        Childs?.ToList().ForEach(c => c.ExpandRecursively());
    }

    public void CollapseRecursively()
    {
        IsExpanded = false;
        Childs?.ToList().ForEach(c => c.CollapseRecursively());
    }

}
}

1 Ответ

1 голос
/ 09 января 2020

Проблема в том, что вы никогда не устанавливаете ItemContainerStyle из root пунктов. Только для детских предметов. Поэтому свойства IsExpanded и IsSelected моделей данных root не связаны с их контейнером данных TreeViewItem.

Просто установите TreeView.ItemContainerStyle для решения проблемы:

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

Примечания

TreeViewItem.IsSelected по умолчанию настроен на BindingMode.TwoWay .
TreeViewItem.IsSelected и TreeViewItem.IsExpanded имеют для Binding.UpdateSourceTrigger значение по умолчанию UpdateSourceTrigger.PropertyChanged, которое является значением по умолчанию для all Dependency Property, за исключением того, что они явно настроены на другой триггер, который имеет место только для нескольких свойств (например, TextBox.Text по умолчанию установлено на UpdateSourceTrigger.LostFocus).
Таким образом вы можете уменьшить код и улучшить читаемость.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...