로그인 화면이 있고 로그인 한 사용자 타입에 따라 보여지는 메뉴 항목과 내용이 달라지게 하고싶다.
코드 비하인드에서 MainWindow 내에 배치한 Button으로 마찬가지로 MainWindow 내에 배치되어 있는 Frame에 화면을 전환하는 방식은 구글링으로 제법 찾아봤는데 MVVM 패턴으로 구현된건 찾아보기가 힘들었다.
사실 안나오는데엔 이유가 있다고도 보는데 아무튼간에...
<Application x:Class="PracticeProject.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PracticeProject"
StartupUri="Views/LoginWindow.xaml">
<Application.Resources>
</Application.Resources>
</Application>
<Window x:Class="PracticeProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PracticeProject"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100*"/>
<ColumnDefinition Width="700*"/>
</Grid.ColumnDefinitions>
<!--메뉴바-->
<Grid Grid.Column="0">
<Frame x:Name="MenuBar" NavigationUIVisibility="Hidden" />
</Grid>
<!--콘텐츠-->
<Grid Grid.Column="1">
<Frame x:Name="Contents" NavigationUIVisibility="Hidden" />
</Grid>
</Grid>
</Window>
<Window x:Class="PracticeProject.Views.LoginWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PracticeProject.Views"
mc:Ignorable="d"
Title="LoginWindow" Height="450" Width="800">
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" >
<Label Content="아이디"/>
<TextBox Width="200"/>
<Label Content="비밀번호"/>
<PasswordBox Width="200"/>
<Button Content="로그인" Margin="0,10,0,0"/>
</StackPanel>
</Grid>
</Window>
<Page x:Class="PracticeProject.Views.MenuBar1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:PracticeProject.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="100"
Title="MenuBar1">
<Grid>
<StackPanel>
<Button Content="Menu1" Height="30" />
<Button Content="Menu2" Height="30" />
<Button Content="Menu3" Height="30" />
</StackPanel>
</Grid>
</Page>
<Page x:Class="PracticeProject.Views.MenuBar2"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:PracticeProject.Views"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="100"
Title="MenuBar2">
<Grid>
<StackPanel>
<Button Content="Menu4" Height="30" />
<Button Content="Menu5" Height="30" />
<Button Content="Menu6" Height="30" />
</StackPanel>
</Grid>
</Page>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace PackagingSystem.Commands
{
public class RelayCommand : ICommand
{
private readonly Action _execute;
private readonly Func<bool> _canExecute;
public RelayCommand(Action execute, Func<bool> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public bool CanExecute(object parameter) => _canExecute == null || _canExecute();
public void Execute(object parameter) => _execute();
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace PackagingSystem.ViewModels
{
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetProperty<T>(ref T backingField, T value, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(backingField, value)) return false;
backingField = value;
OnPropertyChanged(propertyName);
return true;
}
}
}
C#을 제대로 기초부터 배워본 적이 없어서...설명을 못하겠다!
using PackagingSystem.Commands;
using PackagingSystem.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace PracticeProject.ViewModels
{
class LoginWindowModel : BaseViewModel
{
private string _id;
private string _password;
public string Id
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged(nameof(Id));
}
}
public string Password
{
get { return _password; }
set
{
_password = value;
OnPropertyChanged(nameof(Password));
}
}
public ICommand LoginCommand { get; set; }
public event Action RequestClose;
public LoginWindowModel()
{
LoginCommand = new RelayCommand(Login);
}
private void Login()
{
int iSegNum = segUser(Id, Password);
if(iSegNum > 0)
{
MainWindow mainWindow = MainWindow.GetInstance();
mainWindow.Show();
if(iSegNum == 1)
{
mainWindow.MenuBar.Source = new Uri("MenuBar1.xaml", UriKind.Relative);
}
else if(iSegNum == 2)
{
mainWindow.MenuBar.Source = new Uri("MenuBar2.xaml", UriKind.Relative);
}
RequestClose?.Invoke(); // 이벤트 발생
}
else
{
MessageBox.Show("아이디, 패스워드가 일치하지 않습니다.");
}
}
private int segUser(string id, string password)
{
int segNum = 0;
if(id == "user" && password == "user")
{
segNum = 1;
}
else if(id == "manager" && password == "manager")
{
segNum = 2;
}
return segNum;
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace PracticeProject
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private static MainWindow _Instance = null;
public static MainWindow GetInstance()
{
if (_Instance == null)
{
_Instance = new MainWindow();
}
return _Instance
}
public MainWindow()
{
InitializeComponent();
}
}
}
<Window x:Class="PracticeProject.Views.LoginWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:PracticeProject.Views"
xmlns:vm="clr-namespace:PracticeProject.ViewModels"
mc:Ignorable="d"
Title="LoginWindow" Height="450" Width="800">
<Window.DataContext>
<vm:LoginWindowModel />
</Window.DataContext>
<Grid>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" >
<Label Content="아이디"/>
<TextBox Width="200" Text="{Binding Id}"/>
<Label Content="비밀번호"/>
<PasswordBox Width="200" PasswordChanged="OnPasswordChanged"/>
<Button Content="로그인" Command="{Binding LoginCommand}" Margin="0,10,0,0"/>
</StackPanel>
</Grid>
</Window>
TextBox는 Text 속성에 Binding이 가능하지만 PasswordBox는 보안상의 이유로 Binding이 불가능하다. 그래서 PasswordChanged 속성에 이벤트를 작성한다.
LoginWindow.xaml.cs(코드 비하인드)
using PracticeProject.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace PracticeProject.Views
{
/// <summary>
/// LoginWindow.xaml에 대한 상호 작용 논리
/// </summary>
public partial class LoginWindow : Window
{
public LoginWindow()
{
InitializeComponent();
var viewModel = new LoginWindowModel();
DataContext = viewModel;
}
private void OnPasswordChanged(object sender, RoutedEventArgs e)
{
if (DataContext is LoginWindowModel viewModel)
{
viewModel.Password = ((PasswordBox)sender).Password;
}
}
}
}
id : user, password : user로 로그인했을 때
id : manager, password : manager로 로그인했을 때
로그인 유형에 따라 각각 다른 메뉴 바(page)가 나오게 했다.
사실 막 찾아볼 때 ViewModel에서 View 혹은 컨트롤의 프로퍼티를 직접 바인딩해서 조작하는 방식은 사용하면 안된다고 했는데(내용 : https://kaki104.tistory.com/531#google_vignette) 이것 말고는 어떻게 해야할지 잘 모르겠다.
그리고 코드 비하인드를 될 수 있으면 작성하지 않는 것에 너무 집착하다보니 사용할 생각을 아예 못했다...
각 메뉴를 눌렀을때 오른쪽 Frame(x:Name을 Contents로 지정한 것)이 전환되는 것을 다음 글에 적기로 한다...