WPF - How to populate TreeView with directory path strings - wpf

I can't find a solution to add TreeViewItems to a WPF TreeView hierarchically. Each string is as follows:
\\localhost\C$\Application\<ServiceN>\folder\file.doc
If there are multiple strings with the same <ServiceN>, I would like them to be added under the same ServiceN TreeViewItem. I would like to display the items like this:
localhost
ServiceN
C$\Application\ServiceN\folder
file.doc
file2.doc
ServiceN2
C$\Application\ServiceN2\folder
fileN.doc
So far, I have tried by using a List with a ItemsSource like this:
BindingList<string> treeItems = new BindingList<string>();
listView.ItemsSource = treeItems;
elsewhere in the code:
foreach(string f in paths) {
treeItems.Add(f);
}
However, that will display the items as simple string items without any depth/level. I also used multiple nested for loops, but that does not seem as the most efficient way. How can I parse each path string and add it into the correct TreeView position?

Related

ItemsCollection's ContainerFromItem fails for an object that I'm pretty sure is in the collection

I am trying to add search functionality to a ListBox that will scroll down to the item being searched for.
I have a ListBox that is bound to an ObservableCollection that I have filled with a bunch of RecipeNameDTO objects. I can easily find an object in the Items collection with a simple search.
string searchItem = tbSearchString.Text;
var recipenameitem = lbRecipeNames.Items.Cast<DTO.RecipeNameDTO>().Where(u => u.RecipeName.ToLower().Contains(searchItem.ToLower())).FirstOrDefault();
I can reproducibly find items with this method.
However, if I then try to find the object's container using ContainerFromItem, the method returns a null unless the object is visible in the ListBox when I execute the method:
ListBoxItem lbi = (ListBoxItem)lbRecipeNames.ItemContainerGenerator.ContainerFromItem(recipenameitem);
I am certain (I think) that the actual object exists in the ItemsCollection before I execute ContainerFromItem because I use a non null result from the search I documented in the beginning of this post. Also, I can scroll down the ListBox and find the object I'm searching for.
It must be something with the way the ListBox caches the objects in the ItemsCollection that is preventing ContainerFromItem from returning the container. Is there a fix to my code (or understanding of the problem)?
Michael
I took Andy's suggestion and made my ListBox IsSynchronizedWithCurrent = True and then used the following code to set the current item and scroll it into view:
string searchItem = tbSearchString.Text;
CollectionViewSource cvs = (CollectionViewSource)this.FindResource("cvsRecipeName");
ObservableCollection<DTO.RecipeNameDTO> itemsCollection = (ObservableCollection<DTO.RecipeNameDTO>)cvs.Source;
List<DTO.RecipeNameDTO> recipenameitems = itemsCollection.Where(u => u.RecipeName.ToLower().Contains(searchItem.ToLower())).ToList();
if (recipenameitems.Count > 0) { cvs.View.MoveCurrentTo(recipenameitems[0]);}
lbRecipeNames.ScrollIntoView(lbRecipeNames.SelectedItem);
I'm sure I could modify this to make it more flexible, but here's the first fix.

WPF DataGrid Binding List of Object that contains Dictionary<string,string> in XAML

I'm trying to figure out how to display the data from a dictionary property of a list inside a WPF DataGrid GridViewColumn
All the examples I find only appear to map to list of simple entities that can be . traversed like {Binding Answers.Title}. Mine however would be more like {Binding Answers["Title"]} which doesn't work in XAML
I'm missing something obvious here. Any ideas?
Edit: The list contains an entity that contains a Dictionary. Where Answers is the dictionary in the entity.
If I understand you correctly, you have a dictionary as the DataContext, and you need to display either its key or value as ListBoxItem. You can try following :
Here Data is the dictionary you can change the DisplayMemberPath="Key", if you need to display key. This can also be used to display data from a property while a complex object is in DataContext.
I'd imagine you simply bind the item source to the dictionary.Values as you would any collection and then create a data template as usual for the item
I.e. mygridview.datacontext = mydictionary.values

Bind Individual DataGrid Cells to Individual Array Elements

As it says in the title, I am trying to bind individual column cells to individual elements in my load array, but I'm not sure how to perform the binding.
VM implements INotifyCollectionChanged.
foreach( Load L in VM.GetLoads()) //returns an ienumerable array of loads
{
LoadGrid2.Columns.Add(new DataGridTextColumn());
LoadGrid2.Columns[L.Index].Binding = VM.Loads[L.Index]; // this doens't work but illustrates what I am trying to get done
}
[edit] Is there any chance that changing VM to implement IDynamicMetaObjectProvider could help?
http://msdn.microsoft.com/en-us/vstudio//ff800651.aspx
[edit] Now I am thinking that perhaps I shoud be using a xaml defined UserControl?

Merging ContextMenus that are not data bound

There seems to be many variations of this question, but none that deal with my scenario.
I have a UserControl that is used in several places. The control has a context menu, but some of its parents also have context menus. The parent context menus are not databound, i.e. they look like this:
<ContextMenu>
<MenuItem Header="Do Something" Click="DoSomethingMenuItem_Click" />
</ContextMenu>
I can walk up the logical tree and find the parent context menu, but I can't find a way to duplicate the MenuItems (I have to duplicate them because they are only allowed one parent).
I think I am asking a very similar question to this one: https://stackoverflow.com/questions/4177298/how-to-merge-wpf-contextmenus But it has not been answers so I'm still searching!
Please don't suggest I data bind the parent control and use composite collections - there are too many places this is used to make that feasable!
Honestly WPF has no direct way to merge context menus (rather menuitems) from the visual / logical tree of controls.
One way yu can acheive it is instead of setting the direct context menu property of your control, implement an attached property say MergedContextMenu which will be of type context menu.
Now in the property changed event...
Create a temporary context menu say currentContextMenu.
Clone the menuitems from the current value i.e. (e.NewValue as ContexteMenu).Items. Add these cloned menuitems into the currentContextMenu.
Traverse the logical tree and perform step 2 for each context menu found until your desired ancestor is reached.
Assign this currentContextMenu to the actual ContextMenu property i.e. ((UIElement)depObj).ContexteMenu = currentContextMenu.
Use the following code for Clone method....
public static UIElement cloneElement(UIElement orig){
if (orig == null)
return (null);
string s = XamlWriter.Save(orig);
StringReader stringReader = new StringReader(s);
XmlReader xmlReader = XmlTextReader.Create(stringReader, new XmlReaderSettings());
return (UIElement)XamlReader.Load(xmlReader);
}
Add the MenuItems from each original ContextMenu to a temporary list object, remove them from the original ContextMenus Items collection, and then add them all to the new ContextMenu. So long as the MenuItems are not contained on more than one ContextMenu at once you will be fine.

Problem with filtering DataGrid

I have bound my DataGrid to a DataTable and only few of the details are displayed in the grid. When I wanted to filter the DataGrid I created a View with my DataGrid's ItemsSource.
Code:
Dim myView As ICollectionView = CollectionViewSource.GetDefaultView(MyDGrid.ItemsSource)
myView.Filter = New Predicate(Of Object)(AddressOf filterFunc1)
Now When I do the search, the non-displayed fields are also included in the search.
Public Function filterFunc1(ByVal obj As Object) As Boolean
Dim filStr As String = "*" & TextBox1.Text & "*"
For Each item As String In obj.Row.ItemArray
**If item.ToLower Like filStr.ToLower Then**
Return True
End If
Next
Return False
End Function
Also I Have ComboBox fields in the DataGrid which are loaded separately from other DataTable's. Now I cant Include them in the search.
A screenshot from my App:
So how do I make a search that includes only the Text from Displayed part.
EDIT: Also how do I skip searching the null valued fileds? 'cause thats causing an exception in my case.
Well then...
Your question is pretty disjointed and I can't understand all of it - maybe that's why you didn't get an answer so far. Skipping null fields is simply a matter of adding a new condition in filterFunc1 - if Convert.IsDBNull(item) then continue for (assuming item is a field in a DataRow, of course).
However, this programming style is pretty foggy and I'd recommend at the very least being more clear on which columns you filter, and the types of objects in the columns. A much better approach would be to map the data you're getting from the database to actual objects in your application - that allows for more type-safe programming. I think the main problem here is that nobody can really tell what's going on there from a few lines of code because nobody can make any assumptions about what kind of objects are there.
About the items in the ComboBox, no idea what kind of difficulties you're having, you might want to clear that up a bit.
you could maintain, instead of simply strings, structures containing both captions and IDs, like say
public class YourComboItem
public property Id as string [get/set]
public property Title as string [get/set]
end class
Then bind your ComboBox's ItemsSource to a collection of these items retrieved from the database, and set DisplayMemberPath to Title and ValueMemberPath to Id. Then you can use the ComboBox's SelectedValue to get the selected ID. As you can see, having objects instead of raw data structures can have quite some advantages.
Of course, I described getting the SelectedValue directly from the ComboBox, while a much better architecture would be MVVM, with the ViewModel containing an ObservableCollection(Of YourComboItem) and the ComboBox's ItemSource bound to it with an actual binding. Then you can also bind the SelectedItem to a property in your ViewModel, and have the item as a whole, including both Id and Title, to work with without knowing anything about your user interface. Alternatively you could have an ICollectionView generated from the collection of items and bind the ItemsSource to that, then you'd have the selected item in the ICollectionView's CurrentItem property.
I'd really recommend reading up on MVVM (Model-View-ViewModel) to make your work with WPF a whole lot easier.

Resources