0

Ломаю голову над проблемой часов 6, надеюсь тут помогут. Имеется WPF приложение на .Net 8. В главном окне есть обычно текстовое поле с биндингом на нужно свойство в DataContext:

 <Grid>
    <TextBlock FontSize="22" Text="{Binding Text, UpdateSourceTrigger=PropertyChanged}" Height="50" Width="100"/>
</Grid>

Сам дата контекст окна задается в code-behind:

using CryptoAnalys.ViewModels;
using Infrastructure;
using Infrastructure.Abstractions;
using MediatR;
using Microsoft.Extensions.DependencyInjection;
using System.Windows;
using UseCases;

namespace CryptoAnalys { public partial class MainWindow : Window { public MainWindow() { InitializeComponent();

    var config = new MediatRServiceConfiguration()
    {
        AutoRegisterRequestProcessors = true,
    };

    config.RegisterServicesFromAssembly(typeof(GetIsApiExistsQuery).Assembly);

    var services = new ServiceCollection()
        .AddSingleton&lt;IGrpcApiService, GrpcApiService&gt;()
        .AddMediatR(config)
        .RegisterGrpc()
        .BuildServiceProvider();

    var sender = services.GetRequiredService&lt;IMediator&gt;();

    DataContext = new MainViewModel(sender);
}

} }

И, собственно, сам ViewModel:

namespace CryptoAnalys.ViewModels
{
 public class MainViewModel : ViewModelBase
 {
    string _text;
public MainViewModel(IMediator sender) : base(sender)
{
    Task.Run(async () =&gt;
    {
        await Task.Delay(10);

        Application.Current.Dispatcher.Invoke(() =&gt;
        {
           Text = &quot;123&quot;;
        });
    });
}

public string Text
{
    get =&gt; _text;
    set
    {
        if (_text == value)
            return;

        _text = value;
        OnPropertyChanged();
    }
} 

} }

В общем, всё по классике, но тут начинается сама настоящая магия. По абсолютно непонятной причине Application.Current.Dispatcher.Invoke не выводит на экран текст '123'. Притом если поменять код:

  Task.Run(async () =>
        {
            await Task.Delay(10);
        Application.Current.Dispatcher.Invoke(() =&gt;
        {
           Text = &quot;123&quot;;
        });
    });

на

Task.Run(async () =>
     {
         Text = "123";
     });

Всё будет работать корректно. Если убрать

Task.Run(async () => ...

И оставить только конструктор с инициализацией:

public MainViewModel(IMediator sender) : base(sender)
{
    Text = "123";
}

Всё также будет работать как следует. Самое интересное: если поместить код в диспетчер, но уже без таска, '123' снова не показывается:

public MainViewModel(IMediator sender) : base(sender)
{
    Application.Current.Dispatcher.Invoke(() =>
    {
       Text = "123";
    });
}

Кто-нибудь сталкивался с такой проблемой?

Upd 1: Код ViewModelBase:

using MediatR;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace CryptoAnalys.ViewModels { public class ViewModelBase { public ViewModelBase(ISender sender) { Sender = sender; }

public ViewModelBase()
{

}

public ISender Sender { get; }

public event PropertyChangedEventHandler PropertyChanged;

protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
    PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}

} }

Что примечательно, создал на форме кнопку с командой в том же ViewModel:

<Grid>
  <Button Command="{Binding LoadedCommand}" Height="50" Width="100" VerticalAlignment="Top"></Button>
  <TextBlock FontSize="22" Text="{Binding Text}" Height="50" Width="100"/>
</Grid>

Код Vm поменялся на следующий:

public class MainViewModel : ViewModelBase
{
  string _text;

public MainViewModel() : base() { LoadedCommand = new RelayCommand<object>(Loaded);
}

public RelayCommand<object> LoadedCommand { get; }

public string Text { get => _text; set { if (_text == value) return;

    _text = value;
    OnPropertyChanged();
}

}

private void Loaded(object obj) { Text = "123"; } }

Loaded выполняется всё синхронно и даже в таком случае изменение текста не происходит( На всякий случай класс команды:

public class RelayCommand<T> : ICommand
{
  private readonly Predicate<T> _canExecute;
  private readonly Action<T> _execute;

public RelayCommand(Action<T> execute) : this(execute, null) { _execute = execute; }

public RelayCommand(Action<T> execute, Predicate<T> canExecute) { if (execute == null) { throw new ArgumentNullException("execute"); } _execute = execute; _canExecute = canExecute; }

public bool CanExecute(object parameter) { return _canExecute == null || _canExecute((T)parameter); }

public void Execute(object parameter) { _execute((T)parameter); }

public event EventHandler CanExecuteChanged { add { CommandManager.RequerySuggested += value; } remove { CommandManager.RequerySuggested -= value; } } }

Vlad
  • 21

1 Answers1

0

Проблема найдена: Базовый класс ViewModelBase не реализует интерфейс INotifyPropertyChanged

Vlad
  • 21
  • 1
    Application.Current.Dispatcher.Invoke вам и не нужен для отображения данных во view так как вы работаете с vm. То есть вы не взаимодействуете с ui и выходить из потока в главный вам не зачем. Удалите эту чать кода, она вам не нужна. – xellan Jan 12 '24 at 15:09