View Issue Details
| ID | Project | Category | View Status | Date Submitted | Last Update |
|---|---|---|---|---|---|
| 0001639 | NoesisGUI | C++ SDK | public | 2020-03-14 18:18 | 2020-03-20 16:38 |
| Reporter | steveh | Assigned To | sfernandez | ||
| Priority | normal | Severity | block | ||
| Status | resolved | Resolution | fixed | ||
| Product Version | 2.2.6 | ||||
| Target Version | 3.0.0 | Fixed in Version | 3.0.0 | ||
| Summary | 0001639: Assert when connecting element to a different view | ||||
| Description | Hi guys, I'm trying to update to 2.2.6 but some of our code is asserting. Bear with me, this will take a bit of explaining... I've created a new control derived from ContentControl called "ExplicitlyControlledElement". The purpose of this control is to allocate a new view and move the children of it's element out of the main view and into a newly allocated view which can be controlled by a different controller. I did it this way to simplify screens which may be controlled by multiple people. This means that we can do something like this in the main XAML: <usercontrol> At runtime, in the OnInitialised delegate of the UniquelyControlledElement, we allocate a new view and move the content of the children into the root of the new view. Something like this: I've attached our actual function to this bug report called "UniquelyControlledElement_CreateView.cpp". Hopefully you should get the general idea of what I'm trying to do. Note: There is a also an additional control called a "DummyPositionalObject". This gets inserted into the hierarchy so if we animate the position of the UniquelyControlledElement in the main view we move the view as well, so the UI artist can still move the position of these views in animations. This all used to work fine in 2.2.1. However, I've just updated to 2.2.6 and I'm getting an assert when this gets called here: void Visual::OnConnectToView(IView* view)
} So this looks like something has already moved the view across. I've added a bunch of debugging and I think I've tracked it down, so here is what I believe is happening: The element at fault is a control with a dynamic template applied, specifically it looks like this (with some slight name adjustments): <Control x:Name="Icon_Locked" HorizontalAlignment="Center" Height="67.333" VerticalAlignment="Center" Width="67.333" Template="{DynamicResource Icons_PopUp_Locked}" Foreground="#FF7C7C7C" Margin="47,14,10,40" Focusable="False"/> The Icons_PopUp_Locked is in a different database and I've attached a copy of what this looks like (with the path data removed to simplify the problem). The issue comes from the DynamicResource...
phew we made it :) Now.. I'm not sure on the best fix here. Perhaps it could be fixed by the same way you prevent infinite loops in the measure by adding a visual flag like "InReparenting", and before you do any logic in Visual::ConnectToView you call something like CheckVisualFlag(!InReparenting), e.g. maybe something like this: void Visual::ConnectToView(IView* view) But I'm not sure if this'll bust anything during the framework application as I'm not sure what side effect will be caused by not calling OnConnectToView when applying the template mid reparenting. This issue is preventing me from integrating 2.2.6 so I'd like to get a fix for this pretty shortly if at all possible, so any suggestions would be great. Cheers, -Steven | ||||
| Steps To Reproduce |
| ||||
| Attached Files | ResourceDatabase.xaml (723 bytes)
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ControlTemplate x:Key="Icons_PopUp_Locked">
<Viewbox>
<Canvas Name="Document" Width="97.8112" Height="119.217" Clip="... SVG Data ...">
<Canvas Name="Layer_1" Width="799.908" Height="600.104" Canvas.Left="0" Canvas.Top="0">
<Path Data="... SVG Data ..." Stretch="Fill" Fill="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Control}}, Path=Foreground}" Name="Path" Width="84.5637" Height="106.094" Canvas.Left="6.5808" Canvas.Top="6.54251" />
</Canvas>
</Canvas>
</Viewbox>
</ControlTemplate>
</ResourceDictionary> UniquelyControlledElement_CreateUniqueView.cpp (2,106 bytes)
void UniquelyControlledElement::CreateUniqueView(BaseComponent* pContent)
{
DestroyView();
m_pContent = Ptr<FrameworkElement>(DynamicCast<FrameworkElement*>(pContent));
if (nullptr == m_pContent)
{
S2_LOG(UI, "Failed to create a view for the content passed to a UniquelyControlledElement. No view will be allocated. Name=%s, Content Type=%s",
GetName(), nullptr != pContent ? pContent->GetClassType()->GetName() : "[Content Address Invalid]");
return;
}
SetContent(static_cast<BaseComponent*>(nullptr));
SlApplication *pApplication = SlApplication::Current();
if (nullptr != pApplication)
{
SlWindow *pWindow = pApplication->GetMainWindow();
if (nullptr != pWindow)
{
//
f32 fWidth = pWindow->GetActualWidth();
f32 fHeight = pWindow->GetActualHeight();
// Start collapsed, we'll make visible when either visibility changes or when the elements loads.
m_pContent->SetVisibility(Visibility_Hidden);
m_pContent->SetHorizontalAlignment(GetHorizontalAlignment());
m_pContent->SetVerticalAlignment(GetVerticalAlignment());
m_pRootNode = MakePtr<Viewbox>();
m_pRootNode->SetStretch(Stretch_Fill);
m_pRootNode->SetDataContext(GetDataContext());
m_uViewIndex = pWindow->RegisterView(m_pRootNode);
BoxedValue *pfUIWidth = TryFindResource<BoxedValue>("UIWidth");
BoxedValue *pfUIHeight = TryFindResource<BoxedValue>("UIHeight");
Ptr<Border> pBorder = MakePtr<Border>();
pBorder->SetWidth(nullptr != pfUIWidth ? *static_cast<const f32*>(pfUIWidth->GetValuePtr()) : 3840.0f);
pBorder->SetHeight(nullptr != pfUIHeight ? *static_cast<const f32*>(pfUIHeight->GetValuePtr()) : 2160.0f);
m_pRootNode->SetChild(pBorder);
pBorder->SetChild(m_pContent);
pWindow->InitView(m_uViewIndex, static_cast<s32>(fWidth), static_cast<s32>(fHeight), 1, true);
SlRenderer::AddRenderTaskStatic(&SelfClass::RenderThread_InitRendererForView, *(u32**)(&m_uViewIndex));
}
}
SetContent(MakePtr<DummyPositionalObject>()); // Set the dummy positional object
} second_callstack.txt (833 bytes)
This is the second callstack of where the same child element tries to reparent the view. However we assert because this was already set in the first callstack. > Game_PcDx11_x64_Debug.exe!Noesis::Visual::OnConnectToView(Noesis::IView * view) Line 660 C++ Game_PcDx11_x64_Debug.exe!Noesis::UIElement::OnConnectToView(Noesis::IView * view) Line 2309 C++ Game_PcDx11_x64_Debug.exe!Noesis::FrameworkElement::OnConnectToView(Noesis::IView * view) Line 1955 C++ Game_PcDx11_x64_Debug.exe!Noesis::Visual::ConnectToView(Noesis::IView * view) Line 191 C++ Game_PcDx11_x64_Debug.exe!Noesis::Visual::OnConnectToViewChildren() Line 718 C++ Game_PcDx11_x64_Debug.exe!Noesis::FrameworkElement::OnConnectToViewChildren() Line 1993 C++ Game_PcDx11_x64_Debug.exe!Noesis::Visual::ConnectToView(Noesis::IView * view) Line 192 C++ first_callstack..txt (3,879 bytes)
This is the first callstack of where the child elements have the root set to the parent's new root (the new view). Game_PcDx11_x64_Debug.exe!Noesis::Visual::OnConnectToView(Noesis::IView * view) Line 653 C++ Game_PcDx11_x64_Debug.exe!Noesis::UIElement::OnConnectToView(Noesis::IView * view) Line 2309 C++ Game_PcDx11_x64_Debug.exe!Noesis::FrameworkElement::OnConnectToView(Noesis::IView * view) Line 1955 C++ Game_PcDx11_x64_Debug.exe!Noesis::Visual::ConnectToView(Noesis::IView * view) Line 191 C++ Game_PcDx11_x64_Debug.exe!Noesis::Visual::AddVisualChild(Noesis::Visual * child) Line 280 C++ Game_PcDx11_x64_Debug.exe!Noesis::FrameworkElement::SetSingleVisualChild(Noesis::Visual * child) Line 1441 C++ Game_PcDx11_x64_Debug.exe!Noesis::FrameworkElement::ApplyFrameworkTemplate(Noesis::FrameworkTemplate * frameworkTemplate) Line 727 C++ Game_PcDx11_x64_Debug.exe!Noesis::Control::OnPropertyChanged(const Noesis::DependencyPropertyChangedEventArgs & args) Line 452 C++ Game_PcDx11_x64_Debug.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 1188 C++ Game_PcDx11_x64_Debug.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 830 C++ Game_PcDx11_x64_Debug.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++ Game_PcDx11_x64_Debug.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++ Game_PcDx11_x64_Debug.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++ Game_PcDx11_x64_Debug.exe!Noesis::DependencyObject::InternalSetExpression(const Noesis::DependencyProperty * dp, Noesis::Expression * newExpression, unsigned char priority) Line 632 C++ Game_PcDx11_x64_Debug.exe!Noesis::DependencyObject::InternalInvalidateProperty(const Noesis::DependencyProperty * dp, unsigned char priority) Line 1046 C++ Game_PcDx11_x64_Debug.exe!Noesis::DependencyObject::InvalidateProperty(const Noesis::DependencyProperty * dp, unsigned char priority) Line 236 C++ Game_PcDx11_x64_Debug.exe!Noesis::DynamicResourceExpression::OnAncestorChanged(Noesis::FrameworkElement * __formal) Line 228 C++ Game_PcDx11_x64_Debug.exe!Noesis::Delegate<void __cdecl(Noesis::FrameworkElement *)>::MemberFuncStub<Noesis::DynamicResourceExpression,void (__cdecl Noesis::DynamicResourceExpression::*)(Noesis::FrameworkElement *)>::Invoke(Noesis::FrameworkElement * <args_0>) Line 465 C++ Game_PcDx11_x64_Debug.exe!Noesis::Delegate<void __cdecl(Noesis::FrameworkElement *)>::operator()(Noesis::FrameworkElement * <args_0>) Line 155 C++ Game_PcDx11_x64_Debug.exe!Noesis::FrameworkElement::OnConnectToView(Noesis::IView * view) Line 1966 C++ Game_PcDx11_x64_Debug.exe!Noesis::Visual::ConnectToView(Noesis::IView * view) Line 191 C++ | ||||
| Platform | Any | ||||
|
I realised I was being silly in my suggestion, I don't need to CheckFlag on the current visual, I need to check the parent's flag. After realising this, I changed the function to look like this (it's not nice, don't judge :) ) void Visual::ConnectToView(IView* view) The lines with #SPH_TO_REMOVE were the ones I added. This did actually work for me, and I've managed to load my XAML elements and everything still seems to work upon initial inspection. I'm working remotely right now so I can't actually check to see if the second controller can control the other view, but the primary user can still control the first view. I can't see any major issues so far, but I've not actually ran through the entire game yet. I'll give it more testing. I'm sure you guys will know if this is a silly thing to do or not though. Cheers, -Steven |
|
|
Hi Steve, we are looking into this as it seems something we accidentally introduced in 2.2.6 and is still occurring in 3.0 version. Thank you so much for the detailed report, it will for sure help a lot. |
|
|
Hi Steve, I fixed this in branch 2.2 revision 8936, could you please give it a try and confirm? |
|
|
Hi there, yeah, that's seemed to have done the trick. I've reverted my hack and replaced it with the submitted changelist and it seems to work great. Cheers! |
|
| Date Modified | Username | Field | Change |
|---|---|---|---|
| 2020-03-14 18:18 | steveh | New Issue | |
| 2020-03-14 18:18 | steveh | File Added: ResourceDatabase.xaml | |
| 2020-03-14 18:18 | steveh | File Added: UniquelyControlledElement_CreateUniqueView.cpp | |
| 2020-03-14 18:18 | steveh | File Added: second_callstack.txt | |
| 2020-03-14 18:18 | steveh | File Added: first_callstack..txt | |
| 2020-03-14 18:34 | steveh | Note Added: 0006147 | |
| 2020-03-17 12:35 | sfernandez | Assigned To | => sfernandez |
| 2020-03-17 12:35 | sfernandez | Status | new => assigned |
| 2020-03-17 12:35 | sfernandez | Target Version | => 3.0.0 |
| 2020-03-17 12:35 | sfernandez | Description Updated | |
| 2020-03-17 12:38 | sfernandez | Status | assigned => feedback |
| 2020-03-17 12:38 | sfernandez | Note Added: 0006150 | |
| 2020-03-17 12:38 | sfernandez | Status | feedback => assigned |
| 2020-03-18 19:30 | sfernandez | Status | assigned => feedback |
| 2020-03-18 19:30 | sfernandez | Note Added: 0006151 | |
| 2020-03-18 21:57 | steveh | Note Added: 0006158 | |
| 2020-03-18 21:57 | steveh | Status | feedback => assigned |
| 2020-03-20 16:38 | sfernandez | Status | assigned => resolved |
| 2020-03-20 16:38 | sfernandez | Resolution | open => fixed |
| 2020-03-20 16:38 | sfernandez | Fixed in Version | => 3.0.0 |