2017-10-04 41 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

我編輯了我的答案 –