WPF 의 xmal 을 rendering 할 때 객체들이 생성되고 loading 되는 lifecycle 을 정리한다.
WPF에서는 Xaml Designer에서 control의 instance 를 정의하고 이를 running 시에 LoadComponent 를 통해서 로드하여 화면에 출력한다.
또한 Xaml에서 MVVM을 위한 binding 을 정의할 때 중요한 것은 DataContext 이다.
아직 잘 모르는 부분이 Binding의 source를 정의하는 것인데..
UserControl을 구현할 때에 DataContext를 Locator에서 정의하지 않고 control의 각각의 instance에 정의를 하기 위해서 찾은 방법을 나의 기준으로 xmal 구현 방법과 behind code 구현 방법으로 비교하여 정리하고자 한다.
우선 wpf의 Lifecycle이 중요하다. Running 타임에서 어떻게 property 들의 값이 선언되고 binding 되어 sync 가 이루어지를 알아야 입맛에 맞게 변경할 수 있기 때문이다.
WPF Xaml Control Lifecycle
1. Behind Code
<.xaml 디자인 코드>
<local:SimImageView x:Name="RodView" TagName="MotionDetectRodView"
Background="#434343" BackgroundColor="Blue" SliceOffset="10"/>
<.cs behind 코드>
public SimImageControl()
{
SetDataContext();
InitizlieDataBinding();
}
protected override void SetDataContext()
{
ViewModel = new SimViewModel();
ViewModel.PipelineUpdated += ViewModel_PipelineUpdated;
DataContext = ViewModel;
}
public void InitizlieDataBinding()
{
var actorBinding = new Binding("ImageActor");
actorBinding.Source = DataContext;
actorBinding.Mode = BindingMode.OneWay;
BindingOperations.SetBinding(this, ImageActorDependency, actorBinding);
var scaleBinding = new Binding("ImageScale");
scaleBinding.Source = DataContext;
scaleBinding.Mode = BindingMode.TwoWay;
BindingOperations.SetBinding(this, ImageScaleDependency, scaleBinding);
var sliceBinding = new Binding("SliceOffset");
sliceBinding.Source = DataContext;
sliceBinding.Mode = BindingMode.OneWayToSource;
BindingOperations.SetBinding(this, SliceOffsetDependency, sliceBinding);
var interactorBinding = new Binding("InteractorStyle");
interactorBinding.Source = DataContext;
interactorBinding.Mode = BindingMode.OneWay;
BindingOperations.SetBinding(this, InteractorStyleDependency, interactorBinding);
}
Behind 방식으로 할때에는 DataContext 선언과 생성자 이전에 지정을 하고 Binding 을 모두 수행한다.
Behind 방식의 Binding 을 할때에 주의점
Behind 에서 Databinding을 수행할 경우 Binding Mode에 따른 sync 의 기준이 달라진다.
OneWayToSource 경우에는 PropertyMetadata의 초기 값을 source 즉 viewmodel의 property 에 값을 쓰게 된다.
반대의 경우에는 viewmodel의 초기값이 target에 입력되게 된다. 이럴 경우에는 xaml 에서 입력한 property 값의 호출이 어떤 시점에 발생하는지에 따라서 원하지 않는 동작을 할 수 있다.
이를 방지하기 위해서 Databiding 하는 코드가 property 업데이트 이전 시점인 생성자 구문에서 이뤄져야 한다.
2. Xaml 디자인 코드
Xaml 에서의 DataContext 생성과 Property 업데이트와 binding의 관계는 다음과 같이 진행된다.
기본적으로 xaml 은 behind 코드의 생성자가 완료된 뒤에 로드하게 된다. 또한 로드되는 node는 기술되어 있는 순서대로 차례로 이루어지며 부모 노드에서 자식 노드 순서로 진행된다.
그렇기에 1번과 2번의 상황에서 binding 하여 제어하고자 하는 property sliceoffset을 사용하고자 할 때에 1번의 property 가 업데이트 된뒤에 control의 datacontext가 변경되므로 이전에 있던 binding은 없어지며 sliceoffset의 metadata 로 초기화 된 다음에 새로 변경된 datacontext와 binding을 수행하게 된다. 그런 이유로 원하는 동작이 이뤄지지 않는다.
반면 3번과 4번의 경우에는 datacontext가 변경할 때에 viewmodel 에 값을 입력하여 생성하였기 때문에 property 의 초기값을 원하는 값으로 binding하여 설정할 수 있게 된다.
또한 주의할 점은 변경된 DataContext와 databinding을 하기 위해 control의 DataContextChanged event 에서 databinding을 behind 코드에서 연결을 해야한다.
private void SimImageControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if(e.NewValue is SimViewModel)
{
ViewModel = (SimViewModel)e.NewValue;
ViewModel.PipelineUpdated += ViewModel_PipelineUpdated;
InitizlieDataBinding();
}
}