为什么 Window.Visibility 属性 不是 "getted"?
Why Window.Visibility property is not being "getted"?
使用 MVVM,我有一个 MainView.xaml
和一个对应的 MainViewModel.cs
。我还有一个用户控件,HomeView.xaml
,以及 HomeViewModel.cs
.
此外,我有一个 TaskBarService
,单例,被注入 MainViewModel.cs
和 HomeViewModel.cs
。 MainView.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 接口的实现。
使用 MVVM,我有一个 MainView.xaml
和一个对应的 MainViewModel.cs
。我还有一个用户控件,HomeView.xaml
,以及 HomeViewModel.cs
.
此外,我有一个 TaskBarService
,单例,被注入 MainViewModel.cs
和 HomeViewModel.cs
。 MainView.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 接口的实现。