[이미지 출처: https://blog.yena.io/studynote/2019/03/16/Android-MVVM-AAC-1.html]
INotifyPropertyChanged
인터페이스를 구현한 기본 클래스namespace ModernDesign.Core
{
class ObservableObject : INotifyPropertyChanged
{
// 구현 강제에 따라 자동으로 정의
public event PropertyChangedEventHandler PropertyChanged;
// name에 해당하는 이름을 가진 데이터가 변화할 때마다 이벤트 발생시키는 메서드
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
// PropertyChanged가 null이 아니면 Invoke 호출
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
}
}
참고: http://ojc.asia/bbs/board.php?bo_table=WPF&wr_id=146
ICommand
인터페이스를 구현하여 만든다Execute
CanExecute
RequerySuggested
이벤트CanExecuteChanged
이벤트namespace ModernDesign.Core
{
class RelayCommand : ICommand
{
private Action<object> _execute;
private Func<object, bool> _canExecute;
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
// 생성자
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
{
_execute = execute;
_canExecute = canExecute;
}
// CommandManager.RequerySuggested 이벤트가 호출될 때마다 실행
// 즉, CanExecuteChanged 이벤트가 호출될 때마다 실행
// false 리턴 시, Execute 메서드 호출하지 않음
public bool CanExecute(object parameter)
{
// 첫번째 식이 true이면 이를 리턴하고 false면 두번째 식 리턴
return _canExecute == null || _canExecute(parameter);
}
public void Execute(object parameter)
{
_execute(parameter);
}
}
}
<Border Background="#272537"
CornerRadius="20">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="20"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="75"/>
<RowDefinition/>
</Grid.RowDefinitions>
</Grid>
</Border>
<TextBox Width="250"
Height="40"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="5"
Grid.Column="1"/>
<ContentControl Grid.Row="1"
Grid.Column="1"
Margin="10"/>
xmlns:viewModel="clr-namespace:ModernDesign.MVVM.ViewModel"
<Window.DataContext>
<viewModel:MainViewModel/>
</Window.DataContext>
...
<ContentControl Grid.Row="1"
Grid.Column="1"
Margin="10"
Content="{Binding CurrentView}"/>
Button Style="{StaticResource ResourceKey}"></Button>
Button Style="{DynamicResource ResourceKey}"></Button>
Resource.MergedDictionaries
{x:Type TypeName="prefix:typeNameValue"/>
prefix
: 선택 사항, 네임 스페이스 매핑용 접두사typeNameValue
: 필수 요소, 형식 이름<Style>
<Setter Property="속성" Value="값">
</Style>
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style BasedOn="{StaticResource {x:Type ToggleButton}}" // ToggleButton 스타일 사용
TargetType="{x:Type RadioButton}" // 대상 타입은 RadioButton
x:Key="MenuButtonTheme"> // 타입명
<Style.Setters> // Style에 속하는 Setter 객체의 목록을 가져온다
<Setter Property="Template">
<Setter.Value>
// 바인딩을 위해 타겟 타입을 지정해줘야 한다
<ControlTemplate TargetType="RadioButton">
<Grid VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Background="{TemplateBinding Background}">
// main window의 라디오 버튼의 문구(Content)와 바인딩
<TextBlock Text="{TemplateBinding Property=Content}"
VerticalAlignment="Center"
Margin="50,0,0,0"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
// Setter Property 여러 개 설정 가능
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="0"/>
</Style.Setters>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True"> // 버튼 체크 여부
<Setter Property="Background" Value="#22202f"/> // 색 변경
</Trigger>
</Style.Triggers>
// MainWindow.xaml
<StackPanel Grid.Row="1">
<RadioButton Content="Home"
Height="50"
Foreground="White"
FontSize="14"
Style="{StaticResource MenuButtonTheme}"
IsChecked="True"
// HomeViewCommand와 바인딩
Command="{Binding HomeViewCommand}"/>
// 여러 개 사용 가능
// 하나의 StackPanel로 묶여 있어 자동으로 효과가 적용된다
<RadioButton Content="Discovery"
Height="50"
Foreground="White"
FontSize="14"
Style="{StaticResource MenuButtonTheme}"
Command="{Binding DiscoveryViewCommand}"/>
</StackPanel>
App.xaml
의 메인 리소스 영역에 추가하여 사용 가능 xmlns=
= using
구문)xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:
접두사에 매핑clr-namespace
:
)으로 토큰과 해당 값을 구분<Application x:Class="ModernDesign.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ModernDesign"
xmlns:viewModel="clr-namespace:ModernDesign.MVVM.ViewModel"
xmlns:view="clr-namespace:ModernDesign.MVVM.View"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Theme/MenuButtonTheme.xaml"/>
<ResourceDictionary Source="Theme/TextboxTheme.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
<DataTemplate DataType="{x:Type viewModel:HomeViewModel}">
<view:HomeView/>
</DataTemplate>
</Application.Resources>
</Application>
x:Name
<Grid>
<Rectangle StrokeThickness="1"/> // 윤곽선 두께 설정
<TextBox Margin="1"
Text="{TemplateBinding Text}"
BorderThickness="0"
Background="Transparent"
VerticalContentAlignment="Center"
Padding="5"
Foreground="#CFCFCF"
x:Name="SearchBox"/>
// 검색창에 아무 것도 입력되지 않았을 때 기본으로 뜨는 문구
<TextBlock IsHitTestVisible="False" // 사용자의 클릭 입력을 무시한다
Text="Search"
VerticalAlignment="Center"
HorizontalAlignment="Left"
Margin="10,0,0,0"
FontSize="11"
Foreground="DarkGray"
Grid.Column="1">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
// 아무것도 입력 안 됐을 때 보이도록 설정
<DataTrigger Binding="{Binding Text, ElementName=SearchBox}" Value="">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
// 글이 입력되면 사라진다
<Setter Property="Visibility" Value="Hidden"/>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
using System;
using ModernDesign.Core;
namespace ModernDesign.MVVM.ViewModel
{
class MainViewModel : ObservableObject
{
// 사용자가 컨트롤 클릭 시, ICommand 개체 공개
public RelayCommand HomeViewCommand { get; set; }
public RelayCommand DiscoveryViewCommand { get; set; }
public HomeViewModel HomeVM { get; set; }
public DiscoveryViewModel DiscoveryVM { get; set; }
private object _currentView;
// 의존 프로퍼티
// 외부에서 접근 시, 프로퍼티를 통해 값을 설정하고 조회
// _currentView를 설정, 조회하는 방법 정의
public object CurrentView
{
get { return _currentView; } // _currentView의 값을 바로 읽는다
set
{
// CurrentView에 설정되는 값을 바로 _currentView에 저장
_currentView = value;
OnPropertyChanged();
}
}
// Home과 Discovery를 클릭할 때, 서로 다른 화면이 나온다
public MainViewModel()
{
HomeVM = new HomeViewModel();
DiscoveryVM = new DiscoveryViewModel();
CurrentView = HomeVM;
HomeViewCommand = new RelayCommand(o =>
{
CurrentView = HomeVM;
});
DiscoveryViewCommand = new RelayCommand(o =>
{
CurrentView = DiscoveryVM;
});
}
}
}
LinearGradientBrush
<Border.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="1,2">
<GradientStop Color="#5bc3ff" Offset="0.0"/>
<GradientStop Color="#3aa0ff" Offset="1"/>
</LinearGradientBrush>
</Border.Background>
// 사각형 모퉁이 둥글게 하기 - CornerRadius="10"와 같은 결과
<Border.Clip>
<RectangleGeometry RadiusX="10"
RadiusY="10"
Rect="0,0,400,200"/>
</Border.Clip>
참고: https://www.youtube.com/watch?v=PzP8mw7JUzI&t=657s