Showing posts with label Tools. Show all posts
Showing posts with label Tools. Show all posts

Wednesday, April 30, 2014

Some WPF Treeview Tips/Tricks

T.StartPost();

Hey everyone/anyone reading, been a long while but I have just been quite busy with my internship and being a tools code monkey :D  It's been pretty fun, interesting and at times, VERY FRUSTRATING.

Now anyone that has done WPF has probably run into any number of things that wants to make you pull your hair out and flip some tables onto small children, but after many hours searching and trying different things I've come across somethings that are VERY VERY handy for tools in WPF. I'll probably just update this post as I come across more to keep them all in once place but for starters, here is a few things that I've learned to make dealing with the dreaded and yet very powerful TreeView.

WPF TreeView is probably the best and the most frustrating thing I've had to deal with on a constant basis in the last few months; it's so handy for presenting data in a hierarchical way but there are things that you just can't do directly with TreeView without searching and searching and searching the internet to find the answers to such as:


  • How can I force TreeView to change the SelectedItem?
          Now this I will say has stunned me for a while and I actually JUST found the answer to this problem Here on Stack Overflow.  So what are we to do here? Well if you're lucky enough to have your project presenting with a inherited model (cause you should be at least using the MVVM programming pattern while doing WPF tools) for your data then you can do this quite easily.  You need about 2-3 things,
1. Keep track of your selectedItem via clicks/keyboard navigation 
2. Add to your base model type, a property like 
        private bool _isSelected;
        public bool IsSelected { get { return _isSelected; } set { Set(ref _isSelected, value); } }
    *NOTE* the Set is just raising INotifyPropertyChanged so how ever you are handling yours is how you should do it here as well *NOTE*
3. In your treeview, you'll need to have at least this line from the link
<Setter Property="IsSelected"
                        Value="{Binding IsSelected, Mode=TwoWay}" />
What this is going to do is allow you to toggle IsSelected in your data Model and since the treeview is bound to it, it will automatically set and be set to IsSelected.  Now some people may think, "Why wouldn't you just do it in code behind or something like that?"  Well that's because IsSelected is a read-only property, so you can't just set it all willy-nilly like that and from all the proposed ways I've seen so far, this is the easiest to setup.

This also goes for being able to select what IS and ISN'T expanded in your treeview programmatically, make a property and bind with the Setter in your treeview.

Search Results




  • How to implement a simple and easy to use Search Filter for your TreeView
Searchable WPF TreeView on Code Project
Thankfully, Already made a great article on this. If you don't need/want the super fancy search box (was a bit overkill for what I needed) then the really relevant parts of the article are having some more setter properties in your Treeview for

<Setter Property="IsExpanded" Value="{Binding Path=IsExpanded, Mode=TwoWay}" />
                <Setter Property="Visibility" Value="{Binding Path=IsMatch, Mode=OneWay, Converter={StaticResource ResourceKey=boolToVisibility}}"/>
This lets you setup your TreeView to work with a method that you will call in your code that will search through your Data (that is bound to the tree) and set Expanded to true/false and Visibility to true/false as well which will effectively hide anything that doesn't match your results in your textbox and expand open to the result, anything that does match. Much like the IsSelected above, you'll add the IsExpanded and IsMatch(I did IsVisible myself) to your data Model.

The next important piece of code that you will need is this
public void ApplyCriteria(string criteria, Stack<TreeNodeViewModel> ancestors) {
    if (IsCriteriaMatched(criteria)) {
        IsMatch = true;
        foreach (var ancestor in ancestors) {
            ancestor.IsMatch = true;
            ancestor.IsExpanded = !String.IsNullOrEmpty(criteria);
        }
    } 
    else
        IsMatch = false;

    ancestors.Push(this);
    foreach (var child in Children)
        child.ApplyCriteria(criteria, ancestors);

    ancestors.Pop();
}
        
The method that determines if this node is a match is in this article a simple string comparison;
private bool IsCriteriaMatched(string criteria) {
    return String.IsNullOrEmpty(criteria) || name.Contains(criteria);
}
The methods will check for anything matching the searchText you give it (I hooked up an extra method from my codebehind that when the text changes in my textbox, call the method which passes the text to the Apply Criteria.) and expand or hide accordingly.

*NOTE* if you want to make the search just a bit more functional change the IsCriteriaMatched to
name.ToLower().Contains(criteria.ToLower());  This will make it that no matter if you have say Matilda or matilda, it will still show up if you gave the search box MaTilDa for example *NOTE*

The only change to this method that I really made was that I also pass the current node you're checking so you can get the name of the node like


private bool IsCriteriaMatched(string criteria, NodeWithName check)
{
    return String.IsNullOrEmpty(criteria) || check.Name.ToLower().Contains(criteria.ToLower());
}

The only part left is in your WPF you have a textbox and choose whether you want it live filtering (as the user types it filters) or keypress filtering (hit enter, it does the search) and make sure that you save/bind to the textbox text in a viewmodel (pretty much the same place you do the above search methods is as good a place as any)

Those are the two biggest things that I've run into and it's taken a while to find good working answers to (I've never used the Setter myself before this so it was a good experience to learn that I could bind to it).

Hopefully people stumble across this post and find these answers instead of searching for a long time.

T.Out();

Monday, March 3, 2014

WPF and Child Code...

T.StartPost();

Just a quick post because you never know who might need to do something like this as well (and maybe this can save them a few days of trying stuff/searching for different things/implementing things that don't work...)

So while making a tool for my internship, I had to implement a UI that would display data in a parent/child relationship. Ok, shouldn't be to bad, but the thing is that it could also have the same parent data in a list as well.

After trying for a while to get this setup to work I finally got something that displayed correctly by just getting the object and stripping the strings from it and displaying that and then more or less "discarding" the object (not discarding but not saving it in my UI for retrieval)

Then I was told we would be able to get things back by the unique ID that they would have (which also put me in a pickle because I wasn't using a Unique ID in my test data...Where I thought this might save me, it put me deeper into the, "Oh crap, how am I going to get the object back and get that ID?"

This is where StackOverflow came in to save the day...finally...after a week of searching different questions on there haha.

http://stackoverflow.com/questions/1912481/wpf-treeview-hierarchicaldatatemplate-binding-to-object-with-multiple-child-co?answertab=votes#tab-top

This answer is the answer that finally gave me the puzzle piece to get my Data Object into the UI as well as being able to get it back after (instead of just getting the strings and displaying that.)

Hopefully someone can find this helpful cause man that took way to long to get that to work..I must have tried over 15 different ways to do the datatemplates and none of them worked till this one so yeah.

Edit:

One thing to note as well, when doing the data templates, if you're doing it in a treeview like I am/the example as well, you don't want to put a TreeViewItem into the datatemplate if you want to be able to click on the text to get selection.  I didn't realize I had done this till yesterday and was trying to figure out why you had to click on this tiny spot between the > and the text.  Apparently TreeView automatically wraps whatever you put in the template in a TreeViewItem anyways so you can just display with textblock/label etc. etc.

T.Out();