Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Two questions regarding porting WPF code to WinUI: Window Datacontext and Application.Current.Dispatcher #2687

Closed
Wigmund opened this issue Jun 16, 2020 · 15 comments
Labels
needs-triage Issue needs to be triaged by the area owners product-winui3 WinUI 3 issues question

Comments

@Wigmund
Copy link

Wigmund commented Jun 16, 2020

Using .Net 5 preview 4 and WinUI 3.0 preview, I'm experimenting porting WPF code and have encountered two questions:

  1. What has happened to the DataContext property for the MainWindow xaml - are viewmodels binded differently?
  2. What is the equivalent of initiating tasks on the Application.Current.Dispatcher GUI thread?
@msft-github-bot msft-github-bot added the needs-triage Issue needs to be triaged by the area owners label Jun 16, 2020
@StephenLPeters StephenLPeters added product-winui3 WinUI 3 issues and removed needs-triage Issue needs to be triaged by the area owners labels Jun 16, 2020
@StephenLPeters
Copy link
Contributor

@MikeHillberg
Copy link
Contributor

Window in WinUI isn't a FrameworkElement like it is in WPF, and so doesn't inherit the DataContext property. So you need to set the DataContext on the root element.

It could potentially be added. But DataContext isn't used in WinUI as often as it is in WPF, because WinUI has x:Bind, which doesn't need it. (WinUI does still have Binding though.)

@tomasfabian
Copy link

@Wigmund

  1. I was also wondering what happened with the DataContext in the MainWindow
  2. please visit also this question Dispatchers in WinUI3. There are multiple options how to use the Dispatcher in WINUI 3 Preview 1

@Wigmund
Copy link
Author

Wigmund commented Jun 18, 2020

OK thanks for pointers, will look at converting to x:Bind as that seems to be the preferred option.
However, my existing code uses a lot of User Controls which simply content bind to ViewModels:
<ContentControl Content="{Binding ControlViewModel}" />

which in turn refers to my MainWindow resources to load the related view:
<DataTemplate DataType="{x:Type vms:ControlViewModel}"> <local:ControlView /> </DataTemplate>

Is this still possible or is there also a different/better approach?
Was hoping for minimal changes ;)

@tomasfabian
Copy link

tomasfabian commented Jun 18, 2020

@Wigmund I did the exact same think. I converted a WPF app to WinUI Desktop app Preview.
Here are my findings hopefully they will spare you a lot of time and you will remain happy :)

  1. ObservableCollections does not raise notifications to the UI, or at least the UI is not refreshed.
  2. {Binding}s are in my opinion not working correctly
  3. be aware that the default mode for x:Bind is OneTime!

In case you would like to switch to x:Bind you can start with a dependency property for your view model:

{x:Bind ViewModel.Property}
public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(nameof(ViewModel),
          typeof(MainViewModel),
          typeof(MainPage),
          new PropertyMetadata(null));

        public MainViewModel ViewModel
        {
          get => GetValue(ViewModelProperty) as MainViewModel;
          set => SetValue(ViewModelProperty, value);
        }

This still works:

<DataTemplate x:DataType="viewModels:ProductViewModel">

with x:Binds inside the template the bindings are actually compile/design time safe. This was not present in WPF

@Wigmund
Copy link
Author

Wigmund commented Jun 18, 2020

Thanks for the heads up.
It appears that <Window.Resources> isn't a thing either :/ I guess because its not a proper FrameworkElement

@Wigmund
Copy link
Author

Wigmund commented Jun 18, 2020

Compilation fails with a "property not found" when using

<DataTemplate x:Name="vStartPageView" x:DataType="vms:StartPageViewModel">

in the Application resource dictionary - is this expected?
Compiles fine without the DataType property...

@abritabroad
Copy link

@Wigmund I did the exact same think. I converted a WPF app to WinUI Desktop app Preview.
Here are my findings hopefully they will spare you a lot of time and you will remain happy :)

  1. ObservableCollections does not raise notifications to the UI, or at least the UI is not refreshed.
  2. {Binding}s are in my opinion not working correctly
  3. be aware that the default mode for x:Bind is OneTime!

In case you would like to switch to x:Bind you can start with a dependency property for your view model:

{x:Bind ViewModel.Property}
public static readonly DependencyProperty ViewModelProperty = DependencyProperty.Register(nameof(ViewModel),
          typeof(MainViewModel),
          typeof(MainPage),
          new PropertyMetadata(null));

        public MainViewModel ViewModel
        {
          get => GetValue(ViewModelProperty) as MainViewModel;
          set => SetValue(ViewModelProperty, value);
        }

This still works:

<DataTemplate x:DataType="viewModels:ProductViewModel">

with x:Binds inside the template the bindings are actually compile/design time safe. This was not present in WPF

I see in your sample you are using a UserControl to bind the ViewModel to. I guess this is because Window is not a DependencyObject (FrameworkElement) so you can't create a dependency property on it.

I was trying to create a MainViewModel for the MainWindow itself. I guess that isn't possible.

Steve

@tomasfabian
Copy link

tomasfabian commented Jun 22, 2020

I see in your sample you are using a UserControl to bind the ViewModel to. I guess this is because Window is not a DependencyObject (FrameworkElement) so you can't create a dependency property on it.

@abritabroad yes that's why I ended with a UserControl. I originally wanted to use PRISM, but it's not available for WinUI3. Currently it's only for WinUI 2. So I also wanted to start with (Shell(MainWindow).xaml/ShellViewModel(MainViewModel)) pair.

It seems that I will have to use conditional compilation in my shared project in some cases, since there are at least three different namespaces (WPF, WinUI2/UWP, WinUI3) :(

using System.Windows.Controls;
using Windows.UI.Xaml;
using Microsoft.UI.Xaml;
    protected override void InitializeShell(UIElement /*Window*/ shell)
    {
      shell.DataContext = shellViewModel;

      base.InitializeShell(shell);
    }

@tomasfabian
Copy link

OK thanks for pointers, will look at converting to x:Bind as that seems to be the preferred option.
However, my existing code uses a lot of User Controls which simply content bind to ViewModels:

which in turn refers to my MainWindow resources to load the related view:
<local:ControlView />

Is this still possible or is there also a different/better approach?
Was hoping for minimal changes ;)

@Wigmund I tried it in a UWP app and it didn't work there neither, so this is probably expected. I moved the datatemplate inside a separate resource dictionary and it worked.

I was also hoping for minimal changes and even sharing of XAML, but different namespace declarations and so on won't enable this (WPF vs WinUI3):

xmlns:views="clr-namespace:Joker.Sample.Views"

xmlns:views="using:Joker.Sample.Views"

AFAIK it's not possible to use conditional compilation in a XAML file.

@MikeHillberg
Copy link
Contributor

Note that with x:Bind the source property doesn't need to be a DependencyProperty, it can be any C# property.

To keep the markup/code the same, can you set the DataContext on the Window's root panel? That prevents the Window itself from binding to the data context, but works the same for anything below the panel.

@abritabroad
Copy link

abritabroad commented Jul 3, 2020

Note that with x:Bind the source property doesn't need to be a DependencyProperty, it can be any C# property.

oh, that's good to know! My brain still thinks in WPF. Been on the web side for several years now.

@jtbrower
Copy link

jtbrower commented Jul 26, 2020

When trying to figure out why I was getting the following compilation error, Google brought me here.

The XAML Binary Format (XBF) generator reported syntax error '0x09C4' : Property Not Found

As a WPF developer, the above error just consumed a couple hours of my day, in part because I allowed myself to become frustrated. I thought I would share the solution so that other developers who are led here by Google can find the answer.

The problem occurs as @Wigmund stated, when you are using a DataType on a DataTemplate.

<DataTemplate x:Key="TitleBarVmDataTemplate" x:DataType="vm:TitleBarVm">
    <customControls:TitleBar Background="{StaticResource TitleBarColor}" />
</DataTemplate>

Although I tried to move the template all over the app, the final attempt was to keep it in a resource dictionary named DataTemplates.xaml. To stop the compilation error, I needed to add a class definition to the DataTemplates.xaml file and then create a partial class DataTemplates.xaml.cs to match.

Even though I am still getting use to the UWP way of XAML vs WPF, I was aware that sometimes you had to have a backing '.cs' file for the XAML. However, I thought that you only needed that when using the 'x:Bind' markup extension. Apparently I was wrong because the above syntax won't even work in a UWP application.

Update - using x:Bind in a Resource Dictionary

I further learned that when you include a dictionary that has been defined as described above it is Important that you include it like the following. If you don't, InitializeComponent will not be called and x:Bind will not work! This is an easy mistake to make.

<Application
    x:Class="WinUI.DemoApp.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:templates="using:WinUI.CustomControls.Templates">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />

                <!-- 
                Please note that when including a dictionary that has a code behind file you MUST
                include the dictionary as if you were defining an instance of it directly.  This
                can take a couple days off your life if you make this mistake.  
                -->

                <!-- NOTE THAT THE FOLLOWING LINE IS CORRECT -->
                <templates:DataTemplates />
                
                <!-- NOTE THAT THE FOLLOWING LINE IS WRONG -->
                <!-- <ResourceDictionary Source="ms-appx:///WinUI.CustomControls/Templates/DataTemplates.xaml" /> -->
                
            </ResourceDictionary.MergedDictionaries>

        </ResourceDictionary>
    </Application.Resources>
</Application>

I will never make this mistake again! :) Hopefully this helps somebody.

@krschau
Copy link
Contributor

krschau commented Jul 28, 2021

Closing this issue. Please @ me to reopen if you feel the questions here haven't been answered.

@LukaszBanach-droid
Copy link

LukaszBanach-droid commented Jul 9, 2024

Compilation fails with a "property not found" when using

<DataTemplate x:Name="vStartPageView" x:DataType="vms:StartPageViewModel">

in the Application resource dictionary - is this expected? Compiles fine without the DataType property...

I know it has been a while but using x:name on a WPF DataTemplate causes memory leak. Is this still the case in WinUI3?
Sincerely WPF Developer

@microsoft-github-policy-service microsoft-github-policy-service bot added the needs-triage Issue needs to be triaged by the area owners label Jul 9, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs-triage Issue needs to be triaged by the area owners product-winui3 WinUI 3 issues question
Projects
None yet
Development

No branches or pull requests

9 participants