1

I am currently working on a Login-Window, which should be responsible for establishing a connection to a server, verify user credentials and then open a MVVM 'MainWindow'. I want to speed up the opening process of my MainWindow a little, which is why I am trying to transfer the Initialization of its ViewModel in an own Task. This somehow broke my Commands and I cannot understand why, so I created a minimal reproducible example to explain my problems.

The ViewModel looks like this:

public class MainWindowViewModel
  {

    public MainWindowViewModel()
    {
        

    }

    public ICommand ClickCommand { get; set; }

    public void CreateCommands()
    {
      ClickCommand = new RelayCommand(ExecuteClick);
    }


    public void ExecuteClick()
    {

    }

The View is just a default window with a button that has a command binding: Command="{Binding ClickCommand}" I create view and viewmodel and call CreateCommands from within a Task, which works.

  MainWindow mw = new MainWindow();

  MainWindowViewModel vm = new MainWindowViewModel();
  mw.DataContext = vm;
   
  Task.Run(() =>
  {
   vm.CreateCommands();

  });
  mw.ShowDialog();

now I create a new Method Init() in my View and have it call CreateCommands(). As soon as I call Init() instead of CreateCommands() in my Task as seen below. ExecuteClick() will not be called when I press the Button.

public void Init()
{
   ((MainWindowViewModel)DataContext).CreateCommands();
}

--

  Task.Run(() =>
  {
   mw.Init();

  });

Since calling mw.Init() without the Task works, I suspected some problem with the WPF Dispatcher, so I tried using App.Current.Dispatcher.BeginInvoke(new Action(() =>mw.Init())); and Invoke, but that didn't change anything. I also tried to replicate this behavior when placing the Init() Mehtod in other UI elements, but calling it from another window like this would work again:

public Window1()
{
  InitializeComponent();

  MainWindow mw = new MainWindow();

  MainWindowViewModel vm = new MainWindowViewModel();
  mw.DataContext = vm;
   
  Task.Run(() =>
  {
   Init(vm);

  });
  mw.ShowDialog();


}

public void Init(MainWindowViewModel vm)
{
  vm.CreateCommands();
}

I also tried differences between Core and Framework, or saving my ViewModel in a Member Variable instead of using DataContext, without any notable difference.

What exactly happens, that blocks my commands from being created only when creating them in a thread via the view?

3
  • 2
    Asynchronous initialization of an ICommand property would not make the command asynchronous. You may want to take a look at this: stackoverflow.com/q/54232156/1136211 Commented Nov 12, 2020 at 13:53
  • Showdialog is blocking so i wonder if it blocks your task. You also didn't implement inpc and notify when your command property is set so that command will never be picked up. Commented Nov 12, 2020 at 19:22
  • Asynchronous Programming. For examples of async command implementation look for AsyncRelayCommand class examples. The issue is old as world. Commented Nov 13, 2020 at 22:42

2 Answers 2

0

Profile before optimizing

The issue is that the main window is slow to open. Have you profiled and concluded it is the command creation that is the major bottleneck? If not, start with that.

Wpf is single threaded

WPF (and most other UI framework) are fundamentally single threaded. This means any UI object may only be accessed from the UI thread. It is still possible to do some initialization in the background, but anything that causes the UI to be updated must be done on the UI thread.

If there is some slow background work the typical approach is to do the slow thing on a background thread, and when this has completed do the UI updates on the UI thread. This can be done rather easily using async/await.

There is no notification when the viewmodel is updated

The view model should implement INotifyPropertyChanged and any change should raise the PropertyChanged event. This is not done in your example. This may be why using a task does not work for you, since the view-bindings might not be updated.

Sign up to request clarification or add additional context in comments.

Comments

0

I created a minimal reproducible example to explain my problems

It would help if you would share that minimal, reproducible example. The code you posted doesn't meet that definition.

That said, if the code you posted is truly indicative of the actual reproducible example, then I would say that the problem is that you haven't implemented INotifyPropertyChanged in the MainWindowViewModel object. I mean, there are other problems with the code too, but that seems to be the one that would directly lead to the issue you're seeing.

Specifically: you are initializing the ClickCommand property asynchronously. But by the time that property value has been set, it's almost certain that the view (which one hopes is declared in XAML, but you don't show it, so it's impossible to know for sure) has been completely initialized and the bindings already populated.

So wherever you have bound that property, the target of the binding has received the uninitialized null value for it, at the time the binding was initialized. And since you don't implement INotifyPropertyChanged, when the value of the property is actually finally initialized later, the binding doesn't have any way to know to update the target.

If that does not answer your question, please improve the question so that it actually does include a minimal, reproducible example.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.