ViewModel의 INotifyPropertyChanged와 DependencyProperty


353

Model-View-ViewModel 아키텍처 WPF 애플리케이션에서 ViewModel을 구현할 때이를 데이터 바인딩 가능하게 만드는 두 가지 주요 선택이있는 것 같습니다. DependencyPropertyView가 바인딩 할 속성에 사용 되는 구현을 보았고 INotifyPropertyChanged대신 ViewModel이 구현되는 것을 보았습니다 .

내 질문은 언제 다른 것을 선호해야 하는가? 성능 차이가 있습니까? ViewModel 종속성을 WPF에 제공하는 것이 실제로 좋은 생각입니까? 디자인 결정시 고려해야 할 사항은 무엇입니까?


11
INotifyPropertyChanged를 구현하는 컴파일러 확인 방법 은 stackoverflow.com/questions/1329138/… 를 참조하십시오 . 속성 이름을 마술 문자열로 사용하지 마십시오.
Ian Ringrose

10
일반적으로 INotifyPropertyChanged를 구현하는 클래스의 종속성 속성과 일반 속성에는 큰 차이가 있습니다. 종속성 특성은 데이터 바인딩에서 소스 또는 대상이 될 수 있지만 INotifyPropertyChanged 지원이있는 일반 특성은 소스로만 사용할 수 있습니다. 따라서 이러한 솔루션은 완전히 호환되지 않습니다. 데이터 바인딩 인프라가 작동하려면 DP가 대상으로 필요하지만 소스는 INotifyPropertyChanged를 지원하는 일반 속성이거나 일반적인 DP 일 수 있습니다.
Mostafa Rezaei

4
.net 4.5 구현 방법은 stackoverflow.com/a/10595688/200442 를 참조하십시오 INotifyPropertyChanged.
Daniel Little

여기에 가장 잘 설명되어 있습니다 stackoverflow.com/a/3552550/366064
Bizhan

답변:


214

켄트는이 주제에 대한 흥미로운 블로그를 작성했습니다. 모델보기 : POCOs 및 DependencyObjects .

짧은 요약:

  1. DependencyObjects는 직렬화 가능으로 표시되지 않습니다
  2. DependencyObject 클래스는 Equals () 및 GetHashCode () 메서드를 재정의하고 봉인합니다.
  3. DependencyObject에는 스레드 선호도가 있습니다.이 스레드는 작성된 스레드에서만 액세스 할 수 있습니다.

나는 POCO 접근법을 선호합니다. INotifyPropertyChanged 인터페이스를 구현하는 PresentationModel (일명 ViewModel)의 기본 클래스는 http://compositeextensions.codeplex.com 에서 찾을 수 있습니다 .


24
또한 DependencyObject는 WPF 라이브러리에 의존하지만 POCO는 그렇지 않으므로 뷰 모델이 WPF를 사용할 수없는 다른 UI 스택을 구동 할 수 없습니다 (Compact Framework, Mono).
codekaizen

26
의존성 속성은 비즈니스 계층이 아닌 UI 전용으로 구축 된 것이 분명합니다.
Andrei Rînea

11
종속성 속성에는 DependencyObject 부모도 필요합니다. ViewModel은 DependencyObject에서 상속해서는 안됩니다.
Gusdor

38

WPF 성능 가이드에 따르면 DependencyObjects는 INotifyPropertyChanged를 구현하는 POCO보다 성능이 뛰어납니다.

http://msdn.microsoft.com/en-us/library/bb613546.aspx


1
나는 그것에 동의해야한다 ;-) : blog.lexique-du-net.com/index.php?post/2010/02/24/…
ANTOINE

.NET Framework 버전 4를 선택하면 링크가 계속 작동합니다. "현재 버전"에는 사용할 수 없습니다.
doubleYou

이것을 지적 해 주셔서 감사합니다. INotifyPropertyChanged가 DP보다 빠르거나 오버 헤드가 적다는 단순한 주장을하는 개발자의 많은 부당한 잘못된 정보가 있으며 이는 근거가 없습니다. DP는 가상 (데이터) 트리를 구조적으로 정의 할 수있는 빠르고 우아하며 강력한 방법입니다.
tpartee

DependencyObjects에는 숨겨진 악이 있습니다. 바인딩하는 컨트롤과 동일한 스레드에서 만들어야합니다. 그것은 GUI 스레드를 의미합니다. 즉, 해당 스레드로 작성을 디스패치해야합니다. 예를 들어 DB의 일부 백그라운드 스레드에서 이러한 것들을로드하고 만들 수는 없습니다. 당신이 창조물을 파견하지 않는 한. 미친 것 같은.
ed22

28

선택은 전적으로 비즈니스 로직과 UI 추상화 수준을 기반으로합니다. 좋은 분리를 원하지 않으면 DP가 당신을 위해 일할 것입니다.

DependencyProperties는 주로 VisualElements 수준에서 적용 할 수 있으므로 각 비즈니스 요구 사항에 대해 많은 DP를 만들면 좋지 않습니다. 또한 INotifyPropertyChanged보다 DP의 비용이 더 큽니다. WPF / Silverlight를 디자인 할 때 UI와 ViewModel을 완전히 분리하여 언제라도 레이아웃 및 UI 컨트롤을 변경할 수 있도록하십시오 (테마 및 스타일 기반).

이 게시물을 참조하십시오-https: //stackoverflow.com/questions/275098/what-applications-could-i-study-to-understand-datamodel-view-viewmodel . 링크는 Model-View-ViewModel 패턴에 대한 많은 참조를 가지고 있으며, 이는이 논의와 매우 관련이 있습니다.


9
jbe의 게시물은 차이점을보다 정확하게 대답합니다. VM (또는 Presenter)이 DependencyObject에서 상속한다고해서 스타일을 지정할 수 없거나 논리적으로 View와 분리되어 있지 않다는 것이 아니라 속성 값의 저장소가 명시 적으로 선언 된 필드와 다름을 의미합니다. 포코 스타일. 즉, 직렬화, 논리적 평등 및 스레드 선호도는 DepedencyObject 기반 VM이 처리해야하는 실제 문제입니다.
micahtan

"또한 INotifyPropertyChanged보다 DP 비용이 더 많이 듭니다"-이에 대한 증거 출처는 어디입니까? 많은 개발자들이이를지지 할 증거없이이 주장을합니다. MSDN에 따르면 사실이 아닙니다. "언제든지 레이아웃과 UI 컨트롤을 변경할 수 있도록 UI와 ViewModel을 완전히 분리 해보십시오"-다시 말하지만 POCO + PropChange와 DO / DP는 전혀 관련이 없습니다. DO / DP의 Reflection and Path 레지스트리는 시각적 측면에서 작업하는 능력을 향상시킵니다.
tpartee

20

표현력 관점에서, 나는 의존성 속성을 사용하는 것을 즐기며의 생각에 울부 짖습니다 INotifyPropertyChanged. string이벤트 구독으로 인한 속성 이름 및 가능한 메모리 누수 외에도 INotifyPropertyChanged훨씬 더 명확한 메커니즘이 있습니다.

종속성 속성은 이해하기 쉬운 정적 메타 데이터를 사용하여 "이 작업을 수행하는 경우"를 의미합니다. 우아함에 대한 투표를 얻는 것은 선언적 접근 방식입니다.


1
문자열 부분은 이제 nameof 연산자를 가진 솔루션을 갖습니다.
Newtopian

@Newtopian : 맞습니다. 로 가능한 몇 가지 흥미로운 것들도 있습니다 [CallerMemberName].
Bryan Watts

DO / DP 모델과 POCO를 사용할 때 WPF와 CLR의 풍부한 재산 등록 (반사) 혜택은 말할 것도 없습니다.
tpartee

16

INotifyPropertyChanged 사용하면 getter 및 속성 설정 코드에 더 많은 로직을 추가 할 수 있습니다.

DependencyProperty 예:

public static DependencyProperty NameProperty = DependencyProperty.Register( "Name", typeof( String), typeof( Customer ) );

public String Name
{
    set { SetValue( NameProperty, value ); }
    get { return ( String ) GetValue( NameProperty ); }
}

getter와 setter에서 --- 당신이 할 수있는 일은 단순히 SetValue와 GetValue를 각각 호출하는 것입니다. 프레임 워크의 다른 부분에서는 getter / setter가 호출되지 않고 대신 SetValue, GetValue를 직접 호출하기 때문에 속성 로직은 그렇지 않습니다. 확실하게 실행됩니다.

INotifyPropertyChanged, 이벤트를 정의합니다 :

public event PropertyChangedEventHandler PropertyChanged;

그런 다음 코드 어디에서나 논리를 가지고 다음을 호출하십시오.

// ...
// Something cool...
// ...

if( this.PropertyChanged != null )
{
    PropertyChanged( this, new PropertyChangedEventArgs( "Name" ) );
}

// More cool stuff that will reliably happen...

이것은 getter / setter 또는 다른 곳에있을 수 있습니다.


11
DependencyProperties에서도 변경 알림을받을 수 있습니다. PropertyMetadata.PropertyChangedCallback을 참조하십시오. 예 : msdn.microsoft.com/en-us/library/ms745795.aspx
Joe White

2
또한 속성 내부뿐만 아니라 어디에서나 SetValue를 호출 할 수 있습니다.
aL3891

DP가 '내부적으로'변경된 경우에도 변경 이벤트에 연결하는 여러 가지 방법이 있습니다. 그들 중 하나는 Joe White에 의해 지적되었습니다
tpartee

16

종속성 속성은 소스와 데이터 바인딩이 아닌 UI 요소에서 (대상으로) 바인딩을 지원하기위한 것입니다. 여기서는 INotifyProperty가 시작됩니다. 순수한 관점에서는 ViewModel에서 DP를 사용해서는 안됩니다.

"바인딩의 소스가 되려면 속성이 종속성 속성 일 필요는 없습니다. CLR 속성을 바인딩 소스로 사용할 수 있습니다. 그러나 바인딩의 대상이 되려면 속성이 단방향 또는 양방향 바인딩을 적용하려면 소스 속성이 바인딩 시스템 및 대상으로 전파되는 변경 알림을 지원해야합니다. 맞춤형 CLR 바인딩 소스의 경우이 속성은 INotifyPropertyChanged를 지원해야합니다. 컬렉션은 INotifyCollectionChanged를 지원해야합니다. "

모든 종속성 개체를 직렬화 할 수 없습니다 (이는 ViewModels 및 DTO (POCO) 사용을 방해 할 수 있습니다).

Silverlight 내의 DP와 WPF와의 차이가 있습니다.

http://msdn.microsoft.com/en-us/library/cc221408(v=VS.95).aspx

http://msdn.microsoft.com/en-us/library/cc903933(VS.95).aspx


2009 년부터 문제없이 직렬화 된 종속성 개체를 사용해 왔으므로 "모든 종속성 개체를 직렬화 할 수 없습니다"라고 말할 때 무엇을 말하는지 잘 모르겠습니다. 실제로 codeproject.com/Articles/61440 / ... emphess.net/2008/11/25/dependencyproperty-serialization 옵션이 많이 있습니다. 개인적으로 가장 좋아하는 것 중 하나는 모든 DP에 백업 저장소를 제공하고 직렬화 가능하게 만드는 것입니다. Google에서 검색한지 2 분 만에 좋은 간단한 예를 찾을 수는 없었지만이 방법은 확실합니다.
tpartee

7

나도이 결정을 최근에 고려해야했다.

나는 INotifyPropertyChanged 메커니즘이 상태를 복제하지 않고 기존 비즈니스 로직 프레임 워크에 GUI를 붙일 수 있기 때문에 내 요구에 더 잘 맞는다는 것을 알았습니다. 내가 사용하고있는 프레임 워크에는 자체 관찰자 패턴이 있었고 한 수준의 알림을 다음 수준으로 쉽게 전달할 수있었습니다. 비즈니스 로직 프레임 워크와 INotifyPropertyChanged 인터페이스에서 옵저버 인터페이스를 구현 한 클래스가 있습니다.

DP를 사용하면 상태를 직접 저장하는 백엔드를 정의 할 수 없습니다. .net이 바인딩하려는 모든 상태 항목의 복사본을 캐시하도록해야했습니다. 이것은 불필요한 오버 헤드처럼 보였습니다. 제 상태는 크고 복잡합니다.

따라서 비즈니스 로직에서 GUI로 속성을 노출하는 데 INotifyPropertyChanged가 더 좋습니다.

그것은 속성을 노출하고 다른 GUI 위젯에 영향을 미치기 위해 해당 속성의 변경을 위해 사용자 정의 GUI 위젯이 필요한 곳에서 DP는 간단한 해결책을 입증했습니다.

그래서 GUI에서 GUI 알림에 DP가 유용하다는 것을 알았습니다.


6

ViewModel 종속성을 WPF에 제공하는 것이 실제로 좋은 생각입니까?

.NET 4.0에는 System.Xaml.dll이 있으므로이를 사용하기 위해 임의의 프레임 워크에 종속 될 필요가 없습니다. PDC 세션에 대한 Rob Relyea의 게시물을 참조하십시오 .

내 테이크

XAML은 개체를 설명하기위한 언어이며 WPF는 설명 된 개체가 UI 요소 인 프레임 워크입니다.

이들의 관계는 논리를 설명하는 언어 인 C # 및 특정 종류의 논리를 구현하는 프레임 워크 인 .NET과 유사합니다.

XAML의 목적은 선언적 객체 그래프입니다. W * F 기술은이 패러다임의 훌륭한 후보이지만 XAML은 독립적으로 존재합니다.

XAML과 전체 종속성 시스템은 WF와 WPF에 대한 별도의 스택으로 구현되었으므로 서로 다른 팀의 경험을 활용하여 팀 간의 종속성을 만들지 않아도됩니다.


대답하면 비트 본이 XAML과 WPF를 동일하게 간주한다고 가정하는 것 같습니다. ViewModel은 논리적 분리를 늘리지 않고 코드 복잡성을 줄이고 사용자 컨트롤의 코드 숨김에서 로직을 작성하는 것과 관련된 모든 문제를 피하기 위해 가능한 한 적은 WPF 종속성을 가져야합니다. 필연적으로 ICommand와 같은 WPF 개념을 구현하고 WPF / Silverlight 만 쉽게 랩핑 할 수있는 동작을 제시합니다. 뷰 모델에서 유일한 프리젠 테이션 스레딩 문제는 CollectionViews 및 ObservableCollection이어야합니다.
Gusdor

6

종속성 속성은 사용자 지정 컨트롤을 만드는 데 사용됩니다. XAML 디자인 타임에 속성 창에 속성을 표시하기 위해 Intelli-sense를 사용하려면 종속성 속성을 사용해야합니다. INPC는 디자인 타임에 속성 창에 속성을 표시하지 않습니다.


4

버튼과 같은 사용자가 만든 컨트롤에는 종속성 속성을 사용해야합니다. XAML에서 속성을 사용하고 모든 WPF 기능을 사용하려면 해당 속성이 종속성 속성이어야합니다.

그러나 INotifyPropertyChanged를 사용하면 ViewModel이 더 좋습니다. INotifyPropertyChanged를 사용하면 필요한 경우 getter / setter 논리를 가질 수 있습니다.

INotifyPropertyChanged를 이미 구현 한 ViewModel에 대한 Josh Smith의 기본 클래스 버전을 확인하는 것이 좋습니다.

http://joshsmithonwpf.wordpress.com/2007/08/29/a-base-class-which-implements-inotifypropertychanged/

이것이 ViewModel을 수행하는 방법의 훌륭한 예라고 생각합니다.


4

DependencyProperty와 INotifyPropertyChanged는 Binding에서 두 가지 다른 용도로 사용된다고 생각합니다. 첫 번째는 속성을 바인딩 대상으로 설정하고 다른 속성에서 입력을 수신하기위한 것입니다 ({Binding ...}을 사용하여 속성 설정). 바인딩 값 (바인딩 경로 표현식의 이름)으로 속성 값을 사용하려는 경우 따라서 선택은 단지 기술적 인 것입니다.


2
두 경우 모두 INotifyPropertyChanged를 사용할 수 있습니다. TwoWay를 바인딩 할 수 있습니다. DependencyProperty는 기술적 인 이유로 View 개체에 대해 수행 된 일부 작업 (예 : XAML에서 View 개체를 인스턴스화 할 때 일부 속성 설정)에 대해서만 필요합니다. ViewModel에는 DependencyProperty가 필요하지 않습니다.
oillio

3

INotifyPropertyChanged없이 Presentation Model 에서 블로그에 대해 더 직접적인 접근 방식을 선호합니다 . 데이터 바인딩에 대한 대안을 사용하면 부기 코드없이 CLR 속성에 직접 바인딩 할 수 있습니다. View Model에서 일반 .NET 코드를 작성하면 데이터 모델이 변경 될 때 업데이트됩니다.


없이 INotifyPropertyChanged, PropertyDescriptor원인이되는, 사용되는 메모리 누수
틸락

해당 블로그 게시물에있는 Update Controls 라이브러리는 속성 설명자가 아닌 약한 참조를 사용합니다. 메모리가 누출되지 않습니다.
마이클 L 페리

1
마이클, 도서관에서 많은 코드를 생성합니다. 혜택이 보이지 않습니다. 생성 된 PropertyChanged 이벤트 호출로 모델 랩퍼를 생성하여 동일한 결과를 얻을 수 있습니다.
Der_Meister

3

선호하는 이유는 단 하나뿐입니다 DependencyObject-바인딩이 더 잘 작동합니다. 그냥 함께 예를 시도 ListBox하고 TextBox, 데이터로 채우기 목록 INotifyPropertyChanged속성 대 DependencyProperty에서 편집 현재 항목을 TextBox...


1
코드 샘플주세요
Hassan Tareq

1

속성을 다른 컨트롤에 노출하려면 종속성 속성을 사용해야합니다 ...하지만 알아내는 데 시간이 걸리기 때문에 행운을 빕니다 ...

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.