【发布时间】:2021-07-14 22:15:36
【问题描述】:
使用 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 从不寻找新值。我在属性设置器上设置了一个断点,它只在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));
}
}
【问题讨论】:
-
您有两个这样的属性。您设置了一个,但绑定了另一个。
-
@Andy 我的错,我发布的代码中有一个额外的属性。
TaskBarVisibility是另一回事,与当前情况无关。我已经编辑了删除未使用属性的帖子。 -
HomeViewModel和CloseWindowCommand是同一个东西吗...? -
好吧,这个例子太乱了,无法“修复”。我应该删除这个问题并重新开始一个新的更好的例子吗?
-
@ASousa:是的,请提供一个最小可重现的样本。
标签: c# wpf mvvm data-binding