我是wpf和xaml的新手,并尝试在WindowsApplication(Xaml,WPF)中更改窗口的内容(登录 - >主要内容和主要内容 - >登录)。到目前为止,我有以下这个简单的登录/注销方案:
BaseViewModel
public class BaseViewModel : DependencyObject, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
BaseMainViewViewModel(用于在MainWindow中设置MainViewType属性的基类。它还包含通过MainViews中的按钮更改属性的命令。)
public class BaseMainViewViewModel : BaseViewModel
{
private static MainViewType _CurrentMainView;
private ICommand _SwitchMainViewCommand;
public BaseMainViewViewModel()
{
SwitchMainViewCommand = new RelayCommand(SwitchMainView);
}
public MainViewType CurrentMainView
{
get { return _CurrentMainView; }
set
{
if (value != _CurrentMainView)
{
_CurrentMainView = value;
OnPropertyChanged(nameof(CurrentMainView));
}
}
}
public ICommand SwitchMainViewCommand
{
get { return _SwitchMainViewCommand; }
set { _SwitchMainViewCommand = value; }
}
#region Test
public void SwitchMainView(object param)
{
Debugger.Break();
switch (CurrentMainView)
{
case MainViewType.Login:
CurrentMainView = MainViewType.Main;
break;
case MainViewType.Main:
CurrentMainView = MainViewType.Login;
break;
default:
break;
}
MessageBox.Show("Login/Logout");
}
#endregion Test
LoginViewModel从BaseMainViewViewModel继承以访问CurrentMainView-Property
public class LoginViewModel : BaseMainViewViewModel {}
MainViewModel她一样
public class MainViewModel : BaseMainViewViewModel {}
MainWindowViewModel
public class MainWindowViewModel: BaseMainViewViewModel {}
LoginMainView
public partial class LoginMainView : UserControl
{
public LoginMainView()
{
InitializeComponent();
DataContext = new LoginViewModel();
}
}
目前我在LoginMainView中只有一个按钮(Login-Button)。如果单击此按钮,则应与MainMainView交换当前的LoginMainView。
<Grid>
<Button Content="Main" Background="Red" Command="{Binding SwitchMainViewCommand}" />
</Grid>
MainMainView
public partial class MainMainView : UserControl
{
public LoginMainView()
{
InitializeComponent();
DataContext = new MainViewModel();
}
}
这里相同(Logout-Button)对应LoginMainView ...
<Grid>
<Button Content="Logout" Background="Green" Command="{Binding SwitchMainViewCommand}" />
</Grid>
主窗口
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
}
在MainWindow-View中,我将CurrentMainView-Property(MainViewType)从BaseMainViewViewModel绑定到contentpresenter,我将通过单击MainMainView / LoginMainView中的按钮进行更改,而ValueConverter shold将完成剩下的工作。
<Grid>
<StackPanel>
<Label Content="Test" />
<ContentPresenter Content="{Binding CurrentMainView, Converter={view:MainViewValueConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</Grid>
MainViewType
public enum MainViewType
{
Login = 0,
Main = 1
}
BaseValueConverter
public abstract class BaseValueConverter<T> : MarkupExtension, IValueConverter
where T : class, new()
{
private static T _Converter = null;
public override object ProvideValue(IServiceProvider serviceProvider)
{
return _Converter ?? (_Converter = new T());
}
public abstract object Convert(object value, Type targetType, object parameter, CultureInfo culture);
public abstract object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture);
}
RelayCommand
public class RelayCommand : ICommand
{
private Action<object> _Execute;
private Predicate<object> _CanExecute;
private event EventHandler CanExecuteChangedInternal;
public RelayCommand(Action<object> execute) : this(execute, DefaultCanExecute) { }
public RelayCommand(Action<object> execute, Predicate<object> canExecute)
{
_Execute = execute ?? throw new ArgumentNullException("execute");
_CanExecute = canExecute ?? throw new ArgumentNullException("canExecute");
}
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
CanExecuteChangedInternal += value;
}
remove
{
CommandManager.RequerySuggested -= value;
CanExecuteChangedInternal -= value;
}
}
public bool CanExecute(object parameter)
{
return (_CanExecute != null) && _CanExecute(parameter);
}
public void Execute(object parameter)
{
_Execute(parameter);
}
public void OnCanExecuteChanged()
{
EventHandler eventHandler = CanExecuteChangedInternal;
if (eventHandler != null)
{
eventHandler.Invoke(this, EventArgs.Empty);
}
}
public void Destroy()
{
_CanExecute = _ => false;
_Execute = _ => { return; };
}
private static bool DefaultCanExecute(object parameter)
{
return true;
}
}
当我启动应用程序时,将调用ValueConverter并加载正确的View(LoginMainView)。然后我单击LoginMainView中的按钮,执行命令(SwitchMainView),但是由于未使用ValueConverter,MainWindow的内容不会更改为MainMainView。
我做错了什么?我有基本的理解问题吗?或者以这种方式不可能映射简单的登录/注销场景?或者我只是忽略了什么?有人可以告诉我我忘记了什么吗?
非常感谢帮助者!
答案 0 :(得分:0)
你不需要ValueConverter。你正走在正确的轨道上。看看here - 这是ReactiveUI框架的示例应用程序(这是我最喜欢的)。
它有AppBootrsapper(应用程序的ViewModel)。由于框架围绕它做了一些魔术,基本的想法是:
MainWindow.Xaml:
<Window x:Class="ReactiveUI.Samples.Routing.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:rx="clr-namespace:ReactiveUI;assembly=ReactiveUI"
Title="MainWindow" Height="350" Width="525">
<Grid UseLayoutRounding="True" >
<ContentControl Content="{Binding ActiveViewModel}">
<ContentControl.ContentTemplate>
<DataTemplate DataType="{x:Type LoginViewModel}">
<!-- here you put your content wof login screen, prefereably as seperate UserControl -->
</DataTemplate>
<DataTemplate DataType="{x:Type MainViewModel}">
<!-- here you put your content main screen, prefereably as seperate UserControl -->
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>
</Window>
然后你只需设置AppBootstrapper.ActiveViewModel = new LoginViewModel()
即可登录屏幕。
如果您登录,AppBootstrapper.ActiveViewModel = new MainViewModel()
和WPF会显示主屏幕。
ReactiveUI框架完成所有这些以及更多工作 - 只有在没有为ViewModels放置DataTemplates的情况下,您将UserControls注册为视图并且RoutedViewHost执行所有的魔法。不要自己这样做,而是再次发明轮子。
编辑回答评论:
您将AppBootstrapper.ActiveViewModel = new MainViewModel()
放入NavigationService。导航意味着改变显示视图的东西。最常见的版本是堆栈,其中top是活动的ViewModel。按“返回”按钮时,只需弹出堆栈即可。
这一切都适用于使用Model First导航的MVVM模型,这意味着您首先实例化ViewModel,导航服务找到正确的视图。
您可以通过其他方式执行此操作:查看第一个导航。有一些WPF页面导航教程。它的工作方式完全相同,但您可以创建一个页面(视图)而不是ViewModel,然后创建基础数据。
MVVM app模型如此受欢迎,因为它允许非常干净的逻辑和表示分离(XAML仅关于视图,ViewModels包含所有逻辑,模型保持数据),这反过来使得在平台之间共享逻辑变得非常容易。事实上,如果你这样做,你可以使用Xamarin,WPF或UWP编写的应用程序中的所有ViewModel,只需创建特定于平台的视图。要结束,WPF允许您切换属性数据,它会自动找到一个视图(通过DataTemplates)。记住关于INotifyPropertyChanged并且一切都会正常工作