Help setting focus to element in ItemsControl
Hi Noesis fokes,
I'm trying to set the default focus to the first element of an ItemControl, on load and if the items change. However, my below code doesn't work.
element->Focus() and element->MoveFocus(request) return false. The end result is no items have focus.
Please advise on what I'm doing wrong.
Thanks in advanced!
Update: I have found that the above works if I add a very small timer to set the focus after a few ms. Any thought on how I can get round this hack would be great :D
I'm trying to set the default focus to the first element of an ItemControl, on load and if the items change. However, my below code doesn't work.
element->Focus() and element->MoveFocus(request) return false. The end result is no items have focus.
Please advise on what I'm doing wrong.
Thanks in advanced!
Code: Select all
MAUserControl::MAUserControl() : UserControl()
{
Loaded() += Noesis::MakeDelegate(this, &MAUserControl::OnLoaded);
}
void MAUserControl::OnLoaded(Noesis::BaseComponent*, const Noesis::RoutedEventArgs&)
{
const char* focus_name = GetValue<Noesis::String>(DefaultFocusElementNameProperty).Str();
if (focus_name != nullptr)
{
Noesis::BaseComponent* focus_child = FindName(focus_name);
if (focus_child != nullptr)
{
Noesis::ItemsControl* item_control = Noesis::DynamicCast<Noesis::ItemsControl*>(focus_child);
if (item_control != nullptr)
{
Noesis::ItemCollection* items = item_control->GetItems();
if (items != nullptr)
{
items->CollectionChanged() += Noesis::MakeDelegate(this, &MAUserControl::OnItemsChaged);
}
}
}
}
SetDefaultFocus();
}
void MAUserControl::OnItemsChaged(Noesis::BaseComponent*, const Noesis::NotifyCollectionChangedEventArgs&)
{
SetDefaultFocus();
}
void MAUserControl::SetDefaultFocus()
{
const char* focus_name = GetValue<Noesis::String>(DefaultFocusElementNameProperty).Str();
if (focus_name != nullptr)
{
Noesis::BaseComponent* focus_child = FindName(focus_name);
if (focus_child != nullptr)
{
Noesis::ItemsControl* item_control = Noesis::DynamicCast<Noesis::ItemsControl*>(focus_child);
if (item_control != nullptr && item_control->GetHasItems())
{
Noesis::ItemContainerGenerator* generator = item_control->GetItemContainerGenerator();
Noesis::UIElement* element = Noesis::DynamicCast<Noesis::UIElement*>(generator->ContainerFromIndex(0));
if (element != nullptr)
{
if (element->Focus() || element->GetIsKeyboardFocusWithin())
{
return;
}
// container is not focusable, try to focus its contents
Noesis::TraversalRequest request = { Noesis::FocusNavigationDirection_First, false };
if (element->MoveFocus(request))
{
return;
}
Noesis::DependencyObject* focusScope = Noesis::FocusManager::GetFocusScope(element);
if (focusScope != nullptr && focusScope != element)
{
Noesis::FocusManager::SetFocusedElement(focusScope, element);
return;
}
}
}
}
}
}
Code: Select all
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Height="50" Margin="0 0 0 5"
DockPanel.Dock="Bottom"
Style="{StaticResource ButtonStyleRectangular_Resize}"
Content="{Binding PlayerChoiceText}">
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
Re: Help setting focus to element in ItemsControl
I have found the solution, posting my findings for others :)
ItemControls go through a few stages before its children can accept a focus. Turns out the OnItemsChanged changed event is purely a data change and is too early, the visual containers have not been built yet.
A rough order of execution when changing the ItemsControl Items Source
ItemsControl->OnItemsChanged
ItemsControl->ContentGenerator->OnItemsChanged
ItemsControl->ContentGenerator->BatchGeneration
ItemsControl->ContentGenerator->OnStatusChanged:GeneratorStatus_GeneratingContainers
ItemsControl->ContentGenerator->OnStatusChanged:GeneratorStatus_ContainersGenerated
ItemsControl->ContentGenerator->ItemContainer->OnInitialised
ItemsControl->ContentGenerator->ItemContainer->OnLoaded <--- at this point I found I can reliably set focus
Here is a snippet of my custom focus Dependency object
ItemControls go through a few stages before its children can accept a focus. Turns out the OnItemsChanged changed event is purely a data change and is too early, the visual containers have not been built yet.
A rough order of execution when changing the ItemsControl Items Source
ItemsControl->OnItemsChanged
ItemsControl->ContentGenerator->OnItemsChanged
ItemsControl->ContentGenerator->BatchGeneration
ItemsControl->ContentGenerator->OnStatusChanged:GeneratorStatus_GeneratingContainers
ItemsControl->ContentGenerator->OnStatusChanged:GeneratorStatus_ContainersGenerated
ItemsControl->ContentGenerator->ItemContainer->OnInitialised
ItemsControl->ContentGenerator->ItemContainer->OnLoaded <--- at this point I found I can reliably set focus
Here is a snippet of my custom focus Dependency object
Code: Select all
void MAFocusDependencyObject::FocusOnItemChange(Noesis::FrameworkElement* element, bool attach)
{
Noesis::ItemsControl* item_control = Noesis::DynamicCast<Noesis::ItemsControl*>(element);
if (item_control != nullptr)
{
Noesis::ItemContainerGenerator* generator = item_control->GetItemContainerGenerator();
generator->StatusChanged() -= Noesis::MakeDelegate(this, &MAFocusDependencyObject::OnCollectionGenStatusChanged);
if (attach)
{
generator->StatusChanged() += Noesis::MakeDelegate(this, &MAFocusDependencyObject::OnCollectionGenStatusChanged);
}
}
}
void MAFocusDependencyObject::OnCollectionGenStatusChanged(Noesis::BaseComponent* component, const Noesis::EventArgs&)
{
Noesis::ItemContainerGenerator* generator = Noesis::DynamicCast <Noesis::ItemContainerGenerator*>(component);
if (generator->GetStatus() == Noesis::GeneratorStatus::GeneratorStatus_ContainersGenerated)
{
Noesis::FrameworkElement* item_focus_child = Noesis::DynamicCast<Noesis::FrameworkElement*>(generator->ContainerFromIndex(0));
if (item_focus_child != nullptr)
{
item_focus_child->Loaded() -= Noesis::MakeDelegate(this, &MAFocusDependencyObject::OnCollectionItemLoaded);
item_focus_child->Loaded() += Noesis::MakeDelegate(this, &MAFocusDependencyObject::OnCollectionItemLoaded);
}
}
}
void MAFocusDependencyObject::OnCollectionItemLoaded(Noesis::BaseComponent* component, const Noesis::RoutedEventArgs&)
{
Noesis::FrameworkElement* focus_element = Noesis::DynamicCast<Noesis::FrameworkElement*>(component);
if (focus_element != nullptr)
{
SetFocus(focus_element);
}
}
-
-
sfernandez
Site Admin
- Posts: 3222
- Joined:
Re: Help setting focus to element in ItemsControl
Thanks for sharing your solution.
As you described, a UI element must be connected to the View and visible/enabled to receive focus. Waiting to the Loaded event is a good place for this to be true.
Another option would be to hook to the LayoutUpdated event instead when the ItemsSource changed.
As you described, a UI element must be connected to the View and visible/enabled to receive focus. Waiting to the Loaded event is a good place for this to be true.
Another option would be to hook to the LayoutUpdated event instead when the ItemsSource changed.
Who is online
Users browsing this forum: Ahrefs [Bot], Google [Bot] and 1 guest