|
원문 : http://kaki104.tistory.com/109
(이미지와 소스는 원문에 있습니다)
Visual Studio 11에서 포터블 라이브러리를 이용해서 WPF, Silverlight, Metro style에서 MVVM Pattern 사용하기
참고 포스트
Understanding the Portable Library by Chasing ICommand (1 of 3)
http://csharperimage.jeremylikness.com/2012/03/understanding-portable-library-by.html
Portable Library Tools
http://visualstudiogallery.msdn.microsoft.com/b0e0b5e9-e138-410b-ad10-00cb3caf4981
참고 포스트에 있는 Jeremy라는 분은 좋은 포스트를 많이 올리는 사람 중에 한명인데, 포터블 라이브러리라는 것이 어떻게 동작을 하는지에 대해서 포스트를 했다. 사실 포터블 라이브러리가 나온지는 약간 시간이 지났는데..그동안 활용을 어떻게해야 하는지 약간 막막했는데, 지난 주 교육 때 이 포스트를 보게 되었고, 내용을 읽어 보니 너무 좋은 내용이라 포스트를 하게 되었다.
핵심은 포터블 라이브러리가 사용되는 프로젝트의 종류에 따라서 dll들을 변경해 준다는 것이다.
1. Portable Class Library 만들기
포터블 클래스 라이브러리를 만들기 위해서는 Portable Library Tools를 다운로드 받아서 설치를 해야한다.
VS11을 실행 -> File -> New Project -> Visual C# -> Portable Class Library 선택
Name : SamplePortableClassLibrary -> OK
SamplePortableClassLibrary 프로젝트의 Property 창으로 들어간다.
Change 클릭
.Net Framework를 4.5로 변경하고, Windows Phone은 체크를 풀어준다. -> OK
기본 Class1.cs는 삭제하고 Models 폴더와 ViewModels 폴더를 만든다.
2. Person.cs
Models 폴더에 Person 클래스를 추가한다.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
namespace SamplePortableClassLibrary.Models
{
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
private int id;
public int Id
{
get { return id; }
set
{
id = value;
RaisePropertyChanged("Id");
}
}
private int age;
public int Age
{
get { return age; }
set
{
age = value;
RaisePropertyChanged("Age");
}
}
private string name;
public string Name
{
get { return name; }
set
{
name = value;
RaisePropertyChanged("Name");
}
}
private bool sex;
public bool Sex
{
get { return sex; }
set
{
sex = value;
RaisePropertyChanged("Sex");
}
}
}
}
3. PersonViewModel.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using SamplePortableClassLibrary.Models;
using System.Windows.Input;
namespace SamplePortableClassLibrary.ViewModels
{
public class PersonViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
ObservableCollection<Person> people;
public ObservableCollection<Person> People
{
get { return people; }
set
{
people = value;
RaisePropertyChanged("People");
}
}
private Person currentPerson;
public Person CurrentPerson
{
get { return currentPerson; }
set
{
currentPerson = value;
RaisePropertyChanged("CurrentPerson");
}
}
public PersonViewModel()
{
People = new ObservableCollection<Person>
{
new Person{ Id=0 , Age=11 , Name="kaki104" , Sex=true},
new Person{ Id=1 , Age=22 , Name="kakao" , Sex=false},
new Person{ Id=2 , Age=33 , Name="kass" , Sex=true},
new Person{ Id=3 , Age=44 , Name="kameo" , Sex=false},
new Person{ Id=4 , Age=55 , Name="keroro" , Sex=true}
};
CurrentPerson = People.FirstOrDefault();
}
ICommand addCommand;
public ICommand AddCommand
{
get
{
if (addCommand == null)
{
addCommand = new DelegateCommand(obj =>
{
CurrentPerson = new Person();
CurrentPerson.Id = People.Max(p => p.Id) + 1;
People.Add(CurrentPerson);
});
}
return addCommand;
}
}
ICommand removeCommand;
public ICommand RemoveCommand
{
get
{
if (removeCommand == null)
{
removeCommand = new DelegateCommand(obj =>
{
var person = obj as Person;
if (person != null && People.Count(p => p.Id == person.Id) > 0)
People.Remove(person);
});
}
return removeCommand;
}
}
ICommand selectChangedCommand;
public ICommand SelectChangedCommand
{
get
{
if (selectChangedCommand == null)
{
selectChangedCommand = new DelegateCommand(obj =>
{
var person = obj as Person;
if (person != null && People.Count(p => p.Id == person.Id) > 0)
{
CurrentPerson = person;
}
});
}
return selectChangedCommand;
}
}
}
}
모델과 뷰모델을 모두 완성 한 후에 빌드를 한번 해서 오류 여부를 확인한다.
4. WPF Project
File -> Add -> New Project -> Visual C# -> Windows -> WPF Application
.Net Framework 4.5로 설정하고 OK를 클릭한다.
WpfApplication1 -> References -> Add Reference -> Solution -> Projects -> SamplePortableClassLibrary 선택 -> OK
MainWindows.xaml
빌드를 한번 해준 후 작업을 한다.
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModels="clr-namespace:SamplePortableClassLibrary.ViewModels;assembly=SamplePortableClassLibrary"
x:Class="WpfApplication1.MainWindow"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<DataTemplate x:Key="DataTemplate1">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Id = "/>
<TextBlock Text="{Binding Id}" Margin="0,0,10,0"/>
<TextBlock Text="Age = "/>
<TextBlock Text="{Binding Age}" Margin="0,0,10,0"/>
<TextBlock Text="Name = "/>
<TextBlock Text="{Binding Name}" Margin="0,0,10,0"/>
<TextBlock Text="Sex = "/>
<TextBlock Text="{Binding Sex}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Window.DataContext>
<ViewModels:PersonViewModel/>
</Window.DataContext>
<Grid>
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Sample Portable Library in WPF"/>
</StackPanel>
<ListBox x:Name="lbPeople" ItemsSource="{Binding People}" ItemTemplate="{DynamicResource DataTemplate1}" >
</ListBox>
<StackPanel Orientation="Horizontal">
<Button Content="Set Current" Command="{Binding SelectChangedCommand, Mode=OneWay}" CommandParameter="{Binding SelectedItem, ElementName=lbPeople}"/>
<Button Content="Add People" Command="{Binding AddCommand, Mode=OneWay}"/>
<Button Content="Remove People" Command="{Binding RemoveCommand, Mode=OneWay}" CommandParameter="{Binding SelectedItem, ElementName=lbPeople}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Id = "/>
<TextBox Text="{Binding CurrentPerson.Id}" Margin="0,0,10,0"/>
<TextBlock Text="Age = "/>
<TextBox Text="{Binding CurrentPerson.Age}" Margin="0,0,10,0"/>
<TextBlock Text="Name = "/>
<TextBox Text="{Binding CurrentPerson.Name}" Margin="0,0,10,0"/>
<TextBlock Text="Sex = "/>
<CheckBox IsChecked="{Binding CurrentPerson.Sex}"/>
</StackPanel>
</StackPanel>
</Grid>
</Window>
MainWindows.xaml.cs 파일에는 단 한줄의 코드도 입력하지 않았다.
실행화면
5. Silverlight 5
File -> Add -> New Project -> Visual C# -> Silverlight -> Silverlight Application -> OK
New Silverlight Application -> OK
SilverlightApplication1 -> Add Reference -> Solution -> SamplePortableClassLibrary -> OK
* 실버라이트의 경우 디자인 타임에서 오류가 발생하는데, 무시하고 작업을 한다.
<UserControl x:Class="SilverlightApplication1.MainPage"
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:ViewModels="clr-namespace:SamplePortableClassLibrary.ViewModels;assembly=SamplePortableClassLibrary"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<UserControl.Resources>
<DataTemplate x:Key="DataTemplate1">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Id = "/>
<TextBlock Text="{Binding Id}" Margin="0,0,10,0"/>
<TextBlock Text="Age = "/>
<TextBlock Text="{Binding Age}" Margin="0,0,10,0"/>
<TextBlock Text="Name = "/>
<TextBlock Text="{Binding Name}" Margin="0,0,10,0"/>
<TextBlock Text="Sex = "/>
<TextBlock Text="{Binding Sex}"/>
</StackPanel>
</DataTemplate>
</UserControl.Resources>
<UserControl.DataContext>
<ViewModels:PersonViewModel/>
</UserControl.DataContext>
<Grid x:Name="LayoutRoot" Background="White">
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Sample Portable Library in Silverlight 5"/>
</StackPanel>
<ListBox x:Name="lbPeople" ItemsSource="{Binding People}" ItemTemplate="{StaticResource DataTemplate1}" >
</ListBox>
<StackPanel Orientation="Horizontal">
<Button Content="Set Current" Command="{Binding SelectChangedCommand, Mode=OneWay}" CommandParameter="{Binding SelectedItem, ElementName=lbPeople}"/>
<Button Content="Add People" Command="{Binding AddCommand, Mode=OneWay}"/>
<Button Content="Remove People" Command="{Binding RemoveCommand, Mode=OneWay}" CommandParameter="{Binding SelectedItem, ElementName=lbPeople}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Id = "/>
<TextBox Text="{Binding CurrentPerson.Id}" Margin="0,0,10,0"/>
<TextBlock Text="Age = "/>
<TextBox Text="{Binding CurrentPerson.Age, Mode=TwoWay}" Margin="0,0,10,0"/>
<TextBlock Text="Name = "/>
<TextBox Text="{Binding CurrentPerson.Name, Mode=TwoWay}" Margin="0,0,10,0"/>
<TextBlock Text="Sex = "/>
<CheckBox IsChecked="{Binding CurrentPerson.Sex, Mode=TwoWay}"/>
</StackPanel>
</StackPanel>
</Grid>
</UserControl>
WPF와 거의 동일한 소스를 사용하며, 역시 MainPage.xaml.cs에는 단 한 줄의 코딩도 하지 않는다.
6. Metro style
File -> Add -> New Project -> Visual C# -> Windows Metro style -> Blank Application -> OK
Application1 -> Add Reference -> Solution -> SamplePortableClassLibrary -> OK
BlankPage.xaml
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:Application1"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:ViewModels="using:SamplePortableClassLibrary.ViewModels"
x:Class="Application1.BlankPage"
mc:Ignorable="d">
<Page.Resources>
<DataTemplate x:Key="DataTemplate1">
<StackPanel Orientation="Horizontal">
<TextBlock Text="Id = "/>
<TextBlock Text="{Binding Id}" Margin="0,0,10,0"/>
<TextBlock Text="Age = "/>
<TextBlock Text="{Binding Age}" Margin="0,0,10,0"/>
<TextBlock Text="Name = "/>
<TextBlock Text="{Binding Name}" Margin="0,0,10,0"/>
<TextBlock Text="Sex = "/>
<TextBlock Text="{Binding Sex}"/>
</StackPanel>
</DataTemplate>
</Page.Resources>
<Page.DataContext>
<ViewModels:PersonViewModel/>
</Page.DataContext>
<Grid Background="{StaticResource ApplicationPageBackgroundBrush}">
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Sample Portable Library in Windows 8 Metro style" Style="{StaticResource HeaderTextStyle}"/>
</StackPanel>
<ListBox x:Name="lbPeople" ItemsSource="{Binding People}" ItemTemplate="{StaticResource DataTemplate1}" >
</ListBox>
<StackPanel Orientation="Horizontal">
<Button Content="Set Current" Command="{Binding SelectChangedCommand, Mode=OneWay}" CommandParameter="{Binding SelectedItem, ElementName=lbPeople}"/>
<Button Content="Add People" Command="{Binding AddCommand, Mode=OneWay}"/>
<Button Content="Remove People" Command="{Binding RemoveCommand, Mode=OneWay}" CommandParameter="{Binding SelectedItem, ElementName=lbPeople}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Id = "/>
<TextBox Text="{Binding CurrentPerson.Id}" Margin="0,0,10,0"/>
<TextBlock Text="Age = "/>
<TextBox Text="{Binding CurrentPerson.Age, Mode=TwoWay}" Margin="0,0,10,0"/>
<TextBlock Text="Name = "/>
<TextBox Text="{Binding CurrentPerson.Name, Mode=TwoWay}" Margin="0,0,10,0"/>
<TextBlock Text="Sex = "/>
<CheckBox IsChecked="{Binding CurrentPerson.Sex, Mode=TwoWay}"/>
</StackPanel>
</StackPanel>
</Grid>
</Page>
실버라이트와 비슷한 코드를 가지며, 역시 BlankPage.xaml.cs에는 한줄의 코딩도 없이 뷰모델과 커맨드를 사용한다.
7. 포터블 라이브러리 1개를 잘 만들면
WPF, Silverlight, Metro style에서 모두 함께 사용할 수 있다는 것은 개발자로서는 참으로 반가운 일이 아닐 수 없다. 물론 약간의 제약 사항이 존재하는 것은 어쩔 수 없다. 그러나, 이점이 더 많은 라이브러리라고 할 수 있겠다. 추가적으로 포터블 라이브러리의 모델을 이용하면 MVC4의 Single Application Page도 만들어서 사용하는 것이 가능하다.
한가지 아쉬운 점은 이 모든 것이 SignalR로 연결이 된다면 금상첨화 일턴데..SignalR에서는 아직 WinRT를 지원하지 않는다고 하니 안타까울 뿐이다.
8. 소스
이 소스는 Windows 8, Visual Studio 11에서만 실행이 가능하다.
|