[英]How do I subscribe to an event from the ViewModel in the MainWindow?
I'm trying to figure out how to subscribe to an event that's firing in my ViewModel from my MainWindow.xaml.cs.我试图弄清楚如何从我的 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主窗口.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");
}
}
}
It would seem all I need to do is in my NOTIFYING CLASS (ViewModel):看来我需要做的就是在我的 NOTIFYING CLASS (ViewModel) 中:
Implement INotifyPropertyChanged
实施
INotifyPropertyChanged
Add the public event PropertyChangedEventHandler PropertyChanged;
添加
public event PropertyChangedEventHandler PropertyChanged;
Add the OnNotifyPropertyChanged("IsAuthenticatedUser");
添加
OnNotifyPropertyChanged("IsAuthenticatedUser");
Add the following method to my notifying class:在我的通知 class 中添加以下方法:
protected void OnNotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
Debug.WriteLine($"Property Change on LoginView.IsAuthenticatedUser");
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Then in my SUBSCRIBING class (MainViewWindow) add the following:然后在我的订阅 class (MainViewWindow) 添加以下内容:
LoginViewModel.PropertyChanged += UserAuthentication_PropertyChange;
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");
}
}
Unfortunately, this doesn't seem to work.不幸的是,这似乎不起作用。 The subscribed method
UserAuthentication_PropertyChange
is ONLY fired once when the application first starts and there is a change in the value of the IsAuthenticatedUser
Property on the ViewModel.订阅的方法
UserAuthentication_PropertyChange
仅在应用程序首次启动并且 ViewModel 上的IsAuthenticatedUser
属性的值发生更改时触发一次。 Why doesn't this work every time there is a change?为什么每次发生变化时这都不起作用?
For more information if you can help me understand this phenomenon (to me) here's a quick run down of what gets printed to the console as the app runs and I trigger login and logout sequences.有关更多信息,如果您可以帮助我(对我而言)理解这种现象,这里是应用程序运行时打印到控制台的内容的快速运行,我触发登录和注销序列。
Start the app - Currently logged out.启动应用程序 - 当前已注销。
TestAuthentication:LoginViewModel:False
Old value:False
New value:False
Property Change on LoginView.IsAuthenticatedUser
PROPERTY CHANGE REGISTERED:MainWindow:Checking if logged in.
Logged Out
Yay.耶。 It looks like its working... but wait - there's more...
它看起来像它的工作......但是等等 - 还有更多......
Then I log in然后我登录
AuthenticateUser:LoginViewModel:True
Old value:False
New value:True
Property Change on LoginView.IsAuthenticatedUser
The property change is fired but the MainWindow no longer hears it... Why wouldn't it?属性更改被触发,但 MainWindow 不再听到它......为什么不呢?
Right now, it looks like you are operating on two instances of LoginViewModel.现在,看起来您正在对两个 LoginViewModel 实例进行操作。 You only listen to the events of the instance you created in the Loaded handler, but when you login you are using a different maybe XAML instance of LoginViewModel.
您只收听您在 Loaded 处理程序中创建的实例的事件,但是当您登录时,您正在使用不同的可能 XAML LoginViewModel 实例。
To answer your question, how to handle the view models, I suggest to replace the Frame
with a ContentControl
.要回答您的问题,如何处理视图模型,我建议将
Frame
替换为ContentControl
。
This requires that each view has its own view model eg LoginViewModel --> LoginView, HomeViewModel --> HomeView.这要求每个视图都有自己的视图 model 例如 LoginViewModel --> LoginView, HomeViewModel --> HomeView。 Furthermore must be each view defined inside a
DataTemplate
.此外,必须在
DataTemplate
中定义每个视图。
A ContentControl
binds to the SelectedPage
property and renders the page by applying the appropriate DataTemplate
. ContentControl
绑定到SelectedPage
属性并通过应用适当的DataTemplate
呈现页面。
Navigation now takes place inside the view model by setting a SelectedPage
property of the MainWindowViewModel
to the view model of the page you wish to navigate to.通过将
MainWindowViewModel
的SelectedPage
属性设置为您希望导航到的页面的视图 model,导航现在发生在视图 model 内。
This way you can move the navigation to the view model and can comfortably handle events etc, since the view model has knowledge of other view models when using composition (or aggregation).这样,您可以将导航移动到视图 model 并且可以轻松处理事件等,因为视图 model 在使用组合(或聚合)时了解其他视图模型。
PageName.cs页面名称.cs
Enumeration to eliminate magic strings as page identifiers in XAML and C#.枚举以消除 XAML 和 C# 中的页面标识符的魔法字符串。
Can be used as CommandParameter
when used eg with a Button.Command
to navigate to a certain page.可以用作
CommandParameter
,例如与Button.Command
一起使用以导航到某个页面。
enum PageName
{
LoginView, HomeView
}
MainWindowViewModel.cs MainWindowViewModel.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));
}
}
LoginViewModel.cs登录视图模型.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));
}
}
** MainWindow.xaml.cs** ** MainWindow.xaml.cs**
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
}
}
LoginView.xaml LoginView.xaml
<UserControl x:Class="LoginView">
<!--
DataContext is automatically the LoginViewModel,
accessible from code-behind via the DataContext property
-->
<TextBlock Text="{Binding IsAuthenticatedUser}" />
</UserControl>
HomeView.xaml主页查看.xaml
<UserControl x:Class="LoginView">
<!--
DataContext is automatically the HomeViewModel,
accessible from code-behind via the DataContext property
-->
<TextBlock Text="Welcome back user!" />
</UserControl>
MainWindow.xaml主窗口.xaml
<Window>
<Window.Resources>
<DataTemplate DataType="{x:Type LoginViewModel}">
<LoginView />
</DataTemplate>
<DataTemplate DataType="{x:Type HomeViewModel}">
<HomeView />
</DataTemplate>
</Window.Resources>
<StackPanel>
<TextBlock x:Name="Header">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsAuthenticated}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock>
</TextBlock>
<!--
Page host. Setting the content to a page view model,
will automatically load the corresponding DataTemplate
-->
<ContentControl Content="{Binding SelectedPage}" />
<TextBlock x:Name="Footer">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Hidden" />
<Style.Triggers>
<DataTrigger Binding="{Binding IsAuthenticated}" Value="True">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock>
</TextBlock>
</Window>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.