Reputation: 87
x:Bind defaults to OneTime, which updates the target UI with the data when the Page's Loading event triggers the generated code's Initialize function.
I have a Page with a ViewModel property. This ViewModel class implements INPC for its properties. The data for the viewModel is loaded asynchronously, only after the page is loaded. So on Page initialization, and subsequently the generated code initialization, the UI target using x:Bind will have null data.
Since it is OneTime, it shouldn't change unless I manually call Update(which I don't).
So why does my x:Bind UI work?
The following is some simplified code snippets.
<Page x:Name="MyPage" x:Class="MyProject.Pages.MyPage">
<Button Command="{x:Bind ViewModel.GoToAnotherPageCommand}">
public sealed partial class MyPage : Page
{
public MyPageViewModel ViewModel { get; set; }
public MyPage()
{
this.InitializeComponent();
}
// called by an event bound to a Frame's Navigated, which all pages use
public void OnNavigatedTo()
{
this.ViewModel = new MyPageViewModel();
}
}
public class MyPageViewModel : INotifyPropertyChanged, INotifyPropertyChanging
{
// GoToAnotherPageCommand is an INPC property and its set in the constructor
Upvotes: 1
Views: 445
Reputation: 3904
The compiled binding system (x:Bind
) is smart enough to check for initial null values and not consider them the actual value you wish to bind. It will wait for the first non-null value and bind that value.
This is by design, as binding to an initial null value is (almost) never the intention of the binding.
I didn't find the source of this information, but I believe it was in the Build talk detailing the x:Bind system in 2015.
Updated:
As Justin mentions in the comments below and in his own answer, the binding will not work if the view model is set after the binding operation happens.
I believe this is because the binding terminates when it encounter a null reference in the property chain, but I haven't tested this, so I might be incorrect.
Upvotes: 1
Reputation: 39006
The reason that your command works fine is because OnNavigatedTo
will be called before the command instantiation. This means by the time the code tries to set the command, the ViewModel
has already been instantiated and is no longer null
.
To prove my point, first go open the file under the following path(could be ARM or *x64 depending on which platform you are running on) -
obj/x86/Debug/MyPage.g.cs
This is basically the code-generated file that hooks up all the x:Bind
stuff for your page.
Now put a breakpoint at where the command is set. In my case, it's a method called Set_Windows_UI_Xaml_Controls_Primitives_ButtonBase_Command
. Then put another breakpoint at OnNavigatedTo
.
Now run the app, you will see that the OnNavigatedTo
method gets called first.
If your page's NavigationCacheMode
is set to Disabled
, this behavior makes OnNavigatedTo
the ideal place to instantiate x:Bind
bindings so the page only uses memory to create these new objects when the user actually navigates to it, instead of doing everything inside the page constructor.
Don't do this inside the Loaded
event of the Page
though. Because it will get called after the command instantiation. You can try the following code to instantiate the ViewModel
, and the result is very different(your command will not work).
public MyPage()
{
InitializeComponent();
Loaded += (s, e) => ViewModel = new MyPageViewModel();
}
Upvotes: 2