x:Bind and nested class in a UWP application

2019-08-05 19:44发布

问题:

I am currently working on a UWP application and I would like to use the compiled binding system.

I have a base that extends the Windows.UI.Xaml.Controls.Page that contains a property ViewModel.

Here the base class of the ViewModel Property :

 public class BaseViewModel : INotifyPropertyChanged
 {

    public event PropertyChangedEventHandler PropertyChanged;

    protected void RaiseOnPropertyChanged([CallerMemberName] string propertyName = "")
    {
      OnPropertyChanged(propertyName);
    }

    public void OnPropertyChanged(string propertyName)
    {
      if (PropertyChanged != null)
      {
        PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
      }
    }

  }

And here my base page :

public abstract class BasePage : Page
{

  public BaseViewModel ViewModel { get; set; }

}

The pages of my app extends the BasePage and contain a nester (inner) class that extends the BaseViewModel class. Here a sample code :

public sealed partial class MyPage : BasePage
{

  public sealed class MyViewModel : BaseViewModel
  {

      public string Title
      {
        get
        {
          return "Test";
        }
      }

    }

    public MyPage()
    {
      InitializeComponent();
      ViewModel = MyViewModel();
    }
}

Now, I would like to bind the property Title of the MyViewModel class to my UI. According to this article and this one, something like that should work :

<TextBlock 
  Text="{x:Bind ViewModel.(namespace:MyPage+MyViewModel.Title)}"  
/>

Unfortunately, I cannot compile. I have several errors on the generated file MyPage.g.cs due to the "+" char. Do you know if the binding on a nested (inner) class is supported in UWP application ? Perhaps it is supported only on WPF app ? :(

Thank you for your help !

回答1:

The solution is to use a new property in order to avoid the cast and the nested class access into the XAML file. So the solution is to use something like :

public sealed partial class MainPage : BasePage
{

  public sealed class MyViewModel : BaseViewModel
  {
    public string Title
    {
      get
      {
        return "Test";
      }
    }
  }

  public MyViewModel LocalViewModel
  {
    get
    {
      return (MyViewModel) ViewModel;
    }
  }

  public MainPage()
  {
    this.InitializeComponent();
    ViewModel = new MyViewModel();
  }
}

So, using the x:Bind syntax, the XAML looks like :

<TextBlock Text="{x:Bind LocalViewModel.Title}" />


回答2:

There is some problem while casting with "+" char in {x:Bind}. While using {x:Bind ViewModel.(local:MyPage+MyViewModel.Title)} in MyPage.xaml, it generates code like following in MyPage.g.cs to update bound data:

private void Update_ViewModel(global::UWP.BaseViewModel obj, int phase)
{
    if (obj != null)
    {
        if ((phase & (NOT_PHASED | (1 << 0))) != 0)
        {
            this.Update_ViewModel__local_MyPage+MyViewModel_Title_(((global::UWP.MyPage.MyViewModel)(obj)).Title, phase);
        }
    }
}
private void Update_ViewModel__local_MyPage+MyViewModel_Title_(global::System.String obj, int phase)
{
    if((phase & ((1 << 0) | NOT_PHASED )) != 0)
    {
        XamlBindingSetters.Set_Windows_UI_Xaml_Controls_TextBlock_Text(this.obj2, obj, null);
    }
}

Although the cast generated is right, but the method name Update_ViewModel__local_MyPage+MyViewModel_Title_ is invalid. So casting nested class in {x:Bind} is not supported by now.

If you want to use {x:Bind}, you can use MyViewModel instead of BaseViewModel as following, it will work well.

In the code-behind:

public sealed partial class MyPage : BasePage
{
    public MyViewModel myViewModel;

    public sealed class MyViewModel : BaseViewModel
    {
        public string Title
        {
            get
            {
                return "Test";
            }
        }
    }

    public MyPage()
    {
        InitializeComponent();
        myViewModel = new MyViewModel();
    }
}

In the XAML:

<TextBlock Text="{x:Bind myViewModel.Title}" />

Also, if you want to use BaseViewModel, you can use {Binding} instead of {x:Bind} since {Binding} uses general-purpose runtime object inspection.

In the code-behind:

public sealed partial class MyPage : BasePage
{
    public sealed class MyViewModel : BaseViewModel
    {
        public string Title
        {
            get
            {
                return "Test";
            }
        }
    }

    public MyPage()
    {
        InitializeComponent();
        ViewModel = new MyViewModel();
        this.DataContext = this;
    }
}

In the XAML:

<TextBlock Text="{Binding ViewModel.Title}" />


标签: c# xaml mvvm uwp