|
|||||||
Время создания: 23.11.2019 20:27
Раздел: Компьютер - C# - WPF - MVVM
Запись: Kozlov-AE/Tetra/master/base/157445530799nd3883ub/text.html на raw.githubusercontent.com
|
|||||||
|
|||||||
Есть 2 ViewModel`и AuthViewModel и LoginViewModel. В LoginView есть Frame который контент которого LoginControl а у него контекст AuthViewModel. И в этом LoginControl есть кнопка при нажатии на которую должно срабатывать события LoginCompleted и в App.xaml.cs идет обработка события. AuthViewModel.cs public event Action LoginCompleted; public ICommand AuthCommand => new RelayCommand(o => AuthMethod()); public void AuthMethod() { LoginCompleted?.Invoke(); } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string name) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); App.xaml.cs private LoginWindow Window { get; set; } private MainWindow CustomWindow { get; set; } public MainLoginVIewModel MainViewModel { get; set; } public LoginViewModel LoginViewModel { get; set; } protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); LoginViewModel = new LoginViewModel(); MainViewModel = new MainLoginVIewModel(LoginViewModel); LoginViewModel.OnAuthorize += LoginViewModelOnOnAuthorize; Window = new LoginWindow { DataContext = MainViewModel }; Window.Show(); } private void LoginViewModelOnOnAuthorize(object sender, LoginEventArgs e) { if (e.IsAuthorized) { CustomWindow = new MainWindow { DataContext = new ContentViewModel(e.User) }; CustomWindow.Show(); Window.Close(); } } Тоесть нажимая на кнопку в LoginControl должно срабатывать событие в App.xaml.cs Если я что то не то делаю подскажите как реализовать правильней. Если нужно еще скину кода RegisterControl.xaml <Grid> <TextBox Text="{Binding User}" Width="100" Height="100"></TextBox> </Grid> RegisterViewModel.cs class RegisterViewModel : VM { private string user; public string User { get => user; set => Set(ref user, value); } } ViewModel главного окна авторизации у меня оно называется MainLoginViewModel RegisterViewModel regVM; public MainLoginVIewModel(LoginViewModel loginVM) { TestCommand = new RelayCommand(Test); regVM = new RegisterViewModel(); CurrentContent = loginVM; } private VM currentContent; public VM CurrentContent { get => currentContent; set => Set(ref currentContent, value); } public ICommand TestCommand { get; } private void Test() { CurrentContent = regVM; } 1971010 бронзовых знаков Как по мне тут неверно 3 вещи. 1. Вы всю логику размещаете в App, когда он должен быть простым звеном для первичной обработки некой вспомогательной логики самой программы при старте/закрытие. Подписываться там на события не есть хорошо. 2. Вы нарушаете MVVM, по MVVM вы не должны работать с контролами через код, для этого используются VM и привязки. У вас же, на сколько я понял, идет инициализация контрола через код (s = (UserControl1)LoginWindow.Hello.Content;). 3. Frame, тоже не очень верный подход. Как по мне он совершенно не годиться для показа контролов... – EvgeniyZ 27 окт '18 в 22:14 А вы можете поптробнее и ввиде ответа все 3 пункта написать? Заранее благодарю – Павел Ериков 27 окт '18 в 22:23 Если так бегло посмотреть на ваше описание, то у вас тут как минимум 3 проблемы:
Давайте попробуем сделать простой пример с реализацией всего, что я написал выше. Сделаем некий аналог авторизации с событием и сменой контента: Я начну это все на пустом проекте, вы же смотрите сами... И так, для начала нам понадобится два вспомогательных класса:
public class VM : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected bool Set<T>(ref T field, T value, [CallerMemberName] string propertyName = null) { if (EqualityComparer<T>.Default.Equals(field, value)) return false; field = value; NotifyPropertyChanged(propertyName); return true; } protected void NotifyPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } public class RelayCommand<T> : ICommand { private Action<T> action; public RelayCommand(Action<T> action) => this.action = action; public bool CanExecute(object parameter) => true; #pragma warning disable CS0067 public event EventHandler CanExecuteChanged; #pragma warning restore CS0067 public void Execute(object parameter) => action((T)parameter); } public class RelayCommand : ICommand { private Action action; public RelayCommand(Action action) => this.action = action; public bool CanExecute(object parameter) => true; #pragma warning disable CS0067 public event EventHandler CanExecuteChanged; #pragma warning restore CS0067 public void Execute(object parameter) => action(); } Отлично, подготовка завершена. Теперь сделаем две страницы и VM под них.
public class LoginViewModel : VM { public event EventHandler<LoginEventArgs> OnAuthorize; public ICommand LoginCommand { get; } public LoginViewModel() { LoginCommand = new RelayCommand(Authorize); } private string user; public string User { get => user; set => Set(ref user, value); } private string password; public string Password { get => password; set => Set(ref password, value); } private void Authorize() { if (User?.ToLower() == "test" && Password?.ToLower() == "123") { OnAuthorize?.Invoke(this, new LoginEventArgs(User, true, "Успешная авторизация!")); } else { OnAuthorize?.Invoke(this, new LoginEventArgs(User, false, "Неверный логин или пароль!")); } } } public class LoginEventArgs : EventArgs { public string User { get; } public bool IsAuthorized { get; } public string Message { get; } public LoginEventArgs(string user, bool isAuthorized, string message) { User = user; IsAuthorized = isAuthorized; Message = message; } } <Grid VerticalAlignment="Center" HorizontalAlignment="Center"> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto"/> <ColumnDefinition/> </Grid.ColumnDefinitions> <TextBlock Grid.Column="0" Grid.Row="0" Text="Логин: "/> <TextBlock Grid.Column="0" Grid.Row="1" Text="Пароль: "/> <TextBox Grid.Column="1" BorderThickness="0 0 0 1" Grid.Row="0" Text="{Binding User}" Width="100"/> <TextBox Grid.Column="1" BorderThickness="0 0 0 1" Grid.Row="1" Text="{Binding Password}" Width="100"/> <Button Grid.Row="2" Margin="0 5" Background="Transparent" Grid.ColumnSpan="2" Grid.Column="0" Content="Войти" Command="{Binding LoginCommand}"/> </Grid>
public class ContentViewModel : VM { public string Title { get; set; } = "У нас получилось!"; } <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center" FontSize="20" Text="{Binding Title}"/> Теперь давайте поработаем над основным окном нашего приложения (MainWindow). Для него мы сделаем тоже свою ViewModel, назовем MainViewModel и в ней мы все это объединим: public class MainViewModel : VM { public LoginViewModel LoginViewModel { get; } public MainViewModel() { LoginViewModel = new LoginViewModel(); LoginViewModel.OnAuthorize += LoginViewModelOnOnAuthorize; CurrentContent = LoginViewModel; } private VM currentContent; public VM CurrentContent { get => currentContent; set => Set(ref currentContent, value); } private string message; public string Message { get => message; set => Set(ref message, value); } private void LoginViewModelOnOnAuthorize(object sender, LoginEventArgs e) { Message = $"[{e.User}] {e.Message}"; CurrentContent = e.IsAuthorized ? (VM) new ContentViewModel() : LoginViewModel; } } Можно заметить, что мы создаем свойство ViewModel авторизации, в конструкторе инициализируем, подписываемся на событие и для примера задаем как основной контент нашего окна. В обработчики события я для примера формирую сообщение, которое в присваиваю созданному свойству. Теперь само MainWindow: <Grid> <Grid.Resources> <DataTemplate DataType="{x:Type vm:ContentViewModel}"> <view:ContentView/> </DataTemplate> <DataTemplate DataType="{x:Type vm:LoginViewModel}"> <view:LoginView/> </DataTemplate> </Grid.Resources> <StackPanel> <Border BorderThickness="0 0 0 1" BorderBrush="Gray" Padding="10" Margin="10"> <TextBlock Text="{Binding Message}"/> </Border> <ContentPresenter Content="{Binding CurrentContent}"/> </StackPanel> </Grid> С помощью DataTemplate связываем каждую ViewModel со своей View. Также выводим сообщение (обычный TextBlock, который привязан к свойству Message), ну и контент страницы (им мы заменяем Frame), задаем мы его с помощью ContentPresenter. Осталось нам задать DataContext, сделаем мы это переписав немного App:
private MainWindow Window { get; set; } public MainViewModel MainViewModel { get; set; } protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); MainViewModel = new MainViewModel(); Window = new MainWindow{DataContext = MainViewModel}; Window.Show(); } Все, запускаем и любуемся проделанной работой: Вот так, довольно просто, мы избавились от ужасного Frame, использовали событие и вывели разное содержимое в одно окно. Заметьте, мы не разу не обратились к элементу какой либо View, мы можем их вообще убрать и наша логика будет спокойно жить свой жизнью. Вот это разбитие по слоям и есть MVVM! Вы главное поймите принцип, как все это работает и дальше пойдет все как по маслу! Удачи! Если у нас разные окна, то самый простой вариант, это сделать следующее:
public MainViewModel(LoginViewModel loginVM) { LoginViewModel = loginVM; CurrentContent = LoginViewModel; } public partial class App : Application { private MainWindow Window { get; set; } private CustomWindow CustomWindow { get; set; } public MainViewModel MainViewModel { get; set; } public LoginViewModel LoginViewModel { get; set; } protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); LoginViewModel = new LoginViewModel(); MainViewModel = new MainViewModel(LoginViewModel); LoginViewModel.OnAuthorize += LoginViewModelOnOnAuthorize; Window = new MainWindow{DataContext = MainViewModel}; Window.Show(); } private void LoginViewModelOnOnAuthorize(object sender, LoginEventArgs e) { if (e.IsAuthorized) { CustomWindow = new CustomWindow { DataContext = LoginViewModel }; CustomWindow.Show(); Window.Close(); return; } MainViewModel.Message = $"[{e.User}] {e.Message}"; } } Все, теперь при запуске у нас откроется MainWindow с контентом авторизации и если авторизация успешна, то MainWindow закроется и заместо него появится CustomWindow (другое наше окно). |
|||||||
Так же в этом разделе:
|
|||||||
|
|||||||
|