본문 바로가기

silverlight

Lambda Expressions in Silverlight #2

Lambda Expressions in Silverlight #1 포스팅과 이어지는 내용입니다.

 구체적인 예를 한 가지 들어보자면, Image Control을 배치 한 뒤 ImageOpened 되면 Image Control을 MouseLeftButtonUp 했을 때, 마우스의 포인트를 ListBox에 추가하는 기능을 구현한다고 가정 해 보겠습니다.
 위와 같이 구현하기 위해서는 이미지가 반드시 Opened 되야 만 MouseLeftButtonUp 이 발생하여야 하도록 하기 때문에 ImageOpened 이벤트 구현부에서 MouseLeftButtonUp 이벤트를 구현 한다면, 무리 없이 모든 기능을 구현할 수 있습니다.
int count = 1;
this.LogoImage.ImageOpened += (s, e) =>
{
    this.MousePositionListBox.Items.Clear();
    this.LogoImage.MouseLeftButtonUp += (obj, args) =>
    {
        this.MousePositionListBox.Items.Add(String.Format("{0} , Point : {1}",
            (count++).ToString(), args.GetPosition(this.LogoImage)));
    };
};

 아무 문제 없이 쉽게 구현 한 것 같지만, 여기에는 한 가지 중요한 문제가 존재합니다. 바로 Image Control의 Source가 변경 되는 경우, ImageOpened 가 또 일어나게 된다는 점이죠. 그렇게 되면, MouseLeftButtonUp 이벤트는 Image Control 의 Source가 변경 될 때마다 이벤트 구현부분인 람다식의 구현부가 계속 추가 되겠죠. 그럼 MousePositionListBox에는 Image Control이 변경 된 만큼 뜨게 됩니다. 

 이런 경우를 막기 위해서, 보통 람다식을 떼고 아래와 같이 코드를 입력하여 방지 할 수 있는데요,
Int32 count = 1; 

public MainPage()
{
    InitializeComponent();
 
    bool firstImageOpened = true;
    this.LogoImage.ImageOpened += (s, e) =>
    {
        this.MousePositionListBox.Items.Clear();
        this.count = 1;

        if (firstImageOpened)
        {
            this.LogoImage.MouseLeftButtonUp += new MouseButtonEventHandler(LogoImage_MouseLeftButtonUp);
            firstImageOpened = false;
        }
        else
        {
            this.LogoImage.MouseLeftButtonUp -= new MouseButtonEventHandler(LogoImage_MouseLeftButtonUp);
            this.LogoImage.MouseLeftButtonUp += new MouseButtonEventHandler(LogoImage_MouseLeftButtonUp);
        }
    };

void LogoImage_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    this.MousePositionListBox.Items.Add(String.Format("{0} , Point : {1}",        (count++).ToString(), e.GetPosition(this.LogoImage)));
}


 우선 count 변수를 맴버 변수로 바꿔야 했고, ImageOpened 가 처음 일어나는 것인지 여부를 체크하여 동일한 이벤트 구현부를 추가 또는 삭제 하는 작업을 추가했습니다. 이렇게 되면, Image Control의 Source가 변경 되어 ImageOpened 될 때 MouseLeftButtonUp에 등록되는 함수는 단 하나만 되게 됩니다.

 미리 예상을 하고 람다식을 사용하지 않았다면 상관이 없지만, 이미 사용한 뒤에 나중에 이런 문제를 발견한다면 (그나마 발견하면 다행이겠죠) 앞서 설명을 드렸던 Variable Lifting이 쓰인 경우 람다식을 떼어 내기가 여간 쉬운 것이 아닙니다. 람다식 블럭 내에서 사용 된 모든 변수를 맴버로 떼어내던가 하는 귀찮고, 정교한 작업을 해야 하기 때문이죠.

 그러나, 이런 점을 Variable Lifting을 그대로 이용하여 적용할 수 있는 방법이 있습니다. 람다식의 다른 표현을 사용하는 것이죠 :)
bool firstImageOpened = true;
MouseButtonEventHandler mouseHandler = null;
this.LogoImage.ImageOpened += (s, e) =>
{
    this.MousePositionListBox.Items.Clear();
    int count = 1;
 
    if (firstImageOpened)
    {
        mouseHandler = delegate(object sender, MouseButtonEventArgs args)
        {
            this.MousePositionListBox.Items.Add(String.Format("{0} , Point : {1}",
                (count++).ToString(), args.GetPosition(this.LogoImage)));
        };
        this.LogoImage.MouseLeftButtonUp += mouseHandler;
        firstImageOpened = false;
    }
    else
    {
        this.LogoImage.MouseLeftButtonUp -= mouseHandler;
        this.LogoImage.MouseLeftButtonUp += mouseHandler; 
    }
};