Keyboard navigation in Telerik WPF RadCarousel - wpf

I'm struggling a bit with keyboard navigation in the Telerik WPF RadCarousel. If i click outside an item, but within the carousel-control, keyboard naviation works as expected (I can switch between items using the left and right keyboard arrows), but if I click an item within the RadCarousel, keyboard navigation is gone. How can I get the RadCarousel to handle keyboard navigation when an item in the carousel has focus?
Additional things I want to accomplish:
Automatically show the SelectedItem as the "front-item" in the carousel.
Automatically select the "front-item" when navigating through the carousel.
My RadCarousel binding is set up as follows:
<ScrollViewer CanContentScroll="true">
<telerik:RadCarousel Name="carousel" HorizontalScrollBarVisibility="Hidden"
ItemsSource="{Binding Path=Templates}"
ItemTemplate="{StaticResource template}"
SelectedItem="{Binding Path=SelectedTemplateAndFolder}" />
</ScrollViewer>
Edit:
By using Snoop, I can see that the "CarouselScrollViewer" has focus when the scrolling is working. Selecting an item causes the RadCarousel to get focus (and the navigation to stop working).

I got the most import things to work, which was the keyboard navigation and moving the selected item to the front of the carousel.
Keyboard navigation:
private void Carousel_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
CarouselScrollViewer scrollViewer = FindChild<CarouselScrollViewer>(this.carousel, null);
scrollViewer.Focus();
}
public static T FindChild<T>(DependencyObject parent, string childName)
where T : DependencyObject
{
// Confirm parent and childName are valid.
if (parent == null) return null;
T foundChild = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
T childType = child as T;
if (childType == null)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
// If the child's name is set for search
if (frameworkElement != null && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
Moving selected item into center of carousel:
private void Carousel_SelectionChanged(object sender, SelectionChangeEventArgs e)
{
this.carousel.BringDataItemIntoView(this.carousel.CurrentItem);
}

Related

WPF DataGridComboBoxColumn Edit on Key press

I have a datagrid with a DataGridComboBoxColumn in it.
I want my users to be able to enter edit mode by just typing.
This is the default behavior for DataGridTextColumn and I don't like that they have to press F2 in order to enable editing for just this column type.
How can I make the DataGridComboBoxColumn enter edit mode without them needing to press F2? Ideally on Key Press, but I would be fine if it entered edit mode on focus as well.
Solution
Modifications to the accepted answer that bring back the basic functionality of the datagrid:
void Cell_PreviewKeyDown(object sender, KeyEventArgs e)
{
if(e.Key == Key.Enter || e.Key == Key.Tab)
{
dgBins.CommitEdit();
dgBins.SelectedIndex += 1;
}else if(e.Key.ToString().Length == 1
|| (e.Key.ToString().StartsWith("D") && e.Key.ToString().Length == 2)
|| e.Key.ToString().StartsWith("NumPad")
|| e.Key == Key.Delete
|| e.Key == Key.Back )
{
if (e.OriginalSource is DataGridCell)
{
DataGridCell cell = (sender as DataGridCell);
Control elem = FindChild<Control>(cell, null);
elem.Focus();
}
}
}
<DataGrid.CellStyle>
<Style TargetType="DataGridCell">
<EventSetter Event="PreviewKeyDown" Handler="Cell_PreviewKeyDown"/>
<EventSetter Event="GotFocus" Handler="Cell_GotFocus"/>
</Style>
</DataGrid.CellStyle>
Handlers :
void Cell_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.OriginalSource is DataGridCell)
{
DataGridCell cell = (sender as DataGridCell);
Control elem = FindChild<Control>(cell, null);
elem.Focus();
}
}
void Cell_GotFocus(object sender, RoutedEventArgs e)
{
DataGridCell cell = (sender as DataGridCell);
cell.IsEditing = true;
}
Helper :
public static T FindChild<T>(DependencyObject parent, string childName)
where T : DependencyObject
{
// Confirm parent and childName are valid.
if (parent == null) return null;
T foundChild = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
T childType = child as T;
if (childType == null)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
// If the child's name is set for search
if (frameworkElement != null && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
If this solves your problem.
You can try to use SelectedCellsChanged event. It works on focus changing.
If you don't want that behaviour on other columns you can check e.AddedCells[0].Column property (if your SelectionUnit="Cell" of DataGrid).
private void dgTest_SelectedCellsChanged( object sender, SelectedCellsChangedEventArgs e )
{
( sender as DataGrid ).BeginEdit();
}
My answer might be a little late but I want to add this, because no given answer was solving the problem for me without breaking normal DataGrid keyboard handling.
The question is simple:
I want my users to be able to enter edit mode by just typing.
So is the solution:
<DataGrid KeyDown="DataGrid_KeyDown">
...
</DataGrid>
With code in code behind xaml.cs:
private void DataGrid_KeyDown(object sender, KeyEventArgs e)
{
DataGrid dg = (sender as DataGrid);
if (dg.CurrentColumn is DataGridComboBoxColumn)
{
dg.BeginEdit();
}
}
Because this already is the normal behavior of TextBoxes I only wanted to apply this to DataGridComboBoxColumn. Of course this will be called on every key press, but a call to 'BeginEdit' does not seem to hurt when the DataGrid already is in edit mode.

How to get the TreeView from inside the ItemTemplate class?

I have a TreeView class like so, which has my own class "EntryPanel" as an ItemTemplate:
<TreeView x:Class="PowerNote.MyEntriesView"
xmlns:local="clr-namespace:PowerNote"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
ItemsSource = "{Binding viewStudents}">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}">
<local:EntryPanel/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
In my EntryPanel.xaml.cs code, I would like to access the TreeView. I tried Parent, and TemplateParent, but neither would work. How can I achieve this?
Try Doing in this way to Find the Parent.
public static T TryFindParent<T>(this DependencyObject child)
where T : DependencyObject
{
//get parent item
DependencyObject parentObject = GetParentObject(child);
//we've reached the end of the tree
if (parentObject == null) return null;
//check if the parent matches the type we're looking for
T parent = parentObject as T;
if (parent != null)
{
return parent;
}
else
{
//use recursion to proceed with next level
return TryFindParent<T>(parentObject);
}
}
This one will find the parent object.
public static DependencyObject GetParentObject(this DependencyObject child)
{
if (child == null) return null;
//handle content elements separately
ContentElement contentElement = child as ContentElement;
if (contentElement != null)
{
DependencyObject parent = ContentOperations.GetParent(contentElement);
if (parent != null) return parent;
FrameworkContentElement fce = contentElement as FrameworkContentElement;
return fce != null ? fce.Parent : null;
}
//also try searching for parent in framework elements (such as DockPanel, etc)
FrameworkElement frameworkElement = child as FrameworkElement;
if (frameworkElement != null)
{
DependencyObject parent = frameworkElement.Parent;
if (parent != null) return parent;
}
//if it's not a ContentElement/FrameworkElement, rely on VisualTreeHelper
return VisualTreeHelper.GetParent(child);
}
This snippet works with arbitrary dependency objects that are of Type Visual or Visual3D. So let’s say you need a reference to the Window that hosts a given Button control somewhere, all you need is this:
Button myButton = ...
Window parentWindow = UIHelper.TryFindParent<Window>(myButton);
The above TryFindParent method also makes it easy to get an item at a given position. The method below performs a hit test based on a given position. If hit testing does not return the requested item (e.g. a clicked CheckBox on a tree, while you are keen on the TreeViewItem that hosts the CheckBox), the procedure delegates the lookup to TryFindParent.
This comes in very handy for mouse-related events if you just need to now what’s under your mouse pointer:
public static T TryFindFromPoint<T>(UIElement reference, Point point)
where T:DependencyObject
{
DependencyObject element = reference.InputHitTest(point)
as DependencyObject;
if (element == null) return null;
else if (element is T) return (T)element;
else return TryFindParent<T>(element);
}
Reference

How to reference image in controltemplate

I have a usercontrol named ItemGrid with a button with an image, I moved the image to a controltemplate so I would be able to size it right:
<Button x:Name="btnOrder" Click="btnOrder_Click" HorizontalAlignment="Right" Width="48" Height="48" Margin="0,0,0,100">
<Button.Template>
<ControlTemplate>
<Image x:Name="imgOrder" Source="/Images/dark/appbar.food.png" Stretch="None"/>
</ControlTemplate>
</Button.Template>
</Button>
In the MainPage I want to set the right image depending on the currently selected them (Dark / Light)
private void detecttheme()
{
Visibility v = (Visibility)Resources["PhoneLightThemeVisibility"];
if (v == System.Windows.Visibility.Visible)
{
uri = new Uri("/Images/light/appbar.food.png", UriKind.Relative);
imgSource = new BitmapImage(uri);
ItemGrid.imgOrder.Source = imgSource;
}
else ....
That gives me: UserControls.ItemGrid' does not contain a definition for 'imgOrder' after I moved imgOrder to the controltemplate
I've tried to use findname but that gives a nullreference exception too for img
//Use FindName because we cannot directly reference the image because it's in a control template
Image img = ItemGrid.FindName("imgOrder") as Image;
img.Source = imgSource;
I also tried putting the findname in the OnApplyTemplate of the control but that doesn't seem to get fired at all?
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
Image i = this.FindName("imgOrder") as Image;
}
I hope somebody has an answer to this?
Kind regards,
Mike
I think in this case you can find it by using the VisualTreeHelper class - there's a thread on this in WPF here, but in case you don't feel like reading it:
make this method:
/// <summary>
/// Finds a Child of a given item in the visual tree.
/// </summary>
/// <param name="parent">A direct parent of the queried item.</param>
/// <typeparam name="T">The type of the queried item.</typeparam>
/// <param name="childName">x:Name or Name of child. </param>
/// <returns>The first parent item that matches the submitted type parameter.
/// If not matching item can be found,
/// a null parent is being returned.</returns>
public static T FindChild<T>(DependencyObject parent, string childName)
where T : DependencyObject
{
// Confirm parent and childName are valid.
if (parent == null) return null;
T foundChild = null;
int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
// If the child is not of the request child type child
T childType = child as T;
if (childType == null)
{
// recursively drill down the tree
foundChild = FindChild<T>(child, childName);
// If the child is found, break so we do not overwrite the found child.
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
// If the child's name is set for search
if (frameworkElement != null && frameworkElement.Name == childName)
{
// if the child's name is of the request name
foundChild = (T)child;
break;
}
}
else
{
// child element found.
foundChild = (T)child;
break;
}
}
return foundChild;
}
and use it to find your image:
Image img = FindChild<Image>(btnOrder, "imgOrder");
Hopefully that works (I'll admit I haven't tested this yet)...and as for why OnApplyTemplate() doesn't work, I believe it's only called if you subclass the Button to make your own Custom button.

How to scroll to the next logical page in a ListBox with physical scrolling

I do have a listbox which must have CanContentScroll==false because i need to be able to scroll it smoothly. This enables physical scrolling.
I also want to scroll the listbox by page, but if i call the PageDown method on the listbox internal ScrollViewer the first row gets cutted because the listbox height is not a multiple of the row height.
I want the first row to be always completely visible, like when using logical scrolling.
Can someone give me a hint on how to do that?
You'll get the same effect for a non virtualizing ItemsControl (ScrollViewer.CanContentScroll="False") as for a virtualizing one if you scroll down and then select the upper visible container with the mouse. This can also be done in code.
When CanContentScroll is set to false, virtualizing is turned off so all containers will be generated at all times. To get the top visible container we can iterate the containers from the top until we reach the VerticalOffset of the ScrollViewer. Once we got it we can simply call BringIntoView on it and it will align nicely at the top just like it would if virtualization was being used.
Example
<ListBox ItemsSource="{Binding MyCollection}"
ScrollViewer.CanContentScroll="False"
ScrollViewer.ScrollChanged="listBox_ScrollChanged" >
Call BringIntoView on the top visible container in the event handler
private void listBox_ScrollChanged(object sender, ScrollChangedEventArgs e)
{
ItemsControl itemsControl = sender as ItemsControl;
ScrollViewer scrollViewer = e.OriginalSource as ScrollViewer;
FrameworkElement lastElement = null;
foreach (object obj in itemsControl.Items)
{
FrameworkElement element = itemsControl.ItemContainerGenerator.ContainerFromItem(obj) as FrameworkElement;
double offset = element.TransformToAncestor(scrollViewer).Transform(new Point(0, 0)).Y + scrollViewer.VerticalOffset;
if (offset > e.VerticalOffset)
{
if (lastElement != null)
lastElement.BringIntoView();
break;
}
lastElement = element;
}
}
To only achieve this effect when you want to call PageDown, in a Button click for example, you can create an extension method for ListBox called LogicalPageDown.
listBox.LogicalPageDown();
ListBoxExtensions
public static class ListBoxExtensions
{
public static void LogicalPageDown(this ListBox listBox)
{
ScrollViewer scrollViewer = VisualTreeHelpers.GetVisualChild<ScrollViewer>(listBox);
ScrollChangedEventHandler scrollChangedHandler = null;
scrollChangedHandler = (object sender2, ScrollChangedEventArgs e2) =>
{
scrollViewer.ScrollChanged -= scrollChangedHandler;
FrameworkElement lastElement = null;
foreach (object obj in listBox.Items)
{
FrameworkElement element = listBox.ItemContainerGenerator.ContainerFromItem(obj) as FrameworkElement;
double offset = element.TransformToAncestor(scrollViewer).Transform(new Point(0, 0)).Y + scrollViewer.VerticalOffset;
if (offset > scrollViewer.VerticalOffset)
{
if (lastElement != null)
lastElement.BringIntoView();
break;
}
lastElement = element;
}
};
scrollViewer.ScrollChanged += scrollChangedHandler;
scrollViewer.PageDown();
}
}
I noticed in your question that you already got the ScrollViewer but I'm adding an implementation to GetVisualChild if anyone else comes across this question
public static T GetVisualChild<T>(DependencyObject parent) where T : Visual
{
T child = default(T);
int numVisuals = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < numVisuals; i++)
{
Visual v = (Visual)VisualTreeHelper.GetChild(parent, i);
child = v as T;
if (child == null)
{
child = GetVisualChild<T>(v);
}
if (child != null)
{
break;
}
}
return child;
}
If you scroll the top item of the "new page" into view, would that achieve what you're looking for?
See ScrollIntoView.

Can I pass events like key strokes to another control in Silverlight?

Can I pass events like key strokes to another control in Silverlight?
Imagine I'm in a custom control that contains a Textbox and a Treeview.
I'm listening to a Key event for the TextBox. When the user pushes the Arrow Up or Arrow Down key, I want the Treeview to behave as if it's itself who received that event, i.e. it should move the current selection up or down. The user shouldn't lose focus on the TextBox though so that they can continue typing.
Is this possible? I don't want to set the selection manually on the Treeview because it has no easy MoveSelectionUp() or MoveSelectionDown() method, so I would have to duplicate that functionality which is not so trivial especially when the tree is databound and loads nodes on demand.
Maybe you could consider using the Mediator Design Pattern to achieve this?
I can only think of two ways to "forward" key events:
Your way: Register key event handlers and process events in your own code and doing the state manipulation on the target control via properties, methods, extensions (and whatever API is offered by the target control).
This works, but it is basically reimplementing stuff that someone else has already written - inside the target control-, and you always run the risk of forgetting a corner case.
Inherit from the target control and add your input textbox to the ControlTemplate:
When your control is really quite similar to an existing control, so you can honestly say "MyFoo IS_A SomeTargetControl" (and then anyway chances are you will want to offer DependencyProperties for further customization that are just a copy of what is already present at the target control class) you should totally use inheritance.
Your TextBox won't set the ArrowUp and ArrowDown key events to handled, so the inherited key handling code will take care of them and will manipulate the selection corretly.
In the meantime I solved my special scenario by creating MoveSelectionUp() and MoveSelectionDown() extension methods on the TreeView. I copied the implementation over from some private methods in the Toolkit's control code and made slight changes were private or protected methods were accessed. Thanks to all the extensions methods that are available in the toolkit now it's not so difficult anymore.
Because it is largely not mine, I hereby provide the code below if future visitors stumble upon the same issue.
I'd leave this question open however because I'd still like to know, in a more general fashion, if events can be forwarded in the DependencyObject framework.
TreeViewExtensions:
using System;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
static public class TreeViewExtensions
{
static public void SetSelectedContainerIfValid(this TreeView self, TreeViewItem itm)
{
Contract.Requires(self != null);
if (itm != null)
{
self.SetSelectedContainer(itm);
}
}
static public void MoveSelectionUp(this TreeView self)
{
Contract.Requires(self != null);
var itm = self.GetSelectedContainer();
if (itm == null)
{
self.SetSelectedContainerIfValid(self.GetContainers().LastOrDefault());
}
else
{
self.SetSelectedContainerIfValid(itm.GetContainerAbove());
}
}
static public void MoveSelectionDown(this TreeView self)
{
Contract.Requires(self != null);
var itm = self.GetSelectedContainer();
if (itm == null)
{
self.SetSelectedContainerIfValid(self.GetContainers().FirstOrDefault());
}
else
{
self.SetSelectedContainerIfValid(itm.GetContainerBelow());
}
}
}
TreeViewItemExtensions:
using System;
using System.Diagnostics.Contracts;
using System.Windows;
using System.Windows.Controls;
static public class TreeViewItemExtensions
{
static public TreeViewItem GetContainerBelow(this TreeViewItem self)
{
return TreeViewItemExtensions.GetContainerBelow(self, true);
}
/// <summary>
/// Find the next focusable TreeViewItem below this item.
/// </summary>
/// <param name="recurse">
/// A value indicating whether the item should recurse into its child
/// items when searching for the next focusable TreeViewItem.
/// </param>
/// <returns>The next focusable TreeViewItem below this item.</returns>
static public TreeViewItem GetContainerBelow(this TreeViewItem self, bool recurse)
{
Contract.Requires(self != null);
// Look for the next item in the children of this item (if allowed)
if (recurse && self.IsExpanded && self.HasItems)
{
TreeViewItem item = self.ItemContainerGenerator.ContainerFromIndex(0) as TreeViewItem;
if (item != null)
{
return item.IsEnabled ?
item :
item.GetContainerBelow(false);
}
}
// Look for the next item in the siblings of this item
ItemsControl parent = self.GetParentTreeViewItem() as ItemsControl ?? self.GetParentTreeView();
if (parent != null)
{
// Get the index of this item relative to its siblings
TreeViewItem item = null;
int index = parent.ItemContainerGenerator.IndexFromContainer(self);
int count = parent.Items.Count;
// Check for any siblings below this item
while (index++ < count)
{
item = parent.ItemContainerGenerator.ContainerFromIndex(index) as TreeViewItem;
if (item != null && item.IsEnabled)
{
return item;
}
}
// If nothing else was found, try to find the next sibling below
// the parent of this item
TreeViewItem parentItem = self.GetParentTreeViewItem();
if (parentItem != null)
{
return parentItem.GetContainerBelow(false);
}
}
return null;
}
/// <summary>
/// Find the last focusable TreeViewItem contained by this item.
/// </summary>
/// <returns>
/// The last focusable TreeViewItem contained by this item.
/// </returns>
static public TreeViewItem GetLastContainer(this TreeViewItem self)
{
Contract.Requires(self != null);
TreeViewItem item = self;
TreeViewItem lastItem = null;
int index = -1;
// Walk the children of the current item
while (item != null)
{
// Ignore any disabled items
if (item.IsEnabled)
{
// If the item has no children, it must be the last
if (!item.IsExpanded || !item.HasItems)
{
return item;
}
// If the item has children, mark it as the last known
// focusable item so far and walk into its child items,
// starting from the last item and moving toward the first
lastItem = item;
index = item.Items.Count - 1;
}
else if (index > 0)
{
// Try searching for the previous item's sibling
index--;
}
else
{
// Stop searching if we've run out of children
break;
}
// Move to the item's previous sibling
item = lastItem.ItemContainerGenerator.ContainerFromIndex(index) as TreeViewItem;
}
return lastItem;
}
/// <summary>
/// Find the previous focusable TreeViewItem above this item.
/// </summary>
/// <returns>
/// The previous focusable TreeViewItem above this item.
/// </returns>
static public TreeViewItem GetContainerAbove(this TreeViewItem self)
{
Contract.Requires(self != null);
ItemsControl parent = self.GetParentTreeViewItem() as ItemsControl ?? self.GetParentTreeView();
if (parent == null)
{
return null;
}
// Get the index of the current item relative to its siblings
int index = parent.ItemContainerGenerator.IndexFromContainer(self);
// Walk the previous siblings of the item to find a focusable item
while (index-- > 0)
{
// Get the sibling
TreeViewItem item = parent.ItemContainerGenerator.ContainerFromIndex(index) as TreeViewItem;
if (item != null && item.IsEnabled)
{
// Get the last focusable descendent of the sibling
TreeViewItem last = item.GetLastContainer();
if (last != null)
{
return last;
}
}
}
return parent as TreeViewItem;
}
}

Resources