Well, I thought I had this one solved. I simply changed the TreeViewItem's template to ignore whether a particular node was expanded or not. Looks great! But that's just it... it looks great! Keyboard navigation still responds as if the node was expanded or collapsed.
For instance, if I'm on the root node and hit the right arrow (nothing moves, nor should it) then hit down, I go to the first child of the root. However, if I'm on the root node and hit left (again, nothing moves) then hit down, I jump to the second root node, jumping over all the first node's children!
Needless to say that's not the behavior we want. We could simply swallow the left and right arrow keys, or simply abandon the treeview entirely and move to nested items presenters (which is sort of what a TreeView does anyway) but I'm hoping I don't have to re-create an entire control just for this functionality. Thoughts?
In your TreeViewItem template, you can set IsExpanded to true, and all of your items should then be expanded if they have child elements.
Unfortunately you may have to swallow those keystrokes to get the keyboard navigation you want, as the navigation you are seeing is by design.
You may be able to create a custom treeview inherited from TreeView and put those button events in there so that you can re-use it.
Related
I'm working on a C# application's UI that has the following structure:
Main Form (MDI Parent)
Tree View
Multiple MDI Childdren, each containing:
Custom User Control
I'd like to separate the form's canvas space allocated for the Tree View from that where the MDI Children are allowed to move in. To that extent, I tried a SplitContainer and setting the MDI Child's TopLevel field to 'false', but, besides the fact that newly created MDI Children were created behind the old ones, I also had issues with them not being cleared properly from the screen and other weird issues.
As I don't have lots of experience with UIs in C#, is the path I am taking correct? Would it be easier to change the above structure so that it doesn't use MDI anymore, or is there a simpler way of dividing the canvas between the tree view and the MDIs, other than the SplitContainer?
Cheers,
Alex
UPDATE: Actually, the solution seems to work quite nicely, thanks! I'll probably be able to stop the widgets from moving in the wrong place by placing a simple check.
Besides that, is there any way in which I could make the TreeView resizable without involving a SplitContainer (because of the reasons mentioned in the post)?
Add a Panel to your Main Form and Dock it to the Left. Now add your TreeView to the Panel. Resize the Panel as necessary. Now your MdiChildren will take up the empty space to the right of the Panel...
*You can actually simply Dock the TreeView itself to the Left side of the Form, but the Panel approach would allow you to place other controls in that left side area as well. Just depends on your UI layout.
I have a WPF/XAML form that has controls on the page and also controls inside a tab control.
I was hoping that by setting the tabindex values appropriately, the user could just tab from the controls outside of the tab control to the controls inside the first tab item, but it seems that the items inside tab control are skipped when tabbing around the form.
Is there a way to have the tabbing go into the tabitem/tab control?
WPF provides a number of ways to affect the tab order in an application. Probably the most important is also strangely the least known. I'm talking about the KeyboardNavigation class and in particular, the KeyboardNavigation.TabNavigation Attached Property. From the linked page on MSDN, this property Gets or sets the logical tab navigation behavior for the children of the element that this property is set on.
There are several possible values in the KeyboardNavigationMode Enumeration used that affect the tabbing order in different ways. Take a look at the last linked page to see which one suits your situation best, but as an example, the Local value has the effect that Tab Indexes are considered on local subtree only inside this container and ... [Navigation leaves the containing element when an edge is reached].
<Grid KeyboardNavigation.TabNavigation="Local">
...
</Grid>
This is a problem I've often faced and it's a tricky one. From my understanding, the reason that it doesn't work as you'd expect is because tab order (even specified via TabIndex) is contextual. TabIndex of higher level items will be prioritized over inner elements. So if you have two TabItems inside of a TabControl, and each one has UIElements inside of them, even if the TabIndex is specified, tabbing will first traverse the TabItems before it moves down to the contents of those controls. I "think", IIRC" this has to do with how the page is composed, but don't quote me on that. MS has weird reasons for some of these subtle nuances.
Onto the solution. What I've done in the past (so long as you're NOT working on WinRT, which makes this problem even worse) you can use UIElement.Focus. I store a list of UIElements in the order I wish them to be when tabbed in the code. Then, by binding the KeyDown event to a common handler for all of these controls, I do something like this:
int currentIndex = TabbableControls.IndexOf(sender);
UIElement next = TabbableControls[(currentIndex + 1) % TabbableControls.Length];
next.Focus();
Hope this helps!
I am curious to know how WPF figures out where the focus should be set when the user hits the TAB key. Thinking aloud, I feel:
It may be doing relative search on the UI and find the nearest control based on (x,y) location.
It could manually walk the logical sub-tree to look for the nearest control
Does it do it each time the TAB key is pressed ?
From WPFWiki:
Tab Navigation moves the focus through
controls in a logical sequence.
The default logical sequence is that
controls will be focused starting from
the first focusable child of the root
control (window, page, etc.). From
that point, the TabNavigation property
is considered, and the next control in
sequence is either the first focusable
descendent of the currently focused
control or the next focusable sibling.
The TabNavigation property of the
newly focused control is then
evaluated, and so on.
For the most part, the tab order (using the rule described above) will generally be from the top of your XAML file to the bottom.
Of course, this can be modified by setting KeyboardNavigation attached properties, such as IsTabStop, TabNavigation, TabIndex, etc.
Perhaps not the most technical answer (I don't know the actual guts of it), but that's the general idea...
I have an ItemsControl with a number of elements, each one with its own ViewModel instance. Each item's ViewModel knows whether that item should be visible (currently each ViewModel has a Visibility property that the UI binds to). When my window first opens, some of these items are visible, others are collapsed. Later, some items' visibility might change in response to user interaction. The window sizes to its content, so the window resizes when items are shown or hidden. And the window is initially centered on the screen (which means everything has to be arranged properly right away, so the window knows its initial size and can center itself accordingly).
Now I want to add animations whenever an item is shown or hidden -- but I only want to animate if the item's visibility changes after the window is already shown. So if the window is already open, and the user does something that makes one of the ViewModels want to appear, it animates in; if the user does something to make one of the ViewModels disappear, it animates out. But when the window first opens, I want everything to start out rock-solid -- no lingering animations.
And I want the window to still set its initial size based on its initially-visible content, and I still want it to be initially centered onscreen. (Although actually, in this case, it would be acceptable if it centered itself as if all items were visible, if event ordering made it work out that way.)
I know a fair bit about WPF, but I admit I'm lost when it comes to triggers and storyboards. I haven't really done anything with WPF animations before, and I'm not sure where to begin.
I already tried using Reveal from the Bag of Tricks, but I had several problems with it, the biggest being that it doesn't have the "only use animations after the window is shown" behavior that I want -- my window would appear and the initially-visible elements would still be animating in. It also didn't play well with my layout (it was centering the elements horizontally, instead of stretching them to the ItemsControl's width), and a few other problems that might or might not be fixable.
I'm not too picky about whether I animate by stretching (e.g. by animating a LayoutTransform from SizeX=1 SizeY=0 to SizeX=1 SizeY=1, thus starting with squished text and expanding to normal size) or by just changing the Height (thus starting with only part of the content visible and revealing more as the animation progresses) -- I'm fine with either.
I'm open to writing my own Panel descendant (I've done it before) if that's the best way to solve this, and I can always steal code from Reveal and hack until I get it working -- but it seems like there should already be an easier way to do this, if I just knew what it was. I'm open to learning about triggers and storyboards, or whatever, if someone can point me in the right direction.
I don't understand, I have a toolbar with buttons bind to custom commands.
Also I have an expandable control docked to the left of window - kinda NavPanel.
(Devcomponents' NavigationPane to be exact)
Now, everytime when it's collapsed or expanded, buttons in the toolbar become disabled and stay like that till the focus changes.
Of course, it's simple to change the focus inside Collapsed and Expanded events, but unfortunately it works only in the first and ignores the second one and all buttons stay disabled.
It seems that it something to do with CommandTarget which I haven't define nowhere. Maybe I should?
Any ideas?
When the Buttons gray-out, then two conditions can exist:
The MyCommandCan_Execute method sets the e.CanEecute property to false.
The CommandBinding is inactive.
I think the latter case is your problem. At what level in the visual tree have you defined your CommandBindings? Put them as high up as you can.