2017-10-04 46 views
-2

我是wpf和xaml的新手,尝试在WindowsApplication(Xaml,WPF)中更改窗口的内容(登录 - >主内容和主内容 - >登录)。到目前为止,我已经在这个简单的登录/注销情况如下:通过ValueConverter更改MainWindow内容

  1. BaseViewModel

    public class BaseViewModel : DependencyObject, INotifyPropertyChanged 
    { 
        public event PropertyChangedEventHandler PropertyChanged; 
    
        public virtual void OnPropertyChanged(string propertyName) 
        { 
         PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 
        } 
    } 
    
  2. BaseMainViewViewModel(在主窗口设置MainViewType产业基地类它还包含了命令来改变。从BaseMainViewViewModel通过在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 inherites得到交流塞斯的CurrentMainView,物业

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

    public class MainViewModel : BaseMainViewViewModel {} 
    
  5. MainWindowViewModel

    public class MainWindowViewModel: BaseMainViewViewModel {} 
    
  6. LoginMainView

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

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

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

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

    同样在这里(退出键式)对应于LoginMainView ...

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

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

    在MainWindow-查看我将CurrentMainView-Property(MainViewType)从BaseMainViewViewModel绑定到contentpresenter,我将通过单击MainMainView/LoginMainView中的按钮进行更改,然后使用ValueConverter进行更改。

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

    public enum MainViewType 
    { 
        Login = 0, 
        Main = 1 
    } 
    
  10. 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); 
    
    } 
    
  11. 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

您不需要ValueConverter。你正处于一个正确的轨道上。看一看here - 这是ReactiveUI框架的示例应用程序(这是我最喜欢的)。

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

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完成所有magick。不要单独做这件事,它会再次发明轮子。

编辑回答评论:

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

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

您可以用另一种方式做到这一点:查看第一个导航。有一些WPF页面导航教程。它的工作原理完全相同,但不是ViewModel,而是创建一个页面(视图),然后创建底层数据。因为它允许非常干净的逻辑和表示分离(XAML仅关于视图,ViewModel包含所有逻辑,模型持久化数据),这反过来又使得在平台之间共享逻辑变得非常容易。事实上,如果你做得对,你可以在Xamarin,WPF或UWP编写的应用程序中使用所有ViewModel,只需创建特定于平台的视图即可。

为了结束,WPF允许您切换属性数据,它会自动(通过DataTemplates)为它查找一个视图。记住INotifyPropertyChanged和一切都会工作

+0

好吧,首先感谢您的答案。但要更好地学习和理解WPF和XAML,我想重新发明轮子。 ;-)那么我需要做什么才能以上述方式实现它?我还缺少什么?顺便说一句。我在哪里放'AppBootstraper.ActiveViewModel = new LoginViewModel()'?我是否必须为此创建两个命令,将它们绑定到相应的按钮? – srcalex

+0

我编辑了我的答案 –