2017-10-04 1 views
-2

Ich bin neu in WPF und XAML und versuche, den Inhalt eines Fensters (Login -> Hauptinhalt und Hauptinhalt -> Login) in einer WindowsApplication (Xaml, WPF) zu ändern. Bisher habe ich im Anschluss an die für diese einfache Login/Logout-Szenario:Change MainWindow Inhalt von ValueConverter

  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 (Basisklasse für die Einstellung der MainViewType Liegenschaften in Mainwindow Es enthält auch den Befehl zu ändern. Eigentum über den Button in der 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 von BaseMainViewViewModel ac zu erhalten Zugang zum Gerät CurrentMainView-Liegenschaften

    public class LoginViewModel : BaseMainViewViewModel {} 
    
  4. MainViewModel sie die gleichen

    public class MainViewModel : BaseMainViewViewModel {} 
    
  5. MainWindowViewModel

    public class MainWindowViewModel: BaseMainViewViewModel {} 
    
  6. LoginMainView

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

    Momentan habe ich nur eine Schaltfläche (Login-Button) in der LoginMainView. Wenn ich auf diese Schaltfläche klicke, sollte die aktuelle LoginMainView mit der MainMainView ausgetauscht werden.

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

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

    hier die gleiche (Logout-Button) entsprechen LoginMainView ...

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

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

    Im MainWindow- View Ich binde die CurrentMainView-Eigenschaft (MainViewType) vom BaseMainViewViewModel an den contentpresenter, den ich durch Klicken auf den Button in der MainMainView/LoginMainView und den ValueConverter ändern werde.

    <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; 
        } 
    } 
    

Wenn ich die Anwendung starte, wird der ValueConverter aufgerufen und die richtige View (LoginMainView) wird geladen.Ich klicke dann auf den Button in der LoginMainView, der Befehl (SwitchMainView) wird ausgeführt, aber dann wird der Inhalt von MainWindow nicht in MainMainView geändert, da der ValueConverter nicht benutzt wird.

Was mache ich falsch? Habe ich ein grundsätzliches Verständnisproblem? Oder ist es auf diese Weise nicht möglich, das einfache Login/Logout-Szenario abzubilden? Oder habe ich einfach etwas übersehen? Kann mir bitte jemand sagen, was ich vergessen habe?

Vielen Dank im Voraus an die Helfer!

Antwort

0

Sie brauchen ValueConverter dafür nicht. Du bist auf einem richtigen Weg. Werfen Sie einen Blick here - das ist Beispielanwendung für ReactiveUI-Framework (das ist mein Favorit).

Es hat AppBootsapper (ViewModel der Anwendung). Da der Rahmen drum herum einige Magick der Fall ist, ist die Grundidee:

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> 

Dann setzen Sie einfach AppBootstrapper.ActiveViewModel = new LoginViewModel() und Sie haben Bildschirm anmelden.

Wenn Sie sich anmelden, zeigt AppBootstrapper.ActiveViewModel = new MainViewModel() und WPF Hauptbildschirm.

All dies und noch viel mehr wird von ReactiveUI framwork getan - nur dort, statt DataTemplates für ViewModels zu setzen, registrieren Sie UserControls als Ansichten und RoutedViewHost erledigt all die Magie. Tun Sie das nicht alleine, es erfindet das Rad wieder.

EDIT den Kommentar zu beantworten:

Sie setzen AppBootstrapper.ActiveViewModel = new MainViewModel() in Ihrem Navigation. Navigation bedeutet die Sache, die die angezeigte Ansicht ändert. Die gebräuchlichste Version ist ein Stapel, der oben aktives ViewModel ist. Wenn Sie die Zurück-Taste drücken, blättern Sie einfach den Stapel.

Dies alles gilt für MVVM-Modell mit Model First Navigation, was bedeutet, dass Sie zuerst ViewModel instanziieren, und Navigationsdienst findet die richtige Ansicht.

Sie können dies auch auf andere Weise tun: Ansicht Erste Navigation. Es gibt einige Tutorials für die WPF-Seitennavigation. Es funktioniert genau gleich, aber statt ViewModel erstellen Sie eine Seite (eine Ansicht), die dann zugrunde liegende Daten erstellt. Das MVVM-App-Modell ist so beliebt, weil es eine sehr saubere Logik- und Präsentationstrennung erlaubt (XAML ist NUR View, ViewModels enthält alle Logik, Modelle persistieren die Daten), was wiederum die gemeinsame Nutzung von Logik zwischen Plattformen sehr einfach macht . Wenn Sie das richtig machen, können Sie alle ViewModels in Apps verwenden, die in Xamarin, WPF oder UWP geschrieben wurden, indem Sie plattformspezifische Ansichten erstellen.

Um es zu beenden, WPF ermöglicht es Ihnen, eine Eigenschaft Daten einzuschalten, und es wird eine Ansicht für es automatisch finden (über DataTemplates). Denken Sie daran, über INotifyPropertyChanged und alles wird funktionieren

+0

Ok, zunächst einmal vielen Dank für die Antwort. Aber um WPF und XAML besser zu lernen und zu verstehen, möchte ich das Rad neu erfinden. ;-) Was müsste ich tun, um es in der oben beschriebenen Weise zu implementieren? Was ich noch vermisse? Übrigens. Wo setze ich 'AppBootstraper.ActiveViewModel = neues LoginViewModel()'? Muss ich zwei Befehle dafür anlegen und an die entsprechenden Buttons binden? – srcalex

+0

Ich habe meine Antwort bearbeitet –

Verwandte Themen