I have created a WPF application . I need to identify Next focusable element . For that I have added following code.
UIElement elementWithFocus = System.Windows.Input.Keyboard.FocusedElement as UIElement;
var a = elementWithFocus.PredictFocus(FocusNavigationDirection.Next);
But it is showing that Next is not supported. How I can achieve the same?
Try to list all the controls on your window and search for the one which has it's TabIndex property equals to the current TabIndex property + 1
private UIElement SearchNextControl(int currentTabIndex)
{
foreach (UIElement element in AllTheControls)
{
if(element.TabIndex == (currentTabIndex + 1))
{
return element;
}
}
return null;
}
Related
I'm trying to find the extents of a UI element in a ListBox. I use WPF / c# for this.
The code is roughly as follows:
ObservableCollection<SomeUserControl> TheCollection = new ObservableCollection<SomeUserControl>();
// setup
ListBox lb = new ListBox();
// code omitted here that setup the listbox with 2 columns
lb.ItemsSource = TheCollection;
// code omitted here that populates TheCollection with UI elements
Then I register a mouse event and I would like to know when a specific point is bounded by a specific element in the listbox. For this I have the following means to do it:
UIElement GetObjectHit(Point P) {
if (!lb.IsVisible) return null;
foreach(var i in TheCollection) {
FrameworkElement item = i as FrameworkElement;
if (item == null || !item.IsVisible)
continue;
Point P00 = item.TranslatePoint(new Point(0,0), lb);
Point P11 = item.TranslatePoint(new Point(item.ActualWidth, item.ActualHeight), lb);
if (IsBounded(P, P00, P11)
return item;
}
return null;
}
UIElement GetObjectHit(MouseButtonEventArgs e) {
return GetObjectHit(e.GetPosition(lb));
}
This works great with one exception...
The different user controls I have have varying sizes and the ListBox will then extend each item to the maximum element size currently in list and then center the smaller UserControls that are in the list.
So what's the problem?
Well when the position is in the white area which is inside the ListBoxItem area but outside the UserControl area the code above fails to find the intersection.
So my question is as follows:
Do I have to do a separate loop first to get the maximum extends of each UI element or is there any way to get the current extent of the ListBoxItem in the ListBox (even though the UserControl extent is smaller)?
In the unlikely event if anyone needs the answer to this I managed to get this to work as follows:
UIElement GetObjectHit(Point P) {
if (!lb.IsVisible) return null;
foreach(var i in TheCollection) {
var item = i as SomeUserControl;
ListBoxItem lbi = lb.ContainerFromElement(item) as ListBoxItem;
if (lbi == null || !lbi.IsVisible)
continue;
Point P00 = lbi.TranslatePoint(new Point(0,0), lb);
Point P11 = lbi.TranslatePoint(new Point(lbi.ActualWidth, lbi.ActualHeight), lb);
if (IsBounded(P, P00, P11)
return i;
}
return null;
}
I want to modify the content in a TabItem of a TabView. And that TabItem uses DataTemplate.
When I am trying to access the children of that item like the following:
var container = tabview.ContainerFromIndex(tabview.SelectedIndex);
int count = VisualTreeHelper.GetChildrenCount(container);
I got the ArgumentException: Wrong Parameter Reference on the second line. How should I use VisualTreeHelper to modify it?
Here is a easy method:
public static T GetChildObject<T>(DependencyObject obj, string name) where T : FrameworkElement
{
DependencyObject child = null;
T grandChild = null;
for (int i = 0; i <= VisualTreeHelper.GetChildrenCount(obj) - 1; i++)
{
child = VisualTreeHelper.GetChild(obj, i);
if (child is T && (((T)child).Name == name | string.IsNullOrEmpty(name)))
{
return (T)child;
}
else
{
grandChild = GetChildObject<T>(child, name);
}
if (grandChild != null)
{
return grandChild;
}
}
return null;
}
From your description, you can already get the container of the target element. Let's assume that the element you need is named TargetEle and the type is TextBlock. You can write it like this:
var target = GetChildObject<TextBlock>(container,"TargetEle");
Update
I tested your code and found that you didn't capture the events loaded by the page.
In fact, the SelectionChanged event is fired when the TabView is just created, but the visual tree is not loaded yet, and you can't get the content from it through the code. You can create an IsLoaded property in the page, set it to True when Page Loaded, and determine this property in the SelectionChanged time.
Only when it is True, proceed to the next step.
Is there a way to evaluate the actual position of a TreeViewItem on a Canvas if its Parent TreeViewItem is collapsed (meaning <ParentTreeViewItem>.IsExpanded = false;)? When debugging neither the Visibility nor the Position information of the collapsed item in the parent´s ItemsHost seems to be updated.
Appreciating any hint!
Cheers, Alex
Found a workaround for this problem:
Basically whenever a TreeViewItem fires an OnCollapsed or OnExpanded event I force all other TreeViewItems to determine their own position based on the IsExpanded property of their ancestors up to the tree root.
If any of the ancestors is collapsed (i. e. IsExpanded == false) I apply its position to the current TreeViewItems position.
If none of the ancestors is collapsed, I apply its own position (whereas position is a custom Point property of the TreeViewItem).
Code sample:
private Point DeterminePosition()
{
Point point = this.position;
if (ParentTreeViewItem != null)
{
MyTreeViewItem parent = null, lastCollapsed = null;
parent = ParentTreeViewItem;
while (parent != null)
{
if (parent.IsExpanded == false)
{
lastCollapsed = parent;
}
parent = parent.ParentTreeViewItem;
}
if (lastCollapsed != null)
{
point = new Point(position.X, lastCollapsed.Position.Y);
}
}
return point;
}
I have bookmarks list, when I tap on a bookmark, another page is loaded with a listA consists of several items.
Now suppose, I tap on a bookmark, which points to the item index 100 of the listA... the other listA opens, I manage to set the SelectedIndex of the listA to 100, which is somewhere down the list is not visible.
The problem is that, the SelectedIndex is set to 100, but the list still shows the top most item, on the top.
How can I set the item number 100 on the top, when it loads the contents?
Works perfectly with ScrollViewer.ScrollToVerticalOffset Method
Step I. Call Loaded event of the listA
<ListBox Name="ListA" Loaded="HookScrollViewer">
Step II. Define the "HookScrollViewer" method
private void HookScrollViewer(object sender, RoutedEventArgs e)
{
var element = (FrameworkElement)sender;
var scrollViewer = FindChildOfType<ScrollViewer>(element);
if (scrollViewer == null)
return;
scrollViewer.ScrollToVerticalOffset(lstA.SelectedIndex);
}
Step III. Define the "FindChildOfType" method
public static T FindChildOfType<T>(DependencyObject root) where T : class
{
var queue = new Queue<DependencyObject>();
queue.Enqueue(root);
while (queue.Count > 0)
{
var current = queue.Dequeue();
for (int i = VisualTreeHelper.GetChildrenCount(current) - 1; 0 <= i; i--)
{
var child = VisualTreeHelper.GetChild(current, i);
var typedChild = child as T;
if (typedChild != null)
{
return typedChild;
}
queue.Enqueue(child);
}
}
return null;
}
And it works with "ListA" (Replace ListA by the name of your ListBox)
Ref: ListBox offset in WP7
ListBox is really pain in the ass (especially with complex datatemplates and virtualizing). You should use this workaround:
listbox.SelectedIndex = 1000; //I mean index of the last item
listbox.UpdateLayout();
listbox.SelectedIndex = 100;
listbox.UpdateLayout();
Hope this helps
Try this:
listBox2.TopIndex = listBox2.SelectedIndex;
I'm not sure if I understand your question correctly. But If you just want to set an item on top of the listbox items then I would do..
listbox.Items.Insert(0, "something);
or
Use linq to order them
listbox.Items.OrderBy("something");
I have a list of files in a ListView in WPF. Users can drag files onto the list view, and right now they are just appended to the end of the list. Is it possible to insert the file into the ListView right where the user dropped it?
WPF isn't really designed to be used that way. While you can brute force add ListViewItem's directly to the ListView, the way it's really supposed to work is that you have a collection of some kind (ObservableCollection<FileInfo> would work well) and bind the ListView's ItemsSource property to that collection.
Then the answer is simple. Instead of the Add method, you use the Insert method of the collection which takes an index.
As for finding which ListViewItem the mouse event occurred over, you could use the VisualTreeHelper.HitTest method.
From my point of view it is little tricky when I used the templated item. I have fight with it little bit. I am sharing my usecase which works with DraggableListBox. But I suppose the same solution works with ListBox control.
As the first I created the dependency object extension which is able to provide me ListItem element:
public static class WpfDomHelper
{
public static T FindParent<T>(this DependencyObject child) where T : DependencyObject
{
DependencyObject parentObject = VisualTreeHelper.GetParent(child);
if (parentObject == null) return null;
T parent = parentObject as T;
if (parent != null)
return parent;
else
return FindParent<T>(parentObject);
}
}
Then I implemented Drop logic which inserts(adds) item according specific Drop Y position of destination ListBoxItems:
private void Grid_Drop(object sender, DragEventArgs e)
{
int dropIndex = -1; // default position directong to add() call
// checking drop destination position
Point pt = e.GetPosition((UIElement)sender);
HitTestResult result = VisualTreeHelper.HitTest(this, pt);
if (result != null && result.VisualHit != null)
{
// checking the object behin the drop position (Item type depend)
var theOne = result.VisualHit.FindParent<Microsoft.TeamFoundation.Controls.WPF.DraggableListBoxItem>();
// identifiing the position according bound view model (context of item)
if (theOne != null)
{
//identifing the position of drop within the item
var itemCenterPosY = theOne.ActualHeight / 2;
var dropPosInItemPos = e.GetPosition(theOne);
// geting the index
var itemIndex = tasksListBox.Items.IndexOf(theOne.Content);
// decission if insert before or below
if (dropPosInItemPos.Y > itemCenterPosY)
{ // when drag is gropped in second half the item is inserted bellow
itemIndex = itemIndex + 1;
}
dropIndex = itemIndex;
}
}
.... here create the item .....
if (dropIndex < 0)
ViewModel.Items.Add(item);
else
ViewModel.Items.Insert(dropIndex, item);
e.Handled = true;
}
So this solution works with my template DraggableListBoxView, I suppose the same solution must work with standard ListBoxView. Good Luck
You can do this. It takes a bit of work, but it can be done. There are a couple demos out there, here is one on CodeProject. This particular one is by the wpf master known as Josh Smith. It's probably not exactly what you are looking for, but it should be pretty darn close.