Focusing ListBoxItem after ItemCollection changed
Hi guys, I'll try my best to explain my problem.
I have derived a new UserControl from a ListBox (Let's call it "CustomListBox"), and the ItemContainerGenerator for this type generates a CustomListBoxItem. I have overridden the OnSelectionChanged virtual to focus the item when the selection changes:
In the XAML, within the DataTemplate which is used for the ContentControl for the CustomListBoxItem, I have a DataTrigger which listens out for this focus property, and it plays a focus / unfocus storyboard:
The CustomListBox has items data bound via the ItemsSource property:
This works great. In C++, when I receive an OnInitialised I call SetSelectedIndex on the CustomListBox and it focuses the first element, taking focus off all other controls and ensures that we are only ever focusing items within this listbox which is exactly what I want.
However, at some point the data behind the binding changes:
I populate the data initially and then I clear the contents and rebuild it with some new data:
I want to be able to focus the first element after the ItemContainerGenerator has finished generating the CustomListBoxItem instances, but I can't seem to find a way to do it at the right time.
Things I have tried:
Many thanks in advance,
-Steven
I have derived a new UserControl from a ListBox (Let's call it "CustomListBox"), and the ItemContainerGenerator for this type generates a CustomListBoxItem. I have overridden the OnSelectionChanged virtual to focus the item when the selection changes:
Code: Select all
void CustomListBox::OnSelectionChanged(const SelectionChangedEventArgs& args)
{
ParentClass::OnSelectionChanged(args);
ItemCollection *pItemContainer = GetItems();
if (nullptr != pItemContainer)
{
if (GetSelectedIndex() >= 0 && GetSelectedIndex() < pItemContainer->Count())
{
BaseComponent *pComponent = pItemContainer->GetComponent(GetSelectedIndex()).GetPtr();
if (nullptr != pComponent)
{
UIElement *pElement = IsItemItsOwnContainerOverride(pComponent) ?
static_cast<UIElement*>(pComponent) :
DynamicCast<UIElement*>(GetItemContainerGenerator()->ContainerFromItem(pComponent));
if (nullptr != pElement)
{
pElement->Focus();
}
}
}
}
}
Code: Select all
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type ContentControl}}, Path=IsFocused}" Value="True">
<DataTrigger.EnterActions>
<StopStoryboard BeginStoryboardName="Off" />
<BeginStoryboard x:Name="On" Storyboard="{StaticResource ResourceKey=Custom_Highlight_On}"/>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="On" />
<BeginStoryboard x:Name="Off" Storyboard="{StaticResource ResourceKey=Custom_Highlight_Off}"/>
</DataTrigger.ExitActions>
</DataTrigger>
</DataTemplate.Triggers>
Code: Select all
<local:CustomListBox x:Name="MyCustomListBoxInstance" ItemsSource="{Binding CustomListBoxSource}"/>
However, at some point the data behind the binding changes:
Code: Select all
class DataContext : public NotifyChangedPropertyBase
{
Ptr<ObservableCollection<SomeClass>> CustomListBoxSource;
};
NS_IMPLEMENT_REFLECTION(DataContext)
{
NsMeta<TyepeId>("MyNamespace.DataContext");
NsProp("CustomListBoxSource", &SelfClass::CustomListBoxSource);
}
Code: Select all
void RebuildData(DataContext &rContext)
{
rContext.CustomListBoxSource->Clear();
rContext.CustomListBoxSource->Add(MakePtr<SomeClass>(Args...));
// ...etc
}
Things I have tried:
- Assign a multidatatrigger to the style of CustomListBox. The conditions are bound to the "HasItems" and "SelectedItem" property. If the HasItems is true and IsSelected is false, it runs a setter to set the SelectedIndex to 0:
This didn't work... I breakpointed the multidatatrigger, it invalidated the setters but it never executed my code after the item had been created.Code: Select all
<Style.Triggers> <MultiDataTrigger> <MultiDataTrigger.Conditions> <Condition Binding="{Binding IsSelected}" Value="False" /> <Condition Binding="{Binding HasItems}" Value="True" /> </MultiDataTrigger.Conditions> <MultiDataTrigger.Setters> <Setter Property="SelectedIndex" Value="0" /> </MultiDataTrigger.Setters> </MultiDataTrigger> </Style.Triggers>
- Override the ListBox::OnItemsChanged event, ensurring that I call SetSelectedItem if we have items in the itemscollection but don't have a selectedindex:
This also didn't work. Despite selecting the item and focusing the element correctly, the storyboard never played. I'm wondering if it focused the element too early?Code: Select all
/*virtual*/ void CustomListBox::OnItemsChanged(const Noesis::NotifyCollectionChangedEventArgs& args) /*override;*/ { ParentClass::OnItemsChanged(args); if (args.action == NotifyCollectionChangedAction_Add || args.action == NotifyCollectionChangedAction_Replace) { if (GetSelectedIndex() == -1) { if (GetHasItems()) { SetSelectedIndex(0); } } } }
- What is the flow for item generation? When can I guarantee that the item container has generated the itemscontainers and they're good to be used?
- Does this sound like a reasonable approach? I feel like I'm missing something. I seem to be fighting the "WPF" way, is there a correct way to do what I want?
Many thanks in advance,
-Steven
-
sfernandez
Site Admin
- Posts: 2983
- Joined:
Re: Focusing ListBoxItem after ItemCollection changed
Hi,
The goal here as I understand is whenever an item gets selected it also gets focus; just remember that selecting an item using mouse or keyboard automatically focuses the item, but I guess you want also to focus the item if it gets selected by code/bindings, right?.
I think this can be simplified a lot by using a plain ListBox/ListBoxItem with interactivity triggers/actions. For example, ListBoxItem Style can have a trigger for the IsSelected property to invoke a SetFocusAction (we did something similar in our QuestLog sample but for the MouseEnter event):
About the animations you want to trigger, can't they just be part of the ListBoxItem template?
They can be storyboards fired on Enter/Exit actions of some trigger, or just part of the Focused/Unfocused visual states.
To focus the first element in the list after the collection is being cleared and populated again, you just need to set the SelectedItem of the ListBox to that first element (becuase when the container gets generated, the triggers will automatically focus the ListBoxItem). If the ListBox.SelectedItem property is already bound to some property in the ViewModel then it is trivial to accomplish that:
Hope this helps.
The goal here as I understand is whenever an item gets selected it also gets focus; just remember that selecting an item using mouse or keyboard automatically focuses the item, but I guess you want also to focus the item if it gets selected by code/bindings, right?.
I think this can be simplified a lot by using a plain ListBox/ListBoxItem with interactivity triggers/actions. For example, ListBoxItem Style can have a trigger for the IsSelected property to invoke a SetFocusAction (we did something similar in our QuestLog sample but for the MouseEnter event):
Code: Select all
<Style x:Key="ListBoxItemStyle" TargetType="ListBoxItem" BasedOn="{StaticResource {x:Type ListBoxItem}}">
<Setter Property="Template" Value="{StaticResource ListBoxItemTemplate}"/>
<Setter Property="noesis:StyleInteraction.Triggers">
<Setter.Value>
<noesis:StyleTriggerCollection>
<ei:DataTrigger
Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}}"
Value="True">
<noesis:SetFocusAction/>
</ei:DataTrigger>
</noesis:StyleTriggerCollection>
</Setter.Value>
</Setter>
</Style>
They can be storyboards fired on Enter/Exit actions of some trigger, or just part of the Focused/Unfocused visual states.
To focus the first element in the list after the collection is being cleared and populated again, you just need to set the SelectedItem of the ListBox to that first element (becuase when the container gets generated, the triggers will automatically focus the ListBoxItem). If the ListBox.SelectedItem property is already bound to some property in the ViewModel then it is trivial to accomplish that:
Code: Select all
myObservableCollection->Clear();
myObservableCollection->Add(...); // etc.
SetMySelectedItem(myObservableCollection->Get(0));
Re: Focusing ListBoxItem after ItemCollection changed
- Oops, logged into an old account reposting in a minute on my actual account :)
Re: Focusing ListBoxItem after ItemCollection changed
Apologies for the previous post, I was logged into an old account from my home PC. I edited my post and posting the initial response here on the correct account now :)
----
Thank you for the response.
And then the focus animation actually modifies properties on the textblock. Obviously our exact example is a far more complex hierarchy, but essentially we have a structure defined in a datatemplate which wants to animate when the parent listbox is focused. In our exact case, we have a ControlTemplate which makes our CustomListBoxItem a generic button, and inside that button we can either have vector paths, texts, bitmap images which pull from live off-screen render targets etc. The artist wants to actually animate the inner content for each bespoke type of data we have (e.g. text animates differently to the vector paths). Since the content is driven via a DataTemplate, the storyboard has to be defined in the DataTemplate as well. However, we do have a trigger defined in the datatemplate, and the binding is set to the IsSelected property on the CustomListBoxItem which is part of the hierarchy. The specific XAML is as follows:
Many thanks,
-Steven
----
Thank you for the response.
Correct. There are lots of things in code which change the focus directly from code. Essentially I want the selected index and focus to be in sync at all points when this customlistbox is on screen.The goal here as I understand is whenever an item gets selected it also gets focus; just remember that selecting an item using mouse or keyboard automatically focuses the item, but I guess you want also to focus the item if it gets selected by code/bindings, right?.
Thank you for the code example, I'll give this a try, it makes sense.I think this can be simplified a lot by using a plain ListBox/ListBoxItem with interactivity triggers/actions. For example, ListBoxItem Style can have a trigger for the IsSelected property to invoke a SetFocusAction (we did something similar in our QuestLog sample but for the MouseEnter event):
Unfortunately the animation has to be part of the datatemplate. Our listboxitem is a container, but we want the elements created by the DataTemplate to animate when the ListBoxItem is focused itself. e.g. we have this in the hierarchy:About the animations you want to trigger, can't they just be part of the ListBoxItem template?
They can be storyboards fired on Enter/Exit actions of some trigger, or just part of the Focused/Unfocused visual states.
Code: Select all
<listboxitem>
<contentpresenter>
<grid>
<textblock text="blah" foreground="red" />
</grid>
</contentpresenter>
</listboxitem>
Code: Select all
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Shared:CustomListBoxItem}}, Path=IsSelected}" Value="True">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource ResourceKey=PopUp_Highlight_On}"/>
<BeginStoryboard x:Name="Highlight_Idle" Storyboard="{StaticResource ResourceKey=PopUp_Highlilght_Idle}"/>
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<StopStoryboard BeginStoryboardName="Highlight_Idle" />
<BeginStoryboard Storyboard="{StaticResource ResourceKey=PopUp_Highlight_Off}"/>
</DataTrigger.ExitActions>
</DataTrigger>
</DataTemplate.Triggers>
I've tried to do precisely this, and it doesn't seem to work. However, I was looking through the patch notes earlier and I saw this snippet from 2.2.3:To focus the first element in the list after the collection is being cleared and populated again, you just need to set the SelectedItem of the ListBox to that first element (becuase when the container gets generated, the triggers will automatically focus the ListBoxItem). If the ListBox.SelectedItem property is already bound to some property in the ViewModel then it is trivial to accomplish that:
We're currently on version 2.2.1. Do you think this issue may be causing some of my other problems? We also experience a few other issues like the following:Fixed Selector resetting SelectedIndex and SelectedItem bound properties on DataContext changes.
Perhaps it's time for me to upgrade to 2.2.6. I was hoping to avoid doing that before our near deadlines and I don't want to risk introducing new bugs, but it looks like there's a bunch of bug fixes which have gone in which would benefit us.Enhancement No more binding errors when clearing or changing ObservableCollection (#1572).
Many thanks,
-Steven
Re: Focusing ListBoxItem after ItemCollection changed
Hi there, just a quick update. I tried to add the style trigger and it hit an assert. Essentially the Selector::OnSelectedIndexChanged sets the IsUpdating scope flag when the selected index has changed. This ultimately causes the style trigger to execute which sets focus. The focus calls ListBoxItem::OnGotFocus which attempts to call Selector::InternalSelectRange, which asserts because the instance already has the flag set.
I've highlighted the 2 functions where the scope flag is attempting to be set by prefixing the signature with ">>>".
-Steven
Code: Select all
>>> MyExe.exe!Noesis::Selector::InternalSelectRange(int start, int end) Line 660 C++
MyExe.exe!Noesis::ListBox::SingleSelection(Noesis::ListBoxItem * lbi) Line 146 C++
MyExe.exe!Noesis::ListBox::ItemClicked(Noesis::ListBoxItem * lbi, bool toggleSelection, bool shiftKeyDown, bool ctrlKeyDown) Line 305 C++
MyExe.exe!Noesis::ListBox::ItemClicked(Noesis::ListBoxItem * lbi, bool toggleSelection) Line 284 C++
MyExe.exe!Noesis::ListBoxItem::OnGotFocus(const Noesis::RoutedEventArgs & e) Line 195 C++
MyExe.exe!Noesis::UIElement::OnIsFocusedChanged(const Noesis::DependencyPropertyChangedEventArgs & e) Line 2807 C++
MyExe.exe!Noesis::UIElement::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & args) Line 2003 C++
MyExe.exe!Noesis::FrameworkElement::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & args) Line 1636 C++
MyExe.exe!Noesis::Control::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & args) Line 442 C++
MyExe.exe!Noesis::ContentControl::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & args) Line 146 C++
MyExe.exe!Noesis::ListBoxItem::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & args_) Line 120 C++
MyExe.exe!Noesis::DependencyObject::NotifyPropertyChanged(const Noesis::DependencyProperty * dp, Noesis::StoredValue * storedValue, const void * oldValue, const void * newValue, bool valueChanged, bool isBaseComponent, const Noesis::PropertyMetadata * metadata) Line 1155 C++
MyExe.exe!Noesis::DependencyObject::InternalSetValue(const Noesis::DependencyProperty * dp, void * oldValue, const void * newValue, void * coercedValue, unsigned char priority, Noesis::Expression * newExpression, const Noesis::PropertyMetadata * metadata, Noesis::Value::Destination destination, bool isBaseComponent) Line 797 C++
MyExe.exe!Noesis::DependencyObject::SetValue_<bool>(Noesis::Int2Type<0> __formal, const Noesis::DependencyProperty * dp, bool value, Noesis::Value::Destination destination) Line 168 C++
MyExe.exe!Noesis::DependencyObject::SetReadOnlyProperty<bool>(const Noesis::DependencyProperty * dp, bool value) Line 62 C++
MyExe.exe!Noesis::Keyboard::Focus(Noesis::UIElement * element, bool askOld, bool askNew, bool canBeNull) Line 442 C++
MyExe.exe!Noesis::Keyboard::Focus(Noesis::UIElement * element) Line 149 C++
MyExe.exe!Noesis::UIElement::Focus() Line 453 C++
MyExe.exe!NoesisApp::SetFocusAction::Invoke(Noesis::BaseComponent * __formal) Line 30 C++
MyExe.exe!NoesisApp::TriggerAction::CallInvoke(Noesis::BaseComponent * parameter) Line 47 C++
MyExe.exe!NoesisApp::TriggerBase::InvokeActions(Noesis::BaseComponent * parameter) Line 76 C++
MyExe.exe!NoesisApp::DataTrigger::Evaluate() Line 99 C++
MyExe.exe!NoesisApp::DataTrigger::EvaluateBindingChange() Line 81 C++
MyExe.exe!NoesisApp::PropertyChangedTrigger::OnBindingChanged(Noesis::DependencyObject * d, const Noesis::DependencyPropertyChangedEventArgs & __formal) Line 70 C++
MyExe.exe!Noesis::Delegate<void __cdecl(Noesis::DependencyObject *,Noesis::DependencyPropertyChangedEventArgs const &)>::FreeFuncStub<void (__cdecl*)(Noesis::DependencyObject *,Noesis::DependencyPropertyChangedEventArgs const &)>::Invoke(Noesis::DependencyObject * <args_0>, const Noesis::DependencyPropertyChangedEventArgs & <args_1>) Line 391 C++
MyExe.exe!Noesis::Delegate<void __cdecl(Noesis::DependencyObject *,Noesis::DependencyPropertyChangedEventArgs const &)>::operator()(Noesis::DependencyObject * <args_0>, const Noesis::DependencyPropertyChangedEventArgs & <args_1>) Line 155 C++
MyExe.exe!Noesis::DependencyObject::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & args) Line 513 C++
MyExe.exe!Noesis::Freezable::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & e) Line 237 C++
MyExe.exe!Noesis::DependencyObject::NotifyPropertyChanged(const Noesis::DependencyProperty * dp, Noesis::StoredValue * storedValue, const void * oldValue, const void * newValue, bool valueChanged, bool isBaseComponent, const Noesis::PropertyMetadata * metadata) Line 1155 C++
MyExe.exe!Noesis::DependencyObject::InternalSetValue(const Noesis::DependencyProperty * dp, void * oldValue, const void * newValue, void * coercedValue, unsigned char priority, Noesis::Expression * newExpression, const Noesis::PropertyMetadata * metadata, Noesis::Value::Destination destination, bool isBaseComponent) Line 797 C++
MyExe.exe!Noesis::ValueStorageManagerImpl<Noesis::Ptr<Noesis::BaseComponent> >::SetValue(Noesis::DependencyObject * dob, const Noesis::DependencyProperty * dp, Noesis::BaseComponent * value, unsigned char priority, Noesis::Expression * expression, const Noesis::PropertyMetadata * metadata, Noesis::Value::Destination destination) Line 234 C++
MyExe.exe!Noesis::ValueStorageManager::SetValueObject(Noesis::DependencyObject * dob, const Noesis::DependencyProperty * dp, Noesis::BaseComponent * value, unsigned char priority, Noesis::Expression * expression, const Noesis::PropertyMetadata * metadata, Noesis::Value::Destination destination) Line 40 C++
MyExe.exe!Noesis::DependencyProperty::SetValueObject(Noesis::DependencyObject * obj, Noesis::BaseComponent * value, unsigned char priority, Noesis::Expression * expression, const Noesis::PropertyMetadata * metadata, Noesis::Value::Destination destination) Line 210 C++
MyExe.exe!Noesis::DependencyObject::InternalSetExpression(const Noesis::DependencyProperty * dp, Noesis::Expression * newExpression, unsigned char priority) Line 599 C++
MyExe.exe!Noesis::DependencyObject::InternalInvalidateProperty(const Noesis::DependencyProperty * dp, unsigned char priority) Line 981 C++
MyExe.exe!Noesis::DependencyObject::InvalidateProperty(const Noesis::DependencyProperty * dp, unsigned char priority) Line 218 C++
MyExe.exe!Noesis::BindingExpression::OnDependencyPropertyChanged(Noesis::BaseComponent * sender, const Noesis::DependencyPropertyChangedEventArgs & args) Line 1747 C++
MyExe.exe!Noesis::Delegate<void __cdecl(Noesis::BaseComponent *,Noesis::DependencyPropertyChangedEventArgs const &)>::MemberFuncStub<Noesis::BindingExpression,void (__cdecl Noesis::BindingExpression::*)(Noesis::BaseComponent *,Noesis::DependencyPropertyChangedEventArgs const &)>::Invoke(Noesis::BaseComponent * <args_0>, const Noesis::DependencyPropertyChangedEventArgs & <args_1>) Line 465 C++
MyExe.exe!Noesis::Delegate<void __cdecl(Noesis::BaseComponent *,Noesis::DependencyPropertyChangedEventArgs const &)>::operator()(Noesis::BaseComponent * <args_0>, const Noesis::DependencyPropertyChangedEventArgs & <args_1>) Line 155 C++
MyExe.exe!Noesis::Delegate<void __cdecl(Noesis::BaseComponent *,Noesis::DependencyPropertyChangedEventArgs const &)>::MultiDelegate::Invoke(Noesis::BaseComponent * <args_0>, const Noesis::DependencyPropertyChangedEventArgs & <args_1>) Line 577 C++
MyExe.exe!Noesis::Delegate<void __cdecl(Noesis::BaseComponent *,Noesis::DependencyPropertyChangedEventArgs const &)>::operator()(Noesis::BaseComponent * <args_0>, const Noesis::DependencyPropertyChangedEventArgs & <args_1>) Line 155 C++
MyExe.exe!Noesis::DependencyObject::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & args) Line 514 C++
MyExe.exe!Noesis::Visual::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & args) Line 766 C++
MyExe.exe!Noesis::UIElement::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & args) Line 1887 C++
MyExe.exe!Noesis::FrameworkElement::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & args) Line 1636 C++
MyExe.exe!Noesis::Control::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & args) Line 442 C++
MyExe.exe!Noesis::ContentControl::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & args) Line 146 C++
MyExe.exe!Noesis::ListBoxItem::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & args_) Line 120 C++
MyExe.exe!Noesis::DependencyObject::NotifyPropertyChanged(const Noesis::DependencyProperty * dp, Noesis::StoredValue * storedValue, const void * oldValue, const void * newValue, bool valueChanged, bool isBaseComponent, const Noesis::PropertyMetadata * metadata) Line 1155 C++
MyExe.exe!Noesis::DependencyObject::InternalSetValue(const Noesis::DependencyProperty * dp, void * oldValue, const void * newValue, void * coercedValue, unsigned char priority, Noesis::Expression * newExpression, const Noesis::PropertyMetadata * metadata, Noesis::Value::Destination destination, bool isBaseComponent) Line 797 C++
MyExe.exe!Noesis::DependencyObject::SetValue_<bool>(Noesis::Int2Type<0> __formal, const Noesis::DependencyProperty * dp, bool value, Noesis::Value::Destination destination) Line 168 C++
MyExe.exe!Noesis::DependencyObject::SetValue<bool>(const Noesis::DependencyProperty * dp, bool value) Line 47 C++
MyExe.exe!Noesis::Selector::SetIsSelected(Noesis::DependencyObject * element, bool value) Line 81 C++
MyExe.exe!Noesis::Selector::OnItemSelected(Noesis::SelectionChangedEventArgs & e, Noesis::BaseComponent * item, int index) Line 1003 C++
MyExe.exe!Noesis::Selector::UpdateSingleSelectedList(Noesis::SelectionChangedEventArgs & e, Noesis::BaseComponent * selectedItem, int selectedIndex) Line 913 C++
>>> MyExe.exe!Noesis::Selector::OnSelectedIndexChanged(int oldIndex, int newIndex) Line 1048 C++
MyExe.exe!Noesis::Selector::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & args) Line 559 C++
MyExe.exe!Noesis::ListBox::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & args) Line 71 C++
MyExe.exe!Noesis::DependencyObject::NotifyPropertyChanged(const Noesis::DependencyProperty * dp, Noesis::StoredValue * storedValue, const void * oldValue, const void * newValue, bool valueChanged, bool isBaseComponent, const Noesis::PropertyMetadata * metadata) Line 1155 C++
MyExe.exe!Noesis::DependencyObject::InternalSetValue(const Noesis::DependencyProperty * dp, void * oldValue, const void * newValue, void * coercedValue, unsigned char priority, Noesis::Expression * newExpression, const Noesis::PropertyMetadata * metadata, Noesis::Value::Destination destination, bool isBaseComponent) Line 797 C++
MyExe.exe!Noesis::DependencyObject::SetValue_<int>(Noesis::Int2Type<0> __formal, const Noesis::DependencyProperty * dp, int value, Noesis::Value::Destination destination) Line 168 C++
MyExe.exe!Noesis::DependencyObject::SetValue<int>(const Noesis::DependencyProperty * dp, int value) Line 47 C++
MyExe.exe!Noesis::Selector::SetSelectedIndex(int index) Line 112 C++
-Steven
-
sfernandez
Site Admin
- Posts: 2983
- Joined:
Re: Focusing ListBoxItem after ItemCollection changed
Firing the animations from the DataTemplate should work fine too. Just with the xaml you attached using DataTriggers listening to the IsSelected property of the ListBoxItem ancestor.Since the content is driven via a DataTemplate, the storyboard has to be defined in the DataTemplate as well
Do you have any problems with that?
I've been thinking, maybe what is happening is that the selected item corresponds to a ListBoxItem that is not visible and virtualized, so the container is not yet generated. If that is the problem you can use ListBox.ScrollIntoView() that will scroll the list to show the item, generating the corresponding container and it will get focused then.I've tried to do precisely this, and it doesn't seem to work
I created a ticket (#1641) in our bugtracker to investigate this.I tried to add the style trigger and it hit an assert
Re: Focusing ListBoxItem after ItemCollection changed
No, this part works great. The animations always trigger when I'd expect.Firing the animations from the DataTemplate should work fine too. Just with the xaml you attached using DataTriggers listening to the IsSelected property of the ListBoxItem ancestor.
Do you have any problems with that?
After a bit more testing, it's only fails if I call SetSelectedIndex prior to the listbox being generated. The reason being is it does indeed call the SetSelectedIndex, but because the element is not yet generated it is unable to focus it. I added this bit of code and it fixed my problem:I've been thinking, maybe what is happening is that the selected item corresponds to a ListBoxItem that is not visible and virtualized, so the container is not yet generated. If that is the problem you can use ListBox.ScrollIntoView() that will scroll the list to show the item, generating the corresponding container and it will get focused then.
Code: Select all
/*virtual*/ void CustomListBox::OnContainersGenerated() /*override*/
{
ParentClass::OnContainersGenerated();
if (GetIsVisible())
{
s32 nDesiredIdx = 0;
if (GetSelectedIndex() != -1)
{
nDesiredIdx = GetSelectedIndex();
}
ItemCollection *pItemContainer = GetItems();
if (nullptr != pItemContainer)
{
if (nDesiredIdx >= 0 && nDesiredIdx < pItemContainer->Count())
{
BaseComponent *pComponent = pItemContainer->GetComponent(nDesiredIdx).GetPtr();
if (nullptr != pComponent)
{
UIElement *pElement = IsItemItsOwnContainerOverride(pComponent) ?
static_cast<UIElement*>(pComponent) :
DynamicCast<UIElement*>(GetItemContainerGenerator()->ContainerFromItem(pComponent));
if (nullptr != pElement)
{
if (!pElement->GetIsFocused())
{
pElement->Focus();
}
}
}
}
}
}
}
Much appreciated! Thank youI created a ticket (#1641) in our bugtracker to investigate this.
-Steven
Who is online
Users browsing this forum: Ahrefs [Bot], Bing [Bot], Google [Bot] and 19 guests