Когда к TreeView потребовалось сделать поиск, я сделал реализацию поиска вне контрола, а потом установил его свойство SelectedItem.
К сожалению у стандартного WPF контрола TreeView свойство SelectedItem только для чтения.
Поэтому мне пришлось ввести в каждый объект отображенный в TreeView свойства IsSelected и IsExpanded и связать эти свойства с соответствующим свойством TreeViewItem. (обычно именно это советуют в инете )
Таким образом нужно только установить у нужного объекта IsSelected, а у всех его предков IsExpanded.
Такая реализация работает прекрасно, но…
В общем поразмыслив немного пришел к такой концепции.
Вот так я осуществил поиск во всех шаблонах. Тут все относительно просто.
Вот логика поиска шаблонов, здесь все как в задуманно за исключением того, что предусмотрен альтернанивный способ задания путей поиска.
Это может быть необходимо с одной стороны потому, что не всегда срабатывает поиск биндингов.
(в случае когда для показа узла используется отдельный контрол со своей достаточно сложной логикой точно не работает).
Ну а с другой стороны иногда надо более точно указывать путь для поиска.
Путь задается через свойство FindPatches в виде строки «TypeName1:Property1,Property2 TypeName2:»
В данном случае для типа TypeName1 будет осуществлен поиск по свойствам Property1,Property2, а по TypeName2 поиск вообще не будет осуществлен.
Вот так выглядит поиск всех элементов содержащих искомый текст.
Вот так выглядит поиск всех элементов содержащих искомый текст.
Функцию GetTreeViewItem(control, L [0]); позволяющую надежно найти узел сделал благодаря
Deranged спасибо ему большое. На основе кода с указанного им сайта.
blogs.msdn.com/b/wpfsdk/archive/2010/02/23/finding-an-object-treeviewitem.aspx
Вот здесь новый код.
myTreeViewWith search.zip
Поиск работает в отдельном потоке.
Возможно сделать расширенный поиск что бы пользователь мог выбирать свойства по которым искать
и… Но это уже украшательства идея вот и работает.
P.S Автор поста, мой брат SergejSh — все вопросы и замечания к нему.
К сожалению у стандартного WPF контрола TreeView свойство SelectedItem только для чтения.
Поэтому мне пришлось ввести в каждый объект отображенный в TreeView свойства IsSelected и IsExpanded и связать эти свойства с соответствующим свойством TreeViewItem. (обычно именно это советуют в инете )
Таким образом нужно только установить у нужного объекта IsSelected, а у всех его предков IsExpanded.
Такая реализация работает прекрасно, но…
- Не очень красиво иметь бизнес объекту IsSelected и IsExpanded. А создавать вью модель для каждого пункта дерева муторно.
- Поиск по дереву должен быть реализован каждый раз в коде, что как минмум требует времени и… морока одним словом.
В общем поразмыслив немного пришел к такой концепции.
- Поиск в объекта осуществлять по части строкового представления объекта в узле дерева.
- На загрузке нужно пройти по всем вышестоящим HierarchicalDataTemplate и DataTemplate получить все биндинги на элементы объекта, а у HierarchicalDataTemplate путь к дочерним элементам.
- При поиске по иерархической коллекции Item Source. Использовать эти данные для поиска элемента.
- Все найденные элементы сохранять вместе путем к узлу.
- Когда надо выделить один из найденных элементов нужно просто выделить нужный узел по запомненному пути.
- Если нужно просто выделить объект (реализация двунаправленного свойства SelectedItem), то нужно найти его в иерархической ItemSource, запомнить путь к нему и потом выделить нужный узел по пу��и.
Вот так я осуществил поиск во всех шаблонах. Тут все относительно просто.
public override void OnApplyTemplate() { base.OnApplyTemplate(); templateDescrColl = new TemplateDescriptionCollection();//создал коллекцию описаний биндингов List<FrameworkElement> parents= Helper.GetAllParent(this);// поулучил спискок предков FindDataTemplatesResources(this.Resources);// поулучил описания шаблонов описанные ресурсах foreach (FrameworkElement parent in parents) // потом во всех предках { FindDataTemplatesResources(parent.Resources); } FindDataTemplatesResources(System.Windows.Application.Current.Resources);// и под конец в приложении }
Вот логика поиска шаблонов, здесь все как в задуманно за исключением того, что предусмотрен альтернанивный способ задания путей поиска.
Это может быть необходимо с одной стороны потому, что не всегда срабатывает поиск биндингов.
(в случае когда для показа узла используется отдельный контрол со своей достаточно сложной логикой точно не работает).
Ну а с другой стороны иногда надо более точно указывать путь для поиска.
Путь задается через свойство FindPatches в виде строки «TypeName1:Property1,Property2 TypeName2:»
В данном случае для типа TypeName1 будет осуществлен поиск по свойствам Property1,Property2, а по TypeName2 поиск вообще не будет осуществлен.
private void FindDataTemplatesResources(ResourceDictionary Resource) { foreach (object xxx in Resource.Values) { DataTemplate dataTemplate = xxx as DataTemplate; if (dataTemplate != null)// если a DataTemplate { bool hierarhical=false; if (!templateDescrColl.ExistType(dataTemplate.DataType as Type, out hierarhical))// если колекция нужного типа { TemplateBindingDescr templateBindingDescr = new TemplateBindingDescr();// HierarchicalDataTemplate hdt = xxx as HierarchicalDataTemplate;// если HierarchicalDataTemplate here if (hdt != null) { Binding ItemsSourceBinding = hdt.ItemsSource as Binding; string ItemsSourcePath = ItemsSourceBinding.Path.Path; templateBindingDescr.itemSourcePath = ItemsSourcePath;// путь для поиска во внутренней коллекции templateBindingDescr.IsHierathical = true; } Type tType = dataTemplate.DataType as Type; templateBindingDescr.TargetType = tType; // type шаблона if (! String.IsNullOrWhiteSpace (FindPatches) && FindPatches.Contains (tType.Name + ":")) //если задан альтернативный поиск для этого типа { Match match = Regex.Match (FindPatches, tType.Name + ":([^:]*)");. if (match.Success) { string re = match.Groups [1]. Value; string [] pathes = re.Split (','); templateBindingDescr.BindingPathes.AddRange (pathes); } } else //Визуализирую шаблон и ищу все его биндинги { FrameworkElement frameworkElement = dataTemplate.LoadContent () as FrameworkElement; // загрузил шаблон List <FrameworkElement> DependencyObjects = Helper.GetAllVisualChildren (frameworkElement);//нашел в нем все элементы if (DependencyObjects!= null) { foreach (FrameworkElement dependencyObject in DependencyObjects) { //получил биндинги на текст BindingExpression BE = System.Windows.Data.BindingOperations.GetBindingExpression (dependencyObject, TextBlock.TextProperty); if (BE!= null) { string path = BE.ParentBinding.Path.Path; templateBindingDescr.BindingPathes.Add (path); } } } } templateDescrColl.Add (templateBindingDescr); // добавил в коллекцию } } } }
Вот так выглядит поиск всех элементов содержащих искомый текст.
/// <summary> /// Иерархический поиск всех узлов в методом поиска по указанным путям если в получившимся объекте после приведения его к строке есть FindText /// Работает гораздо быстрее /// </summary> /// <param name="control"></param> /// <param name="FindText"></param> /// <returns></returns> public List<NamedObject> FindObjectByPropertyNames(IEnumerable itemsSource, List<int> path) { List<NamedObject> ret = new List<NamedObject>();//Создали список куда будем епомещать результаты поиска int i = 0; path.Add(i);//добавили в путь этот уровень иерархии foreach (object Item in itemsSource) //Перебираем все элементы коллекции { path[path.Count - 1] = i;//по ходу дела устанавливаем текущий путь TemplateBindingDescr desctiption= templateDescrColl.Get(Item.GetType());//ищем в коллекции описаний подходящий по типу шаблон string subItemsPath=desctiption.itemSourcePath;//получили путь по которому искать детей string ObjectText=String.Empty;//здесь мы будем хранить текст для представления объекта в комбобоксе поиска bool ok=false; foreach (string findPath in desctiption.BindingPathes)// прошлись по путям указанным в биндинге данного узла { object ItemVal = Item.GetObjectSubItem(findPath); //Получили по пути объект if (ItemVal != null )//Если он не нулл { string ItemText = ItemVal.ToString().ToUpper();//получаем текстовое представление объекта ObjectText += ItemText + " "; if (ItemText.Contains(FindText.ToUpper())) { ok = true; //объект содержит искомую строку пометим его } } } if (ok)//если объект содержал найденные значения { List<int> path2 = new List<int>(); path2.AddRange(path); ret.Add(new NamedObject() { Name = ObjectText, Item = Item, Path = path2 });// поместим его в коллекцию } if (subItemsPath != null)//А теперь пройдем по его детям { IEnumerable subItems=Item.GetObjectSubItem(subItemsPath) as IEnumerable; if (subItems != null) { List<int> path1 = new List<int>(); path1.AddRange(path); ret.AddRange(FindObjectByPropertyNames(subItems, path1)); } } i++; } return ret; }
Вот так выглядит поиск всех элементов содержащих искомый текст.
int failCount; //количество падений поиска //выделение узла по пути void SelectNodeByPath(ItemsControl control, IEnumerable<int> path) { List<int>L= path.ToList<int>(); if (L.Count == 0) { return; } if (control.Items.Count > L[0]) { do //тут вот чуть подождем пока сонтрол сгенерит своих детей { control.UpdateLayout(); } while (control.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated); TreeViewItem dObject = control.ItemContainerGenerator.ContainerFromIndex(L[0]) as TreeViewItem; if (dObject == null)//не нашли сразу применим специально обученный метод { dObject = GetTreeViewItem(control, L [0]); } if (dObject == null) { MessageBox.Show("Не могу найти ); } else //нашли родимого { L.RemoveAt(0); if (L.Count == 0) //последнее звено в дереве его нуждно выделить { dObject.IsSelected = true; dObject.BringIntoView(); } else //Это только промежуточный узел и его надо раскрыть { dObject.IsExpanded = true; SelectNodeByPath(dObject, L); //и искать дальше } } } else { MessageBox.Show("В контроле"+control.Items.Count+" Задан поиск > "+L[0]); } }
Функцию GetTreeViewItem(control, L [0]); позволяющую надежно найти узел сделал благодаря
Deranged спасибо ему большое. На основе кода с указанного им сайта.
blogs.msdn.com/b/wpfsdk/archive/2010/02/23/finding-an-object-treeviewitem.aspx
Вот здесь новый код.
myTreeViewWith search.zip
Поиск работает в отдельном потоке.
Возможно сделать расширенный поиск что бы пользователь мог выбирать свойства по которым искать
и… Но это уже украшательства идея вот и работает.
P.S Автор поста, мой брат SergejSh — все вопросы и замечания к нему.
