2008-09-28 3 views
8

유닛 테스트에서 비정상적인 문제가 있습니다. 필자가 테스트하는 클래스는 런타임에 종속성 속성을 동적으로 만들고 해당 종속성 속성의 유형은 상황에 따라 다를 수 있습니다. 단위 테스트를 작성하는 동안 다른 유형으로 종속성 특성을 작성해야하며 기존 종속성 특성을 다시 정의 할 수 없기 때문에 오류가 발생합니다.WPF 종속성 등록 정보의 등록을 취소하는 방법은 무엇입니까?

종속성 속성을 등록 취소하거나 기존 종속성 속성의 유형을 변경하는 방법이 있습니까?

감사합니다.


OverrideMetadata()를 사용하면 기본값과 같은 몇 가지 사항 만 변경할 수 있으므로 도움이되지 않습니다. AppDomain 방식은 좋은 아이디어이며 작동 할 수도 있지만 실제로는 단위 테스트를 위해 조사하기보다는 복잡해 보입니다.

의존성 속성을 등록 취소하는 방법을 찾지 못해서 단위 테스트를 조심스럽게 재구성하여 문제가 발생하지 않도록했습니다. 나는 조금 더 적은 테스트 커버리지를 얻고 있지만,이 문제는 실제 어플리케이션에서는 결코 일어나지 않을 것이고 단위 테스트 동안에 만 그것과 함께 살 수 있습니다.

도움 주셔서 감사합니다.

답변

8

난 그냥 어제 유사한 문제가 있었다 :

Label myLabel = new Label(); 
this.RegisterName(myLabel.Name, myLabel); 

우리는 쉽게 사용하여 이름 등록을 취소 할 수 있습니다 클래스 만들기. 나는이 질문을 보았고 의존성 등록을 등록 해제하는 실제 해결책이 없다는 것을 알아 차렸다. 그래서 Red Gate .NET Reflector을 사용하여 파고 들었습니다.

DependencyProperty.Register 오버로드를 살펴보면 모두 DependencyProperty.RegisterCommon 인 것으로 보입니다. 속성이 이미 DependencyProperty에 주위

DependencyProperty dp = 
    new DependencyProperty(name, propertyType, ownerType, 
    defaultMetadata, validateValueCallback); 

defaultMetadata.Seal(dp, null); 
//...Yada yada... 
lock (Synchronized) 
{ 
    PropertyFromName[key] = dp; 
} 

두 개의 센터 DependencyProperty.PropertyFromName, 해시 테이블을 등록하는

FromNameKey key = new FromNameKey(name, ownerType); 
lock (Synchronized) 
{ 
    if (PropertyFromName.Contains(key)) 
    { 
    throw new ArgumentException(SR.Get("PropertyAlreadyRegistered", 
     new object[] { name, ownerType.Name })); 
    } 
} 

번째 등록되면

먼저 확인 : 그 방법은 두 부분을 가진다. 나도 DependencyProperty.RegisteredPropertyList (ItemStructList<DependencyProperty>)을 알아 차렸다. 그러나 그것이 사용되는 곳을 보지 않고 있었다. 그러나, 안전을 위해, 나는 가능한 한 제거하려고 노력할 것이라고 생각했습니다.

그래서 종속성 속성을 "등록 취소"할 수있는 다음 코드를 작성했습니다.

private void RemoveDependency(DependencyProperty prop) 
{ 
    var registeredPropertyField = typeof(DependencyProperty). 
    GetField("RegisteredPropertyList", BindingFlags.NonPublic | BindingFlags.Static); 
    object list = registeredPropertyField.GetValue(null); 
    var genericMeth = list.GetType().GetMethod("Remove"); 
    try 
    { 
    genericMeth.Invoke(list, new[] { prop }); 
    } 
    catch (TargetInvocationException) 
    { 
    Console.WriteLine("Does not exist in list"); 
    } 

    var propertyFromNameField = typeof(DependencyProperty). 
    GetField("PropertyFromName", BindingFlags.NonPublic | BindingFlags.Static); 
    var propertyFromName = (Hashtable)propertyFromNameField.GetValue(null); 

    object keyToRemove = null; 
    foreach (DictionaryEntry item in propertyFromName) 
    { 
    if (item.Value == prop) 
     keyToRemove = item.Key; 
    } 
    if (keyToRemove != null) 
    propertyFromName.Remove(keyToRemove); 
} 

"AlreadyRegistered"예외가 발생하지 않고 테스트를 실행하기에 충분했습니다. 그러나 은 프로덕션 코드에서이 코드를 사용하지 말 것을 강력히 권장합니다. MSFT가 종속성 속성을 등록 취소하는 공식적인 방법을 선택하지 않았을 가능성이 높습니다. 그 이유는 MSFT가 문제를 묻는 것일뿐입니다.

+1

아주 좋은 일했다! 당신은 프로덕션 코드에서 사용하는 것을 원치 않지만 탐정 작업의 아주 좋은 직업입니다! –

0

나는 당신이 종속성 속성 등록을 취소 할 수 있다고 생각하지 않습니다하지만 당신은이 같은 메타 데이터를 오버라이드 (override)를 다시 정의 할 수 있습니다 : 다른 모든 실패하면

MyDependencyProperty.OverrideMetadata(typeof(MyNewType), 
        new PropertyMetadata()); 
2

, 당신은 모든 시험에 대한 새로운 응용 프로그램 도메인을 만들 수 있습니다 .

0

우리는이 같은 레이블에 대한 이름을 등록하는 경우 : 내 자신의 DependencyProperty를 테스트 할 때

this.UnregisterName(myLabel.Name); 
0

두 개의 ItemsSource 속성, HorizontalItemsSourceVerticalItemsSource이 있어야하는 Selector에서 상속되는 사용자 지정 컨트롤을 만든 시나리오에 직면했습니다.

ItemsControl 속성을 사용하지 않아도 사용자가 액세스 할 수 없도록하려고합니다.

그래서 나는 statenjason's great answer을 읽고 나에게 DP를 제거하는 방법에 대한 거대한 POV를주었습니다.
그러나 내 문제는 ItemsSourceProperty 멤버와 ItemsSourcePrivate Shadows (C#에서는 private new)으로 선언했기 때문에 MyControlType.ItemsSourceProperty을 사용하면 섀도 잉 된 변수를 참조하기 때문에 디자인 타임에로드 할 수 없다는 것이 문제였습니다.
또한 위에서 언급 한 루프 (foreach DictionaryEntry 등)를 사용할 때 반복 중에 컬렉션이 변경되었다는 예외가 발생했습니다.

그러므로 나는 DependencyProperty에이 hardcodedly 런타임에 대해 참조하는 약간 다른 접근 방식과 함께 와서이 (VB.NET, 죄송합니다)이 변경되지 그래서 컬렉션은 배열에 복사 :

Dim dpType = GetType(DependencyProperty) 
Dim bFlags = BindingFlags.NonPublic Or BindingFlags.Static 

Dim FromName = 
    Function(name As String, ownerType As Type) DirectCast(dpType.GetMethod("FromName", 
    bFlags).Invoke(Nothing, {name, ownerType}), DependencyProperty) 

Dim PropertyFromName = DirectCast(dpType.GetField("PropertyFromName", bFlags). 
    GetValue(Nothing), Hashtable) 

Dim dp = FromName.Invoke("ItemsSource", GetType(DimensionalGrid)) 
Dim entries(PropertyFromName.Count - 1) As DictionaryEntry 
PropertyFromName.CopyTo(entries, 0) 
Dim entry = entries.Single(Function(e) e.Value Is dp) 
PropertyFromName.Remove(entry.Key) 

중요 참고 : 위의 코드는 모두 사용자 지정 컨트롤의 공유 생성자에 둘러싸여 있으며 등록되어 있는지 확인하지 않아도됩니다. Selcetor의 하위 클래스에서 해당 ItemsSource dp를 제공한다는 것을 알고 있기 때문에 등록되어 있는지 확인할 필요가 없습니다.

0

다른 Datatemplates가있는 ContentPresenter에 문제가있는 경우 그 중 하나에 PropertyChangedCallback이있는 DependencyProperty가있는 경우 ContentPresenters 콘텐츠를 다른 DataTemplate으로 변경하면 콜백이 남아 있습니다. 이벤트 언로드 된 UserControls에서

은 내가 전화 :

BindingOperations.ClearAllBindings(this); 
Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.Normal, new DispatcherOperationCallback(delegate { return null; }), null); 

것은 나를 위해

관련 문제