Brad Sherard
Brad Sherard

Reputation: 87

XAML x:bind oneTime on initially null property still works. Why?

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

Answers (2)

Jon G St&#248;dle
Jon G St&#248;dle

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

Justin XL
Justin XL

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

Related Questions