我试图找出如何从MainWindow.xaml.cs订阅在ViewModel中触发的事件。
ViewModel:
public class LoginViewModel : INotifyPropertyChanged
{
private bool isAuthenticatedUser;
public bool IsAuthenticatedUser
{
get { return isAuthenticatedUser; }
set
{
Debug.WriteLine("Old value:" + isAuthenticatedUser);
isAuthenticatedUser = value;
Debug.WriteLine("New value:" + isAuthenticatedUser);
OnNotifyPropertyChanged("IsAuthenticatedUser");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnNotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
Debug.WriteLine($"Property Change on LoginView.IsAuthenticatedUser");
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
MainWindow.xaml.cs
public partial class MainWindow
{
private int? oldUnreadTextsCount = 0;
public MainWindow()
{
DataContext = new MainWindowViewModel();
InitializeComponent();
InitializeTimer();
Loaded += MainWindow_Loaded;
}
public TextsViewModel TextsViewModel { get; set; }
public LoginViewModel LoginViewModel { get; set; }
public MainWindowViewModel MainWindowViewModel { get; set; }
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
LoginViewModel = new LoginViewModel();
// First Subscribe;
LoginViewModel.PropertyChanged += UserAuthentication_PropertyChange;
// Second Fire Change / Fire update on UserAuthentication_PropertyChange.
LoginViewModel.TestAuthentication();
// Third Change Views appropriately
if (LoginViewModel.IsAuthenticatedUser)
{
NavigationFrame.NavigationService.Navigate(new HomeView());
TextsViewModel = new TextsViewModel();
TextsViewModel.PropertyChanged += UnreadTexts_PropertyChanged;
}
else
{
NavigationFrame.NavigationService.Navigate(new LoginView());
}
}
private void UserAuthentication_PropertyChange(object sender, PropertyChangedEventArgs e)
{
Debug.WriteLine("PROPERTY CHANGE REGISTERED:MainWindow:Checking if logged in.");
if (LoginViewModel.IsAuthenticatedUser)
{
Header.Visibility = Visibility.Visible;
Footer.Visibility = Visibility.Visible;
Debug.WriteLine("Logged In");
}
else
{
Header.Visibility = Visibility.Hidden;
Footer.Visibility = Visibility.Hidden;
Debug.WriteLine("Logged Out");
}
}
}
似乎我需要做的只是在NOTIFYING CLASS(ViewModel)中:
实施INotifyPropertyChanged
添加public event PropertyChangedEventHandler PropertyChanged;
添加OnNotifyPropertyChanged("IsAuthenticatedUser");
将以下方法添加到通知类:
protected void OnNotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
Debug.WriteLine($"Property Change on LoginView.IsAuthenticatedUser");
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
然后在我的SUBSCRIBING类(MainViewWindow)中添加以下内容:
LoginViewModel.PropertyChanged += UserAuthentication_PropertyChange;
中订阅事件 private void UserAuthentication_PropertyChange(object sender, PropertyChangedEventArgs e)
{
Debug.WriteLine("PROPERTY CHANGE REGISTERED:MainWindow:Checking if logged in.");
if (LoginViewModel.IsAuthenticatedUser)
{
Header.Visibility = Visibility.Visible;
Footer.Visibility = Visibility.Visible;
Debug.WriteLine("Logged In");
}
else
{
Header.Visibility = Visibility.Hidden;
Footer.Visibility = Visibility.Hidden;
Debug.WriteLine("Logged Out");
}
}
不幸的是,这似乎不起作用。订阅的方法UserAuthentication_PropertyChange
仅在应用程序首次启动且ViewModel的IsAuthenticatedUser
属性的值发生更改时才触发一次。为什么每次更改都不起作用?
有关更多信息(如果可以(对我来说)可以帮助我理解这一现象),请快速浏览一下该应用程序运行时打印到控制台的内容,并触发登录和注销顺序。
启动应用-当前已注销。
TestAuthentication:LoginViewModel:False
Old value:False
New value:False
Property Change on LoginView.IsAuthenticatedUser
PROPERTY CHANGE REGISTERED:MainWindow:Checking if logged in.
Logged Out
是的!看起来像在工作...但是等等-还有更多...
然后我登录
AuthenticateUser:LoginViewModel:True
Old value:False
New value:True
Property Change on LoginView.IsAuthenticatedUser
已触发属性更改,但MainWindow不再听到它了...为什么不呢?
答案 0 :(得分:0)
现在,您似乎正在对LoginViewModel的两个实例进行操作。您只能监听在Loaded处理程序中创建的实例的事件,但是登录时,您正在使用的是LoginViewModel的另一个XAML实例。
要回答您的问题,如何处理视图模型,我建议将Frame
替换为ContentControl
。
这就要求每个视图都有自己的视图模型,例如LoginViewModel-> LoginView,HomeViewModel-> HomeView。此外,必须在DataTemplate
内定义每个视图。
ContentControl
绑定到SelectedPage
属性,并通过应用适当的DataTemplate
呈现页面。
现在可以通过将SelectedPage
的{{1}}属性设置为要导航到的页面的视图模型来在视图模型内进行导航。
通过这种方式,您可以将导航移动到视图模型,并可以舒适地处理事件等,因为在使用合成(或聚合)时,视图模型还具有其他视图模型的知识。
PageName.cs
枚举,以消除魔术字符串作为XAML和C#中的页面标识符。
可以在使用时用作MainWindowViewModel
,例如CommandParameter
导航到特定页面。
Button.Command
MainWindowViewModel.cs
enum PageName
{
LoginView, HomeView
}
LoginViewModel.cs
public class MainWindowViewModel : INotifyPropertyChanged
{
public TextViewModel TextViewModel { get; set; }
private Dictionary<PageName, object> Pages { get; set; }
private object selectedPage;
public object SelectedPage
{
get => this.selectedPage;
set
{
this.selectedPage = value;
OnPropertyChanged();
}
}
private bool isAuthenticated;
public bool IsAuthenticated
{
get => this.isAuthenticated;
set
{
this.isAuthenticated = value;
OnPropertyChanged();
}
}
public MainWindowViewModel()
{
// Alternatively use constructor injection
var loginPageViewModel = new LoginViewModel();
loginPageViewModel.AuthenticationStatusChanged += OnAuthenticationStatusChanged;
this.Pages = new Dictionary<PageName, object>()
{
{ PageName.LoginView, loginPageViewModel },
{ PageName.HomeView, new HomeViewModel() }
}
// Show login screen initially
this.SelectedPage = Pages[PageName.LoginView];
this.TextViewModel = new TextViewModel();
}
// Example ICommand execute action, triggered e.g. on Button.Command,
// where the CommandParameter is a value of the PageName enumeration
private void ExecuteNavigateToPage(object commandParameter)
{
if (commandParameter is PageName pageName
&& this.Pages.TryGetValue(pageName, out object pageViewModel)
{
this.SelectedPage = pageViewModel;
}
}
private void OnAuthenticationStatusChanged(object sender, EventArgs e)
{
this.IsAuthenticated = (sender as LoginViewModel).IsAuthenticatedUser;
// Redirect to main page when user has authenticated successfully
if (this.IsAuthenticated)
{
this.SelectedPage = Pages[PageName.HomeView];
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
** MainWindow.xaml.cs **
public class LoginViewModel : INotifyPropertyChanged
{
public event EventHandler AuthenticationStatusChanged;
public event PropertyChangedEventHandler PropertyChanged;
private bool isAuthenticatedUser;
public bool IsAuthenticatedUser
{
get => this.isAuthenticatedUser;
set
{
this.isAuthenticatedUser = value;
OnPropertyChanged();
OnAuthenticationStatusChanged();
}
}
private void OnAuthenticationStatusChanged()
{
this.AuthenticationStatusChanged?.Invoke(this, EventArgs.Empty);
}
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
LoginView.xaml
public partial class MainWindow
{
private int? oldUnreadTextsCount = 0;
public MainWindow()
{
InitializeComponent();
InitializeTimer();
var mainWindowViewModel = new MainWindowViewModel();
DataContext = mainWindowViewModel;
mainWindowViewModel.TextsViewModel.PropertyChanged += UnreadTexts_PropertyChanged;
Loaded += MainWindow_Loaded;
}
private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
// Navigation (and debugger output) has been moved to the MainWindowViewModel.
// Toggling of the visibility has been moved to XAML using DataTrigger
}
}
HomeView.xaml
<UserControl x:Class="LoginView">
<!--
DataContext is automatically the LoginViewModel,
accessible from code-behind via the DataContext property
-->
<TextBlock Text="{Binding IsAuthenticatedUser}" />
</UserControl>
MainWindow.xaml
<UserControl x:Class="LoginView">
<!--
DataContext is automatically the HomeViewModel,
accessible from code-behind via the DataContext property
-->
<TextBlock Text="Welcome back user!" />
</UserControl>