C#을 한 번도 해본 적 없다. 그나마도 처음엔 WinForms를 보고 있었는데 어쩌다보니 WPF를 사용하게 되었다.
WPF는 WinForms에 비해 구글링 검색 결과가 많지 않아 걱정하던 차에 좋은 기회로 인프런에서 강의를 듣게 되었는데 유료 강의니까 내용을 다 적지 않겠다...
- 뛰어난 UI 쉽게 개발 가능
- WinForms에선 어렵게 작성했던 그라데이션과 반투명 컨트롤 등을 WPF에서는 쉽게 구성 가능하다.
- 리스트뷰에 버튼을 넣는 것도 손쉽게 구현 가능.
WinForms를 조금 사용해 봤는데 모서리가 둥글게 처리된 Round Label이나 Button을 만드는 것도 엄청 복잡했었다.
- MVVM 패턴을 사용한 정해진 구조 사용.
- UI와 비즈니스 로직을 분리해서 작성하여 협업에 좋다.
- Visual Studio Community 설치, 새 프로젝트 만들기
🤔 여기서 좀 혼란스러웠다.
Visual Studio Community 2022 버전을 사용 중인데 강의 속 닷넷 코어 WPF가 어떤건지 헷갈렸기 때문이다...
Core가 적혀있는 WPF 클래스 라이브러리 프로젝트를 생성해보니 기본으로 생성되는 MainWindow.xaml이랑 App.xmal이 없어서 혼란스러웠다.
닷넷 프레임워크로 프로젝트를 생성하면 기본 생성되는 파일이 동일하게 있긴한데 닷넷 코어와 닷넷 프레임워크는 다른 것이라고 하니...잘못 만든것 같다.
일단 사진에서 보이는 것 처럼...아래쪽은 .NET Framework라고 확실하게 적혀있으니까 위 쪽의 WPF 애플리케이션으로 시작했다.
- 도구 상자에서 도구를 끌어와 화면에 배치하는 것은 WinForms와 동일하다.
- x:Name을 지정하면 코드 behind에서 접근할 수 있다.
- Model은 데이터베이스의 테이블 또는 사물, 정보를 표현한 객체
- 바인딩은 그 객체를 컨트롤, 속성과 연결해 주는 것
- 프로젝트에 Models 라는 폴더를 추가하고 User 라는 이름의 클래스 파일을 추가한다.
Java Spring 할 때 DTO 같은 느낌...
namespace MyFirstProject.Models
{
class User
{
private string name;
public string Name
{
get { return name; }
set { name = value; }
}
...
}
}
- 도구 상자에서 ListView를 끌어와 배치한 후 GridView을 클릭하고 속성의 Colums를 편집한다.
- 각 열에 DisplayMemberBinding="{Binding (클래스의 이름)}" 으로 바인딩한다.
- ListView를 x:Name으로 이름을 지정한다.
<ListView x:Name="listView1" Margin="0,0,269,289">
<ListView.View>
<GridView>
<GridViewColumn Header="이미지" Width="300">
<GridViewColumn.CellTemplate>
<DataTemplate>
<StackPanel> <!--StackPanel은 기본적으로 세로 방향으로 쌓는다.-->
<Image Width="60" Height="60" Source="{Binding UserImg}" />
</StackPanel>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="이름" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="나이" DisplayMemberBinding="{Binding UserAge}"/>
</GridView>
</ListView.View>
</ListView>
- 코드 보기에서 앞서 작성한 User 클래스의 객체를 생성해서 List에 추가한다.
(MainWindow.xaml.cs)
namespace MyFirstProject
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<User> myList1 = new List<User>();
User userA = new User();
userA.Name = "Noah";
userA.UserAge = 15;
userA.UserImg = @"C:\\Users\\yujin\\source\\repos\\MyFirstProject\\Resources\\photo1.jpg";
...
listView1.ItemsSource = myList1;
}
}
}
실행하면 이런 화면을 확인할 수 있다.
- Style
WinForms에서 복잡하게 구현했던 그라데이션이나 반투명 배경 컨트롤을 컨트롤-속성에서 간단하게 만들 수 있다.
- Xaml Resources
Xaml 리소스는 다른 위치에서 Style을 재사용할 수 있도록 해준다.
- Window(창)은 <Window.Resource></Window.Resource> 사이에 작성한다.
- x:Key로 Style의 이름을 지정한다.
- 작성한 스타일 리소스를 컨트롤에 적용할 때엔 Style="{StaticResource (지정한이름)}" 속성을 추가한다.
<Window.Resources>
<Style x:Key="testStyle">
<Setter Property="Label.Background">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Label Style="{StaticResource testStyle}" x:Name="labelTest1" Content="Label" HorizontalAlignment="Left" Margin="621,58,0,0" VerticalAlignment="Top">
</Grid>
이제 Label 컨트롤 생성한 후 Style 속성을 testStyle로 지정하면 Window.Resource에서 작성한 testStyle(배경이 검정색 그라데이션인)이 적용되는 것이다.
- Trigger
Trigger를 사용하면 이벤트 발생, 속성 값 변경 시 스타일을 변경할 수 있다.
이런 행위를 하면...이런 동작을 하겠다...
- testButtonStyle 이라는 이름의 Style 작성
- <Style.Triggers></Style.Triggers> 사이에 Trigger의 속성에 따른 동작을 작성한다.
- 아래 소스는 IsMouseOver : 마우스가 Button 위로 올라왔을 때 Foreground : 글자의 색상을 Value : Red 색상이 되게 하는 Trigger이다.
<Window.Resources>
<Style x:Key="testButtonStyle">
<Setter Property="Button.Background">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="Control.IsMouseOver" Value="True">
<Setter Property="Control.Foreground" Value="Red"></Setter>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<Button Style="{StaticResource testButtonStyle}" Content="Button" HorizontalAlignment="Left" Margin="10,333,0,0" VerticalAlignment="Top" Width="348" />
</Grid>
Button의 Background 색상은 마우스 오버 됐을 때 자체 Template이 하늘색으로 설정되어있기 때문에 Trigger만으로 바꿀 수 없다고 한다.
방법은 Button의 Template을 재정의하는 것이라고 한다. 참고한 글을 4. 참조에 적었다...
- DataTrigger
- 다른 컨트롤 또는 UI 클래스가 아닌 모든 속성과 연결할 수 있다.
- DataTrigger를 사용해서 자기 자신의 속성 변화가 아닌 특정 Control의 속성 변화에 따라 Style에 변화를 줄 수 있다.
- 아래 소스는 CheckBox가 IsChecked : 체크박스를 체크 상태일때 Button의 Foreground : 글자 색이 Red가 되는 동작을 수행한다.
<Window.Resources>
<Style x:Key="testButtonStyle">
<Setter Property="Button.Background">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="Black"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=checkBox1, Path=IsChecked}" Value="True">
<Setter Property="Control.Foreground" Value="Red" />
<Setter Property="Control.IsEnabled" Value="true" />
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<CheckBox x:Name="checkBox1" Content="CheckBox" HorizontalAlignment="Left" Margin="148,299,0,0" VerticalAlignment="Top"/>
<Button Style="{StaticResource testButtonStyle}" Content="Button" HorizontalAlignment="Left" Margin="10,333,0,0" VerticalAlignment="Top" Width="348" />
</Grid>
- 클래스를 생성하고 객체를 생성하여 객체의 속성이 변경되면 DataTrigger를 통해 해당 속성 값 대로 ProgressBar가 움직이도록 구성할 수 있다.
- 아래 소스는 Button을 눌러서 ProgressBar의 Value 변경할 수 있다.
- INotifyPropertyChanged라는 인터페이스를 상속 받는다.
앞서 View와 MainViewModel의 속성을 Binding하기 위해서인데, INotifyPropertyChanged 사용하면 해당 객체 속성을 변경해주면 연결된 View에게 업데이트 하라고 알려준다. (여기서 변경되면 화면에도 반영이 되도록 해준다.)
namespace MyFirstProject.ViewModels
{
class MainViewModel : INotifyPropertyChanged
{
private int progressValue;
public int ProgressValue
{
get { return progressValue; }
set
{
progressValue = value;
NotifyPropertyChanged(nameof(ProgressValue));
}
}
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
MainViewModel 클래스의 객체 mainViewModel을 생성하고 값을 30으로 초기화 한다.
namespace MyFirstProject
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
MainViewModel mainViewModel;
public MainWindow()
{
InitializeComponent();
mainViewModel = new MainViewModel();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
mainViewModel.ProgressValue = 30;
DataContext = mainViewModel;
}
}
}
<ProgressBar Height="10" Width="100" Margin="10,375,690,49" Value="{Binding ProgressValue}" />
인프런-WPF 강의
https://inf.run/b2cK
.NET Core와 .NET Framework 차이
https://blog.centbin.com/net-core%EC%99%80-net-framework-%EB%B9%84%EA%B5%90-%EB%B6%84%EC%84%9D-c-%EC%8B%9C%EB%A6%AC%EC%A6%88-3/
WPF Button의 Background 색상 변경하기
- https://jay1127.github.io/wpf/21-How-To-Change-WPF-Button-MouseOver-Color/
- https://syntaxfix.com/question/16039/how-do-you-change-background-for-a-button-mouseover-in-wpf
WPF Template이란?
https://sunday5214.tistory.com/21
WPF DataContext란?
https://yeko90.tistory.com/entry/c-wpf-DataContext%EB%9E%80-%EC%99%9C#google_vignette