6

Right. So moving from WPF to UWP, I'm trying to use x:Bind to get compile-time benefits. Simple scenarios work fine; however I have found a number of issues that I was not able to solve. They are all related, so I thought I'd post them in one place:

  1. I haven't been able to make Intellisense work with x:Bind. I have set DataContext (as well as d:DataContext just as we do in WPF) both in XAML and in the constructor, but it won't show members no matter what. Has anyone done this successfully?
  2. Then I read somewhere that in UWP, DataContext is always set to Page's code-behind (really??) and that I need to define a ViewModel type property in the code-behind and then use that property in x:Bind. Is this correct? I tried it and it works but gives rise to the next question.
  3. If I define a property of ViewModel type in Page's code-behind, Any sub-properties that raise PropertyChanged notifications do not update the UI. For example, if the code-behind property is named Game (of type GameVM) and there is a public property in GameVM named Player (of type GamePlayer), and in turn GamePlayer contains a property named Name, the x:Bind path will look like {x:Bind Path=Game.Player.Name}. But if I do this, any change notifications raised from within Name property do not update Page's UI.

One alternate I tried was to listen to PropertyChanged at each level and then bubble it up the hierarchy, but that hasn't worked. Even if it does, doing this seems a bit too much work. In WPF sub-properties like Game.Player.Name work properly without having to doing property change bubbling. Or am I missing something?

dotNET
  • 33,414
  • 24
  • 162
  • 251
  • To your first point: You need always need to build the Project before getting suggestions. I always build the Project and then I'm opening the Property window then I select the Binding source there from a list of possible resources – Matthias Herrmann Sep 23 '16 at 10:27
  • @MatthiasHerrmann: That's not the problem here. I have already built the project several times. Please read my discussion in Monish's answer below. – dotNET Sep 23 '16 at 10:29
  • 4
    The default mode for [`{x:Bind}`](https://msdn.microsoft.com/en-us/windows/uwp/xaml-platform/x-bind-markup-extension) is `OneTime`. The default for `{Binding}` is `OneWay` in most cases. Related Q&A: [With compiled bindings (x:bind), why do I have to call Bindings.Update()?](http://stackoverflow.com/q/33070705/1889329). – IInspectable Sep 23 '16 at 10:32
  • @IInspectable: Ouch. That would wreak havoc. Let me read. – dotNET Sep 23 '16 at 10:40

3 Answers3

14

Right. After playing with it for a few days and searching numerous references, here are my findings:

  1. {x:Bind} lacks design-time support. The feature is on the wishlist though. You may want to upvote it there. (The new version of Visual Studio 15.4.4 does support Intellisense in {x:Bind}in the required way.)
  2. {x:Bind} uses code-behind as its DataContext. So you need to define a public property of your ViewModel type in the code-behind and then use it in your {x:Bind} path.
  3. As pointed out by IInspectable, the default mode for {x:Bind} is OneTime, unlike {Binding} which uses OneWay or TwoWay in almost all cases. So you need to explicitly specify Mode in your binding. People coming from WPF should take special care of it.
  4. Sub-properties that implement notification change work perfectly fine in {x:Bind}. There is no need of bubbling these notifications upwards in the property hierarchy. The problem I was facing (#3 in the question) was because my sub-property was of type List<T>. I changed it to ObservableCollection<T> and it started working.

Hope this works somebody down the road.

Monish Koyott
  • 374
  • 1
  • 4
  • 20
dotNET
  • 33,414
  • 24
  • 162
  • 251
  • I don't get this, why this framework forces someone to put viewmodel property in code behind ? i want my code behind to be clean as i am using mvvm. why why why ? – IronHide Jun 29 '22 at 05:33
  • @IronHide: This may look a bit strange to the people coming from WPF (like me), but in effect, it is very simple to tame it to your need. Just define a property of your ViewModel type in the code-behind and then use 'DataContext` (plus `d:DataContext`) on your root XMAL element (e.g. `Window` or `UserControl`) to point to this property. You'll then be able to set your ViewModel bindings just like you do in WPF. – dotNET Jun 29 '22 at 10:02
1

Well as a beginner, the only question I can answer for you is the first one. Intellisense does not work inside the {x:Bind}. The members are never shown there in UWP for some unknown reasons. As for the next two questions of yours, I am still working on them.

Monish Koyott
  • 374
  • 1
  • 4
  • 20
  • Any official word or other reference on that (Intellisense)? – dotNET Sep 23 '16 at 10:18
  • https://social.msdn.microsoft.com/Forums/windowsapps/en-US/f4cdeeb6-c55a-4916-a885-83b16817fe47/no-intellisense-for-xbind?forum=wpdevelop ..Hope this helps – Monish Koyott Sep 23 '16 at 10:22
  • Part of what you said is not correct. Intellisense works fine with `{Binding}`. I can see my VM's members in XAML when i use `{Binding}` in UWP. Only `{x:Bind}` lacks Intellisense. The link you provided only talks about `x:Bind`. – dotNET Sep 23 '16 at 10:23
  • OK. Looks better now, even though that is not what I wanted to hear :). Anyway I think I can live without Intellisense for now. The other two issues are more important though. – dotNET Sep 23 '16 at 10:26
0

I ran into the same challenge that you have seen. In my experience, in order to create the compile-time binding and have it update with custom objects as properties, the Page class seems to need to know about the data context and custom objects... all you need to do is reference them in the code behind, and then bind to them in the XAML. This creates the code generation objects it needs.

For example, I have a viewmodel, CustomerViewModel that is bound in XAML. That viewmodel also has a property of type IGuest. In order to use the guest object and have it update properly, I came up with this in the code behind...

        CustomerViewModel vm
        {
            get
            {
                return (CustomerViewModel)DataContext;
            }
        }
      IGuest g
        {
            get
            {
                return vm.CurrentGuest;
            }
        }
        public CartGuestControl()
        {
            this.InitializeComponent();
        }

You don't need to assign any of the UI data contexts from the code behind... simply reference the datacontext that is bound in XAML. When binding to any straight viewmodel properties, I use {x:Bind Path=vm.IsEditing, Mode=OneWay}. For binding to any of the guest properties, it looks like this, {x:Bind Path=g.FirstName, Mode=TwoWay}. You could do something like this for your Player object.

I have run into times where x:Bind simply won't do what I expect it to do no matter what I try. This can usually be solved by breaking things out into smaller user controls with more specific data contexts or by using "regular" Binding.

Mark W
  • 1,050
  • 7
  • 15
  • Thanks for sharing the info. If you look closely at what you have suggested, Your `x:Bind` is still using the code-behind class as its `DataContext`. All you're doing is creating top-level properties in the code-behind that act as wrappers for those nested object properties. This is exactly what I finally did (last paragraph of the question), but this hadn't worked for me then. I have now found the root causes of that problem. I'll post my findings after I finish digging. – dotNET Sep 23 '16 at 13:07