-
- bridgetfoote
- Posts: 2
- Joined:
Custom ItemsControl to maintain focused index
Hello, trying to create a custom items control that maintains the index of focus when an item is replaced. For our use case we have an ItemsControl that contains a number of content cards. While we are retrieving the information to create the proper cards in the ItemsControl, it is initialized with a list of loading cards which are then replaced by the proper cards. We want to maintain the index of focus once the proper cards load in - ex: if the second loading card is in focus, when it is replaced that replacement card which is still the second card in the items control is focused. All cards inherit from the same view model class, so we have SpecificCardTypeOneViewModel, SpecificCardTypeTwoViewModel, etc. which all inherit from BaseCardViewModel. The ItemsSource for the ItemsControl is an observable collection of BaseCardViewModel.
Attempted to achieve this by overloading OnItemsChanged and OnPreviewGotKeyboardFocus as follows:
However, this only works when the card that is being replaced is the same subclass as the card that is replacing it, so they both are type SpecificCardTypeOneViewModel. If we try to replace a SpecificCardTypeOneViewModel item with a SpecificCardTypeTwoViewModel we get into the OnItemsChanged override and element->Focus() returns false but element->GetIsKeyboardFocusWithin() returns true and so we return but the ItemsControl loses focus.
Is there something different happening to the container in this case? What could be causing it to not focus correctly?
Attempted to achieve this by overloading OnItemsChanged and OnPreviewGotKeyboardFocus as follows:
Code: Select all
void CustomItemsControl::OnItemsChanged(const Noesis::NotifyCollectionChangedEventArgs& args)
{
switch(args.action)
{
case Noesis::NotifyCollectionChangedAction_Replace:
{
m_replacingElement = true;
if (m_focusedIndex == args.newIndex)
{
Noesis::ItemCollection* items = GetItems();
Noesis::ItemContainerGenerator* generator = GetItemContainerGenerator();
int index = args.newIndex;
OnBringItemIntoView(items->GetItemAt(index));
Noesis::UIElement* element = Noesis::DynamicCast<Noesis::UIElement*>(generator->ContainerFromIndex(index));
if (element != 0)
{
if (element->Focus() || element->GetIsKeyboardFocusWithin())
{
return;
}
// container is not focusable, try to focus its contents
Noesis::TraversalRequest request = { Noesis::FocusNavigationDirection_First, false };
element->MoveFocus(request);
}
}
}
}
}
Noesis::Vector<Noesis::UIElement*> CustomItemsControl::FindFocusableElements(Noesis::Visual* root)
{
Noesis::Vector<Noesis::UIElement*> children;
if (root == nullptr)
{
return children;
}
const uint32_t visualChildCount = Noesis::VisualTreeHelper::GetChildrenCount(root);
for (uint32_t n = 0; n < visualChildCount; n++) {
Noesis::Visual* child = Noesis::VisualTreeHelper::GetChild(root, n);
Noesis::UIElement* element = Noesis::DynamicCast<Noesis::UIElement*>(child);
// If not enabled or visible, don't even bother with it
if (!element->GetIsEnabled() || !element->GetIsVisible())
continue;
// If it's focusable, that's what we want
if (element->GetFocusable()) {
children.PushBack(element);
}
// Otherwise, keep searching downward
else {
auto focusableChildren = FindFocusableElements(child);
children.Insert(children.End(), focusableChildren.Begin(), focusableChildren.End());
}
}
return children;
}
void CustomItemsControl::OnPreviewGotKeyboardFocus(const Noesis::KeyboardFocusChangedEventArgs& args)
{
if (!m_replacingElement)
{
auto focusableElements = FindFocusableElements(this);
int numFocusableElements = focusableElements.Size();
for (int i = (numFocusableElements - 1); i >= 0; i--)
{
if (focusableElements.Back() == args.newFocus)
{
m_focusedIndex = i;
return;
}
focusableElements.PopBack();
}
}
else
{
m_replacingElement = false;
}
}
Is there something different happening to the container in this case? What could be causing it to not focus correctly?
-
-
sfernandez
Site Admin
- Posts: 2910
- Joined:
Re: Custom ItemsControl to maintain focused index
Hi,
Just a suggestion, for finding the index of the item containing the focused element you can just use the ItemsControl::ContainerFromElement() + ItemContainerGenerator::ContainerFromIndex().
Regarding your problem, when the item being replaced has the same type, what happens with the check
Is also element->Focus() returning false, and element->GetIsKeyboardFocusWithin() returning true?
Could you try to call element->UpdateLayout() before calling element->Focus()? It might happen that the new type requires the use of a new data template and elements inside are not yet generated.
Just a suggestion, for finding the index of the item containing the focused element you can just use the ItemsControl::ContainerFromElement() + ItemContainerGenerator::ContainerFromIndex().
Regarding your problem, when the item being replaced has the same type, what happens with the check
Code: Select all
if (element->Focus() || element->GetIsKeyboardFocusWithin())
Could you try to call element->UpdateLayout() before calling element->Focus()? It might happen that the new type requires the use of a new data template and elements inside are not yet generated.
Who is online
Users browsing this forum: Ahrefs [Bot] and 0 guests