Search code examples
xamlmvvmdata-bindinguwpwinui-3

Binding not updating WinUI 3


I'm using WinUI in combination with the microsoft MVVM toolkit. However im experiencing some issues with Binding and can't figure out where the problem lies.

The ViewModel and models used within the ViewModel are of type observableObject. The Command is fired, and the data is fetched. However the binding is not showing a result in the UI, unless i change the xaml and hot reload the change.

My page:

<Page
x:Class="ThrustmasterGuide.Pages.WheelBasePage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ThrustmasterGuide.Pages"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:model="using:ThrustmasterGuide.DataAccess.Context.Model"
xmlns:wheelbase="using:ThrustmasterGuide.ViewModel.Wheelbase"
xmlns:xaml="using:ABI.Microsoft.UI.Xaml"
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
xmlns:core="using:Microsoft.Xaml.Interactions.Core"
xmlns:interactivity="using:Microsoft.Xaml.Interactivity"
xmlns:converters="using:ThrustmasterGuide.Converters"
xmlns:wheelBase="using:ThrustmasterGuide.Model.WheelBase"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance wheelbase:WheelBaseViewModel, IsDesignTimeCreatable=True}">
<Page.Resources>
    <converters:BoolToVisibilityConverter x:Key="BoolToVisibilityConverter" />
    <converters:InvertBoolToVisibilityConverter x:Key="InvertBoolToVisibilityConverter" />
</Page.Resources>
<interactivity:Interaction.Behaviors>
    <core:EventTriggerBehavior EventName="Loaded">
        <core:EventTriggerBehavior.Actions>
            <core:InvokeCommandAction Command="{x:Bind ViewModel.LoadWheelBaseCommand}" />
        </core:EventTriggerBehavior.Actions>
    </core:EventTriggerBehavior>
</interactivity:Interaction.Behaviors>

<StackPanel Padding="16 16 16 16" Orientation="Vertical">
    <StackPanel>
        <TextBlock FontSize="18" Text="{x:Bind ViewModel.WheelBase.Name}" />
        <TextBlock FontSize="18" Text="Symptomen:" />
        <TextBlock Text="Kies hieronder een symptoom uit om te starten." />
        <ProgressRing IsActive="true"
                      Visibility="{x:Bind ViewModel.LoadWheelBaseCommand.IsRunning, Converter={StaticResource BoolToVisibilityConverter}}" />
        <TreeView ItemsSource="{x:Bind ViewModel.WheelBase.Symptoms, Mode=OneWay}">
            <TreeView.ItemTemplate>
                <DataTemplate x:DataType="wheelBase:SymptomModel">
                    <TreeViewItem ItemsSource="{x:Bind Children}" Content="{x:Bind Description}" />
                </DataTemplate>
            </TreeView.ItemTemplate>
        </TreeView>
    </StackPanel>
    <Button VerticalAlignment="Bottom" Command="{x:Bind ViewModel.LoadWheelBaseCommand}"
            Content="Refresh">
    </Button>
</StackPanel>

My ViewModel:

    public class WheelBaseViewModel : ObservableRecipient
{
    public WheelBaseModel WheelBase { get; set; }

    public string WheelBaseName { get; set; }

    private readonly WheelBaseService _wheelBaseService;

    public IAsyncRelayCommand LoadWheelBaseCommand { get; }

    public WheelBaseViewModel(WheelBaseService wheelBaseService)
    {
        _wheelBaseService = wheelBaseService;
        LoadWheelBaseCommand = new AsyncRelayCommand(FetchWheelBase);
    }

    public async Task FetchWheelBase()
    {
        WheelBase = await _wheelBaseService.GetWheelBase(WheelBaseName);
    }
}

My model:

namespace ThrustmasterGuide.Model.WheelBase
{
public class WheelBaseModel : ObservableObject
{
    public string Name { get; set; }

    public ObservableCollection<SymptomModel> Symptoms { get; set; }
}

}

My Code behind:

    public sealed partial class WheelBasePage : Page
{
    public WheelBasePage()
    {
        this.InitializeComponent();
        this.DataContext = App.Current.Services.GetService<WheelBaseViewModel>();
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        base.OnNavigatedTo(e);
        this.ViewModel.WheelBaseName = e.Parameter as string;
    }

    public WheelBaseViewModel ViewModel => (WheelBaseViewModel)DataContext;
}

What is it that i missed to make the UI bind to the WheelBaseModel values?

Update I added mode=OneWay, but still not updating.

Should it be noted that im showing pages within a content frame after navigation?


Solution

  • I believe you need to use SetProperty so it's known when to raise such events?

    https://learn.microsoft.com/en-us/windows/communitytoolkit/mvvm/observableobject

    namespace ThrustmasterGuide.Model.WheelBase
    {
    public class WheelBaseModel : ObservableObject
    {
        public string Name
        {
            get => name;
            set => SetProperty(ref name, value);
        }
    
        public ObservableCollection<SymptomModel> Symptoms { get; set; }
    }
    

    and also bind mode = OneWay

    <TextBlock FontSize="18" Text="{x:Bind ViewModel.WheelBase.Name, Mode=OneWay}" />