Wolf schmidt씨가 Loaded event timing in Silverlight에서 실버라이트와 WPF의 Loaded 이벤트 발생 시점의 차이와 이에 따른 workaround에 대해 설명했는데요, 실버라이트는 FrameworkElement.Loaded 이벤트가 템플릿이 적용되기 전에 발생하지만 WPF에서는 템플릿이 적용된 후에 발생한다고 해요.

MSDN 문서에서는 실버라이트의 Loaded 이벤트에 대해 “FrameworkElement가 레이아웃을 잡는 과정을 완료하고, 렌더링 되고, 인터랙션을 할 준비가 완료되었을 때”라고 설명하고 있는데요, 이것보다는 “FrameworkElement가 생성되고 오브젝트 트리(비주얼 트리)에 추가되었을 때 발생”이 더 적합한 설명이므로 차후 SDK에서는 설명을 변경하겠다는군요.

이 차이는 개발자들에게 혼란을 주는데요 왜 이런 차이를 주었는지에 대한 것은 모르겠어요.

여튼, 실버라이트는 Loaded 이벤트에서는 아직 템플릿이 적용되지 않은 상태이므로 템플릿이 적용된 명확한 시점을 알 수 있는 방법이 없는데요, Wolf씨는 다음과 같은 workaround를 소개하고 있어요(날림 번역 발동). 각각의 workaround는 가능성과 한계가 다르므로 상황에 맞춰서 써야겠죠.

  1. 컨트롤을 서브클래싱한다면 Loaded 이벤트를 핸들링하는 것보다 OnApplyTemplate을 오버라이드 하는 것이 낫습니다.
    OnApplyTemplate은 템플릿으로 부터 비주얼 트리를 얻었고 이제 그 비주얼들을 사용하거나 조정하려고 하는 상황을 위해 명확하게 의도된 콜백입니다.
    이 방법의 한계는 이 서브클래싱된 컨트롤을 사용하는 코드에서는 OnApplyTemplate의 정의를 바꿀 수 없고 또한 OnApplyTemplate이 호출되는 시점에 다른 추가적인 처리를 할 수 없다는 점입니다.
  2. Loaded를 사용할 수도 있습니다. 그렇지만 사용자는 첫번째 호출되는 Loaded 이벤트 핸들러에서 Control.ApplyTemplate을 명시적으로 호출해야 합니다. ApplyTemplate은 동기적인 메서드이기 때문에 이 호출이 완료된 후에는 템플릿에서 만들어진 비주얼 트리를 사용 가능한 상태가 됩니다.
    다만 ApplyTemplate을 호출하는 것은 이미 Loaded 이벤트에서 일어났던 여러가지 실버라이트의 서브시스템(렌더링, 레이아웃 등)에 중복하여 적용되지 않습니다. 이런 시도를 사용하려면 해당 엘리먼트는 FrameworkElement와는 달리 Control일 필요가 있고 실제 템플릿과 엮여 있어야 합니다. 그래서 이 상황은 보통 ContentControl이나 ItemsControl에 적합합니다. (왜냐면 ContentControl이나 ItemsControl은 항상 비주얼 트리의 결정이 템플릿이 적용된 이후에 이루어지기 때문이죠.)
  3. 또한 Loaded 이벤트 대신에 LayoutUpdated 이벤트를 이용할 수 있습니다. LayoutUpdated는 실버라이트 UI에서 컨트롤을 만드는 과정 중에서 가장 마지막에 일어나는 “오브젝트 라이프타임”이벤트입니다. 중요한 제한은 LayoutUpdated가 일련의 초기화 과정 중에서 단 한번만 발생하지는 않는다는 점입니다. LayoutUpdated는 레이아웃의 변화(예를 들어 레이아웃 안에 있는 요소가 사이즈가 변경되는 등)와 연관되어 발생할 수 있기 때문입니다. 따라서 아주 작은 프로퍼티의 변화가 있더라도, 심지어 비주얼 트리의 변화가 없더라도 이 이벤트가 발생할 수 있으므로 LayoutUpdated 이벤트가 실제로 의미 있는 변화가 있었는지를 결정하는 로직이 따로 필요할 것입니다.

그럼 제 경우는 어떻게 하느냐…

기본 컨트롤의 경우는 어쩔 수 없지만 새로 작성하는 모든 커스텀 컨트롤은 ControlBase라는 기반 클래스에서 파생되도록 하고 있어요. 그리고 이 ControlBase 클래스는 Control에서 파생되었으며 OnApplyTemplate을 좀더 기대되는 타이밍에 수행될 수 있도록 오버라이드 했어요.

 public abstract class ControlBase : Control
    {
        /// <summary>
        /// 템플릿 바인딩이 완료되었는지 여부를 설정하거나 반환합니다.
        /// </summary>
        public bool IsTemplateApplied { get; private set; }
 

        /// <summary>
        /// 템플릿 적용이 완료되었을때 발생합니다.
        /// </summary>
        public event EventHandler TemplateApplied;

        /// <summary>
        /// 기본 메서드는 적절한 이벤트 처리를 위해 숨깁니다. 오버라이딩된 메서드를 이용해야 합니다.
        /// </summary>
        public sealed override void OnApplyTemplate()
        {
           this.OnApplyTemplate(this); 

            IsTemplateApplied = true;           

            if (TemplateApplied != null)
                TemplateApplied(this, EventArgs.Empty);
        }

        /// <summary>
        /// 템플릿 적용이 완료된 시점에서 수행할 동작을 정의합니다.
        /// 기본 메서드는 구현 내용이 없습니다.
        /// </summary>
        protected virtual void OnApplyTemplate(object sender)
        {
        }
    }

사실 또 한가지 의문이라면 OnApplyTemplate이란 메서드가 도대체 왜 public인가인데요, 이 부분은 좀 더 공부를 해 봐야 겠네요.

정리하자면, Custom Control을 사용할 때엔 템플릿이 적용되는 시점 제어를 세심하게 해야 한다는거죠.

저작자 표시 동일 조건 변경 허락
신고
Posted by gongdo


티스토리 툴바