Страницы

Поиск по вопросам

Показаны сообщения с ярлыком treeview. Показать все сообщения
Показаны сообщения с ярлыком treeview. Показать все сообщения

вторник, 31 декабря 2019 г.

Програмно снять выделение с TreeView

#c_sharp #wpf #treeview


Необходимо снять выделение с TreeView при удалении элемента. По умолчанию выделяется
первый элемент, а задать SelectedItem = null не получается так как это свойство только
для чтения. Как это реализовать?
    


Ответы

Ответ 1



В обычном случае вы не создаёте элементы TreeView вручную, а генерируете их автоматически, используя ItemsSource + ItemTemplate. При этом treeView.SelectedItem будет не TreeViewItem, а элемент коллекции, привязанной к ItemsSource. Так что приведение к типу TreeViewItem не сработает. Чтобы получить из этого собственно визуальный элемент, обычно применяется конструкция list.ItemContainerGenerator.ContainerFromItem(selectedItem);. Но в случае TreeView она, к сожалению, работает лишь на верхнем уровне: каждый TreeViewItem рекурсивно сам является TreeView, и заботится сам о своих контейнерах, так что list.ItemContainerGenerator ничего не знает о вложенных частях дерева. Неплохое универсально работающее (но многословное) решение — следить за изменением выделенного элемента, и запоминать его в code behind. Пример: public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DataContext = new[] { new DataHolder("Привет-привет", null), new DataHolder("Пока-пока", null), new DataHolder(null, new[] { new DataHolder("Я очень", null), new DataHolder("буду ждать", new[] { new DataHolder("звонка", null) }), }), }; TreeViewItem selectedItemTV = null; TV.AddHandler(TreeViewItem.SelectedEvent, (RoutedEventHandler)((sender, args) => selectedItemTV = (TreeViewItem)args.OriginalSource)); TreeViewItem selectedItemTV2 = null; TV2.AddHandler(TreeViewItem.SelectedEvent, (RoutedEventHandler)((sender, args) => selectedItemTV2 = (TreeViewItem)args.OriginalSource)); var dt = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(5) }; dt.Tick += (sender, args) => { if (selectedItemTV != null) { selectedItemTV.IsSelected = false; selectedItemTV = null; } if (selectedItemTV2 != null) { selectedItemTV2.IsSelected = false; selectedItemTV2 = null; } }; dt.Start(); } } public class DataHolder { public string Key { get; private set; } public IEnumerable Value { get; private set; } public DataHolder(string key, IEnumerable value) { Key = key; Value = value; } } Ну или вы можете подписаться обычным способом в XAML'е: TreeViewItem.Selected="TVItemSelected". Ещё есть решение с полным рекурсивным обходном дерева, и поиском TreeViewItem'а, как описано здесь. Но оно мне кажется слишком уж неэффективным, и наверняка плохо дружит с виртуализацией.

Ответ 2



Реализовал так: ((TreeViewItem)treeView.SelectedItem).IsSelected = false;

четверг, 26 декабря 2019 г.

JavaFx: TreeView

#java #javafx #treeview #кастомизация


Как можно сделать такие полосы (штриховку) в TreeView? 
Перегуглил как мог, нигде и намёка нет на подобную штуку 

javafx: 
 
swing: 
 
    


Ответы

Ответ 1



Очень просто: TreeItem item=new TreeItem(new HBox(new ImageView(new Image(inputStream)),new Label("Root"))); item.getChildren().add(new TreeItem(new HBox(new ImageView(new Image(inputStream)),new Label("Item1")))); TreeView treeView=new TreeView(item); При желании можно сделать вместо HBox класс, который будет автоматически менять изображение в зависимости от строения TreeItem.

воскресенье, 22 декабря 2019 г.

Изменение внешнего вида выбранного TreeViewItem

#c_sharp #wpf #xaml #treeview


Здравствуйте, имею в своём приложении собственный обозреватель папок. В целом он
функционирует хорошо, но есть один недочёт: если нажать на тот, или иной TreeViewItem,
то он обводиться стандартным и не красивым синим прямоугольником. Как это можно изменить?
Надо заменить этот прямоугольник чем-то...

  
    


Ответы

Ответ 1



Элементы TreeView неявно (или явно, если ваш код это указывает) кладутся в TreeViewItem. Визуализация выбранных элементов/элементов с фокусом делается в TreeViewItem'е. То есть, вам нужно переопределить, как он выглядит. Насколько я понимаю, для этого вам нужно поменять его стиль (ItemContainerStyle), причём целиком. Кажется, нет возможности подменять лишь часть стиля. Вот здесь дан код, как этот стиль определён по умолчанию. (Ищите Style TargetType="controls:TreeViewItem".) Определение реально большое, но правильное. Его можно переопределить например так, как описано здесь (хотя техника немного устарела: вместо триггеров нужно бы использовать VisualState). Или вы можете базироваться на стиле по умолчанию, и подменить нужные части.

Ответ 2



Вам следует переопределить системный цвет, который хранится в ресурсе SystemColors.HighlightBrushKey на который и ссылается стандартный стиль TreeViewItem. Пример:

воскресенье, 15 декабря 2019 г.

Как сделать перемещение нод TreeView?

#c_sharp #wpf #treeview #drag_n_drop


Ветки нужно перемещать ноды на уровень выше в материнские и ниже в дочерние.

Моё дерево:




    


На другом примере работает нормально но в моём нет.

1
-2
--3
4
-5
--6


У меня любую ноду можно переместить в корень или в ту же ноду, в которой и находится.
Например 3 можно переместить в корень либо в 2, где он уже есть.

В другие почему-то не перемещается.

public class Node
        {
            public string name_ { get; set; }


            public ObservableCollection collection_node { get; set; }

            public Node()
            {

                collection_node = new ObservableCollection();
            }
        }

    


Ответы

Ответ 1



Рекомендую использовать GongSolutions.WPF.DragDrop, которую можно легко установить добавив соответствующий NuGet-пакет. Дальше к своему TreeViewпросто добавляете два свойства: где dd: xmlns:dd="clr-namespace:GongSolutions.Wpf.DragDrop;assembly=GongSolutions.Wpf.DragDrop" Теперь вы можете легко перетаскивать элементы:

воскресенье, 7 июля 2019 г.

KeyBinding в TreeView для элементов

Пытаюсь разобраться с привязкой "горячих" клавиш к дереву. Не получается передать в качестве параметра к команде саму выбранную ветку дерева.
Хочу, чтобы при нажатии F2 открывалось окно редактирования наименования ветки дерева (или даже лучше не отдельного окна, а напрямую в самом дереве, но с этим как мне показалось еще более сложно. Хотя бы через дополнительное окно)
На текущий момент через контекстное меню работает правильно, так как к каждому элементу дерева привязано свое контекстное меню и в качестве параметра эта ветка и передается.
А вот с привязкой команды по горячей кнопке для меня оказалось сложнее.
Команда объявлена в окне:

Горячая клавиша объявлена в дереве:

В качестве контекста у дерева в инициализации формы присваивается список:
tp_View.DataContext = TPTree;
public ObservableCollection TPTree = new ObservableCollection();
Который отдельно заполняется через другие команды.
Что необходимо передать в CommandParameter, чтобы в команду e.Parameter помещался не весь список дерева, а только выбранный в данный момент? Или нужно делать вообще по другому и я не в ту сторону копаю?
PS: Команда должна привязываться именно к дереву, так как на форме будет несколько деревьев и у каждого клавиша F2 должна вызывать редактирование именно своей ветки.
Спасибо.


Ответ

У вас команда привязана ко всему TreeView, соответственно Binding возвращает TreeView.DataContext, а он может быть каким угодно. Решение очевидное - передавать явно текущий элемент TreeView

Ну и так как при активном TreeView выбранного элемента может не быть - вы должны в коде команды первым делом проверить параметр:
void RenameNode(NodeVm node) { if (node == null) return;
Либо составить правильный CanExecute, но это сложнее, думаю.


Так как же все-таки реализовать редактирование элемента прямо в дереве? Давайте попробуем сделать это!
Я воспользуюсь паттерном MVVM, у меня есть соответствующие заготовки для классов команды:
class DelegateCommand : ICommand { protected readonly Predicate _canExecute; protected readonly Action _execute;
public event EventHandler CanExecuteChanged;
public DelegateCommand(Action execute) : this(execute, _ => true) { }
public DelegateCommand(Action execute, Predicate canExecute) { _execute = execute ?? throw new ArgumentNullException(nameof(execute)); _canExecute = canExecute ?? throw new ArgumentNullException(nameof(canExecute)); }
public bool CanExecute(object parameter) => _canExecute(parameter);
public void Execute(object parameter) => _execute(parameter);
public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty); }
и база для VM:
abstract class Vm : INotifyPropertyChanged { protected bool Set(ref T field, T value, [CallerMemberName]string propertyName = null) { if (EqualityComparer.Default.Equals(field, value)) return false; field = value; NotifyPropertyChanged(propertyName); return true; }
protected void NotifyPropertyChanged([CallerMemberName]string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
public event PropertyChangedEventHandler PropertyChanged; }
Теперь, для этого примера я написал такой класс, представляющий один элемент дерева:
class NodeVm : Vm { string name; public string Name { get => name; set => Set(ref name, value, nameof(Name)); }
ObservableCollection children; public ObservableCollection Children { get => children; set => Set(ref children, value, nameof(Children)); }
public NodeVm() { }
public NodeVm(string name, IEnumerable children) { Name = name; Children = new ObservableCollection(children); }
public NodeVm(string name, params NodeVm[] children) : this(name, children.AsEnumerable()) { } }
Теперь главная VM, она должна содержать коллекцию элементов корневого уровня, я вместо этого создал один корневой элемент, но привязку мы сделаем не к нему, а к коллекции его дочерних элементов. Также нам потребуется одно свойство для обозначения текущего редактируемого элемента и одна команда для установки этого элемента, итого:
class MainVm : Vm { public NodeVm RootNode { get; }
NodeVm editableNode; public NodeVm EditableNode { get => editableNode; set => Set(ref editableNode, value, nameof(EditableNode)); }
public DelegateCommand SetEditableNodeCommand { get; }
public MainVm() { RootNode = new NodeVm("RootNode", new NodeVm("Node 1", new NodeVm("Node 7"), new NodeVm("Node 8", new NodeVm("Node 11"), new NodeVm("Node 12", new NodeVm("Node 14"), new NodeVm("Node 15"), new NodeVm("Node 16")), new NodeVm("Node 13")), new NodeVm("Node 9"), new NodeVm("Node 10")), new NodeVm("Node 2"), new NodeVm("Node 3"), new NodeVm("Node 4", new NodeVm("Node 17"), new NodeVm("Node 18", new NodeVm("Node 21"), new NodeVm("Node 22", new NodeVm("Node 24"), new NodeVm("Node 25"), new NodeVm("Node 26")), new NodeVm("Node 23")), new NodeVm("Node 19"), new NodeVm("Node 20")), new NodeVm("Node 5"), new NodeVm("Node 6")); SetEditableNodeCommand = new DelegateCommand(o => EditableNode = (NodeVm)o); } }
Теперь разметка, я в корневой грид окна просто кладу TreeView, у меня больше ничего не будет:
...
Шаблон элементов TreeView будет состоять из TextBlock в обычном режиме и TextBox в режиме редактирования, поэтому просто обернем их в Grid, также я немного подшаманил со стилем TextBox, чтобы он выглядел более "плоско":
... ...
Теперь нам нужно чтобы если текущий элемент в режиме редактирования отображался TextBox, иначе TextBlock, для этого воспользуемся конвертером (я стащил его из этого ответа и немного "докрутил"):
class ObjectsEqualConverter : MarkupExtension, IMultiValueConverter { public T EqualValue { get; set; } public T NotEqualValue { get; set; } public object Convert(object[] values, Type tt, object p, CultureInfo ci) => values[0] == values[1] ? EqualValue : NotEqualValue; public object[] ConvertBack(object value, Type[] tt, object p, CultureInfo ci) => throw new NotImplementedException(); public override object ProvideValue(IServiceProvider serviceProvider) => this; }
class VisibilityEqualConverter : ObjectsEqualConverter { }
Добавляем в TextBlock:

и в TextBox:

Теперь, по клавише F2 нужно выбранный элемент помечать как редактируемый, это у нас уже показано как сделать:

Ну и еще, чтобы при уходе курсора из TextBox редактируемый элемент сбрасывался нужно по событию LostFocus выполнить эту же команду с параметром null, я сделаю это с помощью пакета System.Windows.Interactivity.WPF подключенного из NuGet:

В заголовке окна подключено пространство имен: xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
Итого:

вторник, 25 декабря 2018 г.

Програмно снять выделение с TreeView

Необходимо снять выделение с TreeView при удалении элемента. По умолчанию выделяется первый элемент, а задать SelectedItem = null не получается так как это свойство только для чтения. Как это реализовать?


Ответ

В обычном случае вы не создаёте элементы TreeView вручную, а генерируете их автоматически, используя ItemsSource + ItemTemplate
При этом treeView.SelectedItem будет не TreeViewItem, а элемент коллекции, привязанной к ItemsSource. Так что приведение к типу TreeViewItem не сработает.
Чтобы получить из этого собственно визуальный элемент, обычно применяется конструкция list.ItemContainerGenerator.ContainerFromItem(selectedItem);. Но в случае TreeView она, к сожалению, работает лишь на верхнем уровне: каждый TreeViewItem рекурсивно сам является TreeView, и заботится сам о своих контейнерах, так что list.ItemContainerGenerator ничего не знает о вложенных частях дерева.
Неплохое универсально работающее (но многословное) решение — следить за изменением выделенного элемента, и запоминать его в code behind.
Пример:


public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DataContext = new[] { new DataHolder("Привет-привет", null), new DataHolder("Пока-пока", null), new DataHolder(null, new[] { new DataHolder("Я очень", null), new DataHolder("буду ждать", new[] { new DataHolder("звонка", null) }), }), };
TreeViewItem selectedItemTV = null; TV.AddHandler(TreeViewItem.SelectedEvent, (RoutedEventHandler)((sender, args) => selectedItemTV = (TreeViewItem)args.OriginalSource));
TreeViewItem selectedItemTV2 = null; TV2.AddHandler(TreeViewItem.SelectedEvent, (RoutedEventHandler)((sender, args) => selectedItemTV2 = (TreeViewItem)args.OriginalSource));
var dt = new DispatcherTimer() { Interval = TimeSpan.FromSeconds(5) }; dt.Tick += (sender, args) => { if (selectedItemTV != null) { selectedItemTV.IsSelected = false; selectedItemTV = null; } if (selectedItemTV2 != null) { selectedItemTV2.IsSelected = false; selectedItemTV2 = null; } }; dt.Start(); } }

public class DataHolder { public string Key { get; private set; } public IEnumerable Value { get; private set; }
public DataHolder(string key, IEnumerable value) { Key = key; Value = value; } }
Ну или вы можете подписаться обычным способом в XAML'е: TreeViewItem.Selected="TVItemSelected"

Ещё есть решение с полным рекурсивным обходном дерева, и поиском TreeViewItem'а, как описано здесь. Но оно мне кажется слишком уж неэффективным, и наверняка плохо дружит с виртуализацией.

пятница, 30 ноября 2018 г.

JavaFx: TreeView

Как можно сделать такие полосы (штриховку) в TreeView? Перегуглил как мог, нигде и намёка нет на подобную штуку
javafx: swing:


Ответ

Очень просто:
TreeItem item=new TreeItem(new HBox(new ImageView(new Image(inputStream)),new Label("Root"))); item.getChildren().add(new TreeItem(new HBox(new ImageView(new Image(inputStream)),new Label("Item1")))); TreeView treeView=new TreeView(item);
При желании можно сделать вместо HBox класс, который будет автоматически менять изображение в зависимости от строения TreeItem.

пятница, 16 ноября 2018 г.

Изменение внешнего вида выбранного TreeViewItem

Здравствуйте, имею в своём приложении собственный обозреватель папок. В целом он функционирует хорошо, но есть один недочёт: если нажать на тот, или иной TreeViewItem, то он обводиться стандартным и не красивым синим прямоугольником. Как это можно изменить? Надо заменить этот прямоугольник чем-то...


Ответ

Элементы TreeView неявно (или явно, если ваш код это указывает) кладутся в TreeViewItem. Визуализация выбранных элементов/элементов с фокусом делается в TreeViewItem'е. То есть, вам нужно переопределить, как он выглядит.
Насколько я понимаю, для этого вам нужно поменять его стиль (ItemContainerStyle), причём целиком. Кажется, нет возможности подменять лишь часть стиля. Вот здесь дан код, как этот стиль определён по умолчанию. (Ищите Style TargetType="controls:TreeViewItem".) Определение реально большое, но правильное.
Его можно переопределить например так, как описано здесь (хотя техника немного устарела: вместо триггеров нужно бы использовать VisualState). Или вы можете базироваться на стиле по умолчанию, и подменить нужные части.

пятница, 26 октября 2018 г.

Как сделать перемещение нод TreeView?

Ветки нужно перемещать ноды на уровень выше в материнские и ниже в дочерние.
Моё дерево:
>

На другом примере работает нормально но в моём нет.
1 -2 --3 4 -5 --6
У меня любую ноду можно переместить в корень или в ту же ноду, в которой и находится. Например 3 можно переместить в корень либо в 2, где он уже есть.
В другие почему-то не перемещается.
public class Node { public string name_ { get; set; }
public ObservableCollection collection_node { get; set; }
public Node() {
collection_node = new ObservableCollection(); } }


Ответ

Рекомендую использовать GongSolutions.WPF.DragDrop, которую можно легко установить добавив соответствующий NuGet-пакет.
Дальше к своему TreeViewпросто добавляете два свойства:

где dd
xmlns:dd="clr-namespace:GongSolutions.Wpf.DragDrop;assembly=GongSolutions.Wpf.DragDrop"
Теперь вы можете легко перетаскивать элементы: