为什么 Window.Visibility 属性 不是 "getted"?

Why Window.Visibility property is not being "getted"?

使用 MVVM,我有一个 MainView.xaml 和一个对应的 MainViewModel.cs。我还有一个用户控件,HomeView.xaml,以及 HomeViewModel.cs.

此外,我有一个 TaskBarService,单例,被注入 MainViewModel.csHomeViewModel.csMainView.Window.Visibility 通过 属性.

绑定到 TaskBarService.WindowVisibility

HomeView 上,当用户单击按钮时,TaskBarService.WindowVisibility 设置为 Visibility.Hidden,同时为相同的 属性 调用 OnPropertyChanged。但是 MainView.Window.Visibility 从不寻找新值。我在 属性 setter 上设置了一个断点,它只在实例化 MainView 时触发一次。我做错了什么?

App.xaml.cs

public partial class App : Application
{
    private readonly IHost _host;
    public App()
    {
        _host = CreateHostBuilder().Build();
    }

    private static IHostBuilder CreateHostBuilder(string[] args = null)
    {
        return Host.CreateDefaultBuilder(args)
            .AddServices()
            .AddStores()
            .AddViewModels()
            .AddViews();
    }

    protected override void OnStartup(StartupEventArgs e)
    {
        _host.Start();


        Window janela = _host.Services.GetRequiredService<MainView>();
        TaskBarController taskBarController = _host.Services.GetRequiredService<TaskBarController>();
        taskBarController.WindowVisibility = Visibility.Visible;
        base.OnStartup(e);
    }

    protected override async void OnExit(ExitEventArgs e)
    {
        await _host.StopAsync();
        _host.Dispose();

        base.OnExit(e);
    }
}

App.xaml

<Application x:Class="<mynamespace>.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:local="clr-namespace:<mynamespace>"
         xmlns:viewmodels="clr-namespace:<mynamespace>.ViewModels"
         xmlns:views="clr-namespace:<mynamespace>.Views">
<Application.Resources>
    <DataTemplate DataType="{x:Type viewmodels:HomeViewModel}">
        <views:HomeView/>
    </DataTemplate>
    <Style TargetType="{x:Type Rectangle}"/>

</Application.Resources>
</Application>

添加服务

public static class AddServicesHostBuilderExtensions
{
    public static IHostBuilder AddServices(this IHostBuilder host)
    {
        host.ConfigureServices((context, services) =>
        {
           services.AddSingleton<TaskBarController>();

        });
        return host;
    }
}

添加商店

 public static class AddStoresHostBuilderExtensions
{
    public static IHostBuilder AddStores(this IHostBuilder host)
    {
        host.ConfigureServices((context, services) =>
        {
            services.AddSingleton<INavigator, Navigator>();
        });

        return host;
    }
}

AddViewModels

public static class AddViewModelsHostBuilderExtensions
{
    public static IHostBuilder AddViewModels(this IHostBuilder host)
    {
        host.ConfigureServices((context, services) =>
        {
            services.AddSingleton<IViewModelFactory, ViewModelFactory>();

            services.AddSingleton<HomeViewModel>();
            services.AddSingleton<MainViewModel>();


            services.AddSingleton<CreateViewModel<HomeViewModel>>(services => () => services.GetRequiredService<HomeViewModel>());
        });
        return host;
    }
}

添加视图

public static class AddViewsHostBuilderExtensions
{
    public static IHostBuilder AddViews(this IHostBuilder host)
    {
        host.ConfigureServices((context, services) => {
            services.AddSingleton<MainView>(createMainView);
        });
        return host;
    }
    private static MainView createMainView(IServiceProvider services)
    {
        return new MainView(services.GetRequiredService<MainViewModel>());
    }
}

ViewModelBase

public delegate TViewModel CreateViewModel<TViewModel>() where TViewModel : ViewModelBase;
public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

}

MainView.xaml

<Window x:Class="<mynamespace>.Views.MainView"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

    Title="MainView" WindowStartupLocation="CenterScreen" ResizeMode="NoResize" WindowStyle="None"
    Height="250" Width="400" Visibility="{Binding WindowVisibility, Mode=TwoWay}">
<Grid>
    <ContentControl Content="{Binding ViewModelAtual}"/>
</Grid>
</Window>

MainView.xaml.cs

public partial class MainView : Window
{
    public MainView(object dataContext)
    {
        InitializeComponent();
        DataContext = dataContext;
    }
}

HomeView.xaml

<UserControl x:Class="<mynamespace>.Views.HomeView"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
         xmlns:d="http://schemas.microsoft.com/expression/blend/2008">
<Grid>
    <TextBlock Text="❌">
        <TextBlock.InputBindings>
            <MouseBinding Gesture="LeftClick" Command="{Binding CloseWindow}"/>
        </TextBlock.InputBindings>
    </TextBlock>
</Grid>
</UserControl>

Navigator.cs

public class Navigator : INavigator
{
    private ViewModelBase _viewModelAtual;

    public ViewModelBase ViewModelAtual
    {
        get { return _viewModelAtual; }
        set
        {
            _viewModelAtual = value;
            StateChanged?.Invoke();
        }
    }

    public event Action StateChanged;
}

ViewModelFactory.cs

public class ViewModelFactory : IViewModelFactory
{
    private readonly CreateViewModel<HomeViewModel> _createHomeViewModelFactory;

    public ViewModelFactory(CreateViewModel<HomeViewModel> createHomeViewModelFactory)
    {
        _createHomeViewModelFactory = createHomeViewModelFactory;
    }

    public ViewModelBase CreateViewModel(TipoView tipoView)
    {
        switch (tipoView)
        {
            case TipoView.Home:
                return _createHomeViewModelFactory();
            default:
                throw new ArgumentException("The ViewType does not have a ViewModel.", "viewType");
        }
    }
}

任务栏控制器

public class TaskBarController : INotifyPropertyChanged
{
    private Visibility windowVisibility;

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
    public Visibility WindowVisibility
    {
        get => windowVisibility;
        set
        {
            windowVisibility = value;
            OnPropertyChanged(nameof(WindowVisibility));
        }
    }

    public void MinimizeToTray()
    {
        WindowVisibility = Visibility.Hidden;
    }

    public void MaximizeFromTray()
    {
        WindowVisibility = Visibility.Visible;
    }
}

UpdateViewModelCommand.cs

public class UpdateViewModelAtualCommand : ICommand
{
    private readonly INavigator _navigator;
    private readonly IViewModelFactory _viewModelFactory;

    public UpdateViewModelAtualCommand(INavigator navigator, IViewModelFactory viewModelFactory)
    {
        _navigator = navigator;
        _viewModelFactory = viewModelFactory;
    }

    public event EventHandler CanExecuteChanged;

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
        if (parameter is TipoView)
        {
            TipoView tipoView = (TipoView)parameter;

            _navigator.ViewModelAtual = _viewModelFactory.CreateViewModel(tipoView);
        }
    }
}

MainViewModel.cs

private readonly IViewModelFactory _viewModelFactory;
        private readonly INavigator _navigator;
        private TaskBarController _taskBarController;

        public ViewModelBase ViewModelAtual => _navigator.ViewModelAtual;
        public MainViewModel(INavigator navigator,
            IViewModelFactory viewModelFactory, TaskBarController taskBarController)
        {
            _taskBarController = taskBarController;
            _navigator = navigator;
            _viewModelFactory = viewModelFactory;

            _navigator.StateChanged += _navigator_StateChanged;

            UpdateViewModelAtual = new UpdateViewModelAtualCommand(navigator, _viewModelFactory);
            UpdateViewModelAtual.Execute(TipoView.Home);

        }

        private void _navigator_StateChanged()
        {
            OnPropertyChanged(nameof(ViewModelAtual));
        }

        public ICommand UpdateViewModelAtual { get; set; }
        public Visibility WindowVisibility
        {
            get => _taskBarController.WindowVisibility;
            set
            {
                _taskBarController.WindowVisibility = value;
                OnPropertyChanged(nameof(WindowVisibility));
            }
        }

曾几何时,我想出了Window.Visibility绑定,但主题是俄语,所以我不在这里给出link。
底线是 Window.Visibility 上的任何其他操作都会破坏 属性.
中的绑定 因此,如何创建和显示 MainWondow 非常关键。
如果你在 StartupUri 中指定它,或者对其应用 Window.Show() 方法,或者直接指定 Visibility = ... 值,绑定将被破坏。

示例:

public class FlashingViewModel : BaseInpc
{
    private Visibility _windowVisibility;

    public Visibility WindowVisibility { get => _windowVisibility; set => Set(ref _windowVisibility, value); }

    private readonly Timer timer = new Timer();

    public FlashingViewModel()
    {
        timer.Elapsed += (s, e) =>
            WindowVisibility = 
                WindowVisibility == Visibility.Visible
                ? Visibility.Hidden
                : Visibility.Visible;
        timer.Interval = 1000;
        timer.Start();
    }
}
<Window x:Class="***.MainWindow"
        ------------
        -----------
        Visibility="{Binding WindowVisibility}"
      >
    <Window.DataContext>
        <local:FlashingViewModel/>
    </Window.DataContext>
<Application x:Class="***.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             Startup="OnStartup">
    <!--StartupUri="MainWindow.xaml">-->
    <Application.Resources>
         
    </Application.Resources>
</Application>
public partial class App : Application
{
    private void OnStartup(object sender, StartupEventArgs e)
    {
        MainWindow = new MainWindow();
    }
}

BaseInpc 是我对 INotifyPropertyChanged 接口的实现。