通过ValueConverter更改MainWindow内容

时间:2017-10-04 12:23:38

标签: c# wpf xaml

我是wpf和xaml的新手,并尝试在WindowsApplication(Xaml,WPF)中更改窗口的内容(登录 - >主要内容和主要内容 - >登录)。到目前为止,我有以下这个简单的登录/注销方案:

  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(用于在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. MainWindowViewModel

    public class MainWindowViewModel: BaseMainViewViewModel {}
    
  6. 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>
    
  7. 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>
    
  8. 主窗口

    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>
    
  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;
        }
    }
    
  12. 当我启动应用程序时,将调用ValueConverter并加载正确的View(LoginMainView)。然后我单击LoginMainView中的按钮,执行命令(SwitchMainView),但是由于未使用ValueConverter,MainWindow的内容不会更改为MainMainView。

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

    非常感谢帮助者!

1 个答案:

答案 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并且一切都会正常工作