Page 1 of 2

A data binding question

Posted: 08 Feb 2018, 09:46
by UE4
Hi,
I have 2 UI(View): MyControl.Xaml, MainWindow.Xaml.
<!-- MyUserControl -->
<UserControl>
<Grid>
<Label  Content="{Binding  Title}">
</Grid>
</UserControl>
<!-- MainWindow -->
<UserControl>
<Grid>
<Label  Content="{Binding  MainWindowTitle}">
<local: MyUserControl <!-- How to binding its Title? -->>
</Grid>
</UserControl>
2 ViewModel: MyControlVM(has a string member: Title), MainWindowVM(has a string member: MainWindowTitle),
then how binding child View's DataContext? especially in UE4's blueprint

Re: A data binding question

Posted: 08 Feb 2018, 19:28
by hcpizzi
Hi,

At the moment there's no way to set a DataContext for elements other than the root, via the SetDataContext function. This data context is inherited by all elements.

I was thinking about adding a SetDataContextForElement function to allow having different DataContexts in different elements of the tree, but it's still not implemented. I'll try to get it into the next code release. For now you can add MyControlVM as a property of MainWindowVM, and then change your xaml like this:

<local: MyUserControl DataContext="{Binding MyControlVM}">

I'll let you know once I've implemented SetDataContextForElement.

Re: A data binding question

Posted: 09 Feb 2018, 05:14
by UE4
Hi,

At the moment there's no way to set a DataContext for elements other than the root, via the SetDataContext function. This data context is inherited by all elements.

I was thinking about adding a SetDataContextForElement function to allow having different DataContexts in different elements of the tree, but it's still not implemented. I'll try to get it into the next code release. For now you can add MyControlVM as a property of MainWindowVM, and then change your xaml like this:

<local: MyUserControl DataContext="{Binding MyControlVM}">

I'll let you know once I've implemented SetDataContextForElement.
Thank you.
And I need to hold a ref of UNoesisInstance in ViewModel, then I can bind events to the UNoesisInstance's element, I don't how to do this for MyUserControl. The blueprint function FindName return a BaseComponent(UObject), but I need a NoesisInstance. Or some other class(BlueprintType) will do the same thing?
Maybe in C++, I could hold the View by MyUserControl* MyControlParam = FindName<MyUserControl>("ControlInstanceName"), and set the return value to MyUserControlVM.

Re: A data binding question

Posted: 09 Feb 2018, 18:43
by hcpizzi
Hi,

I've committed a new version to GitHub. I've added a new function called TrySetDataContext. You can use it in combination with FindName. You use FindName to get the element you want, then TrySetDataContext to set the data context. The Try in the name is because it takes an UObject as both parameters, but only works if the Element parameter is indeed a Noesis FrameworkElement.

Please, let us know if this solves your problem.

Re: A data binding question

Posted: 09 Feb 2018, 20:50
by sfernandez
Another option is to provide MyControlVM as nested data of the parent MainWindowVM. That is, you store MyControlVM in a public property ("Content" for example) of the MainWindowVM. Then you can bind MyUserControl DataContext to that property:
<!-- MainWindow -->
  <UserControl>
    <Grid>
      <Label Content="{Binding  MainWindowTitle}">
      <local:MyUserControl DataContext="{Binding Content}"/>
    </Grid>
</UserControl>

Re: A data binding question

Posted: 11 Feb 2018, 03:05
by UE4
Hi
2 methods all works

Is there some way could help me hold a View's ref in ViewModel? the C++ code may do it, but not flexible
void UMainWindowViewModel::InitialzieDataBinding()
{
        //MainWindow can hold a ref to UNoesisInstance(NGUI) easily
	if (VirtualJoystickVM && NGUI && NGUI->Xaml)
	{
		VirtualJoystick* VJoystick = NGUI->Xaml->FindName<VirtualJoystick>(TCHAR_TO_UTF8( *JoystickControlName));
		if (VJoystick)
		{
		        //Let child hold View's ref
			VirtualJoystickVM->SetViewRaw(VJoystick);
		}
	}

	if (TouchButtonVM && NGUI && NGUI->Xaml)
	{
		GameGUI::TouchButton* TouchBtn = NGUI->Xaml->FindName < GameGUI::TouchButton > (TCHAR_TO_UTF8(*TouchButtonName));
		if (TouchBtn)
		{
			TouchButtonVM->SetViewRaw(TouchBtn);
		}

		TouchButtonVM->OnDragStarted.AddDynamic(this, &UMainWindowViewModel::ProcessTouchButtonEvent);
	}
}
for ItemsControl, I don't have a right way to set a ref to View automatically
<ItemsControl ItemsSource="{Binding TouchButtonVMList}">
            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <StackPanel Orientation="Horizontal" <!-- Orientation doesn't work -->>
                        <local:TouchButton >

                        </local:TouchButton>

                    </StackPanel>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>

Re: A data binding question

Posted: 12 Feb 2018, 17:45
by hcpizzi
Yous still have to use C++ for the VM, but what you can do is use FindName in the Blueprint, and have a native function in your VM class that takes an UObject*. In that function you can retrieve the Noesis component, like this:
void VirtualJoystickVM::SetView(UObject* WrappedView)
{
	Noesis::Ptr<Noesis::BaseComponent> Component = NoesisCreateComponentForUObject(WrappedView);
	if (Component)
	{
		VirtualJoystick* VJoystick = NsDynamicCast<VirtualJoystick*>(Component.GetPtr());
		if (VJoystick)
		{
			SetViewRaw(VJoystick);
		}
	}
}
Is this what you had in mind?

Re: A data binding question

Posted: 13 Feb 2018, 16:48
by sfernandez
<ItemsControl ItemsSource="{Binding TouchButtonVMList}">
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <StackPanel Orientation="Horizontal" <!-- Orientation doesn't work -->>
        <local:TouchButton />
      </StackPanel>
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>
About this code, ItemTemplate property defines the appearance of a single item only.
If you want to manage the layout of items in an ItemsControl, you should use the ItemsPanel property:
<ItemsControl ItemsSource="{Binding TouchButtonVMList}">
  <ItemsControl.ItemsPanel>
    <ItemsPanelTemplate>
      <StackPanel Orientation="Horizontal"/>
    </ItemsPanelTemplate>
  </ItemsControl.ItemsPanel>
  <ItemsControl.ItemTemplate>
    <DataTemplate>
      <local:TouchButton />
    </DataTemplate>
  </ItemsControl.ItemTemplate>
</ItemsControl>

Re: A data binding question

Posted: 14 Feb 2018, 10:18
by Wanderer
Maybe late, but try this:
<!-- MainWindow -->
<UserControl>
<Grid>
<Label x:Name="TitleElement"  Content="{Binding MainWindowTitle}">
</Grid>
</UserControl>
<!-- MyUserControl -->
<UserControl>
<Grid>
<Label  Content="{Binding ElementName=TitleElement, Path=Content}">
</Grid>
</UserControl>
if your dataContext is some xaml file, then try this, similar is working on my side (but instead UserControl I use grid).
<!-- MyUserControl -->
<UserControl>
<Grid>
<Label  Content="{Binding ElementName=TitleElement, Path=DataContext.NameOfDataContext[MainWindowTitle]}"> // It depends which element you add DataContext. In this case I think you add DataContext in to label named TitleElement
</Grid>
</UserControl>
Btw. I think your labels missing "/" sign.
<Label x:Name="TitleElement"  Content="{Binding MainWindowTitle}" />

Re: A data binding question

Posted: 14 Feb 2018, 15:34
by UE4
so, I have to call SetView everytime add/remove a array element in DataContext?
the child elements is defined in a template, and initialized at runtime, I don't its name, the FindName may be helpless?