繁体   English   中英

通过 ValueConverter 更改 MainWindow 内容

[英]Change MainWindow Content by ValueConverter

我是 wpf 和 xaml 的新手,并尝试更改 Windows 应用程序(Xaml、WPF)中的窗口内容(登录 -> 主内容和主内容 -> 登录)。 到目前为止,对于这个简单的登录/注销场景,我有以下内容:

  1. 基础视图模型

    public class BaseViewModel : DependencyObject, INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; public virtual void OnPropertyChanged(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
  2. 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
  3. LoginViewModel 继承自 BaseMainViewViewModel 以访问 CurrentMainView-Property

     public class LoginViewModel : BaseMainViewViewModel {}
  4. MainViewModel 跟她一样

    public class MainViewModel : BaseMainViewViewModel {}
  5. 主窗口视图模型

    public class MainWindowViewModel: BaseMainViewViewModel {}
  6. 登录主视图

    public partial class LoginMainView : UserControl { public LoginMainView() { InitializeComponent(); DataContext = new LoginViewModel(); } }

    目前我在 LoginMainView 中只有一个按钮(登录按钮)。 如果我点击这个按钮,当前的 LoginMainView 应该与 MainMainView 交换。

     <Grid> <Button Content="Main" Background="Red" Command="{Binding SwitchMainViewCommand}" /> </Grid>
  7. 主视图

    public partial class MainMainView : UserControl { public LoginMainView() { InitializeComponent(); DataContext = new MainViewModel(); } }

    这里相同的 (Logout-Button) 对应于 LoginMainView ...

     <Grid> <Button Content="Logout" Background="Green" Command="{Binding SwitchMainViewCommand}" /> </Grid>
  8. 主窗口

    public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); DataContext = new MainWindowViewModel(); } }

    在 MainWindow-View 中,我将 BaseMainViewViewModel 中的 CurrentMainView-Property (MainViewType) 绑定到 contentpresenter,我将通过单击 MainMainView/LoginMainView 中的按钮进行更改,其余的由 ValueConverter 负责。

     <Grid> <StackPanel> <Label Content="Test" /> <ContentPresenter Content="{Binding CurrentMainView, Converter={view:MainViewValueConverter}, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> </StackPanel> </Grid>
  9. 主视图类型

    public enum MainViewType { Login = 0, Main = 1 }
  10. 基值转换器

    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); }
  11. 中继命令

    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 并加载正确的视图 (LoginMainView)。 然后我点击LoginMainView中的按钮,执行了命令(SwitchMainView),但是由于没有使用ValueConverter,MainWindow的内容并没有变成MainMainView。

我究竟做错了什么? 我有基本的理解问题吗? 还是不可能以这种方式映射简单的登录/注销场景? 还是我只是忽略了什么? 有人可以告诉我我忘记了什么吗?

非常感谢帮助者!

为此,您不需要 ValueConverter。 你在正确的轨道上。 看看这里- 这是 ReactiveUI 框架的示例应用程序(这是我最喜欢的)。

它有 AppBootrsapper(应用程序的 ViewModel)。 由于框架在它周围做了一些魔术,基本思想是:

主窗口.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 框架完成的 - 只有在那里而不是为 ViewModel 放置 DataTemplates,您将 UserControls 注册为视图,而 RoutedViewHost 完成所有魔术。 不要自己做,这又是在发明轮子。

编辑以回答评论:

您将AppBootstrapper.ActiveViewModel = new MainViewModel()放在您的 NavigationService 中。 导航意味着改变显示视图的东西。 最常见的版本是一个堆栈,其顶部是活动的 ViewModel。 当你按下返回按钮时,你只是弹出堆栈。

这一切都适用于 Model First 导航的 MVVM 模型,这意味着您首先实例化 ViewModel,然后导航服务找到合适的视图。

您可以通过另一种方式执行此操作:查看第一个导航。 有一些关于 WPF 页面导航的教程。 它的工作原理完全相同,但不是 ViewModel,而是创建一个页面(一个视图),然后创建基础数据。

MVVM 应用程序模型如此受欢迎,因为它允许非常干净的逻辑和表示分离(XAML 仅与视图有关,ViewModel 包含所有逻辑,模型保留数据),这反过来又使平台之间的逻辑共享变得非常容易。 事实上,如果你正确地做到了这一点,你可以在用 Xamarin、WPF 或 UWP 编写的应用程序中使用你的所有 ViewModel,只需创建特定于平台的视图。

总结一下,WPF 允许您切换属性数据,它会自动为它找到一个视图(通过 DataTemplates)。 记住 INotifyPropertyChanged 一切都会起作用

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM