본문 바로가기
wpf/wpf layout 및 문법

외부 Thread에서 UIThread 제어하기(DIspatcher의 이벤트 큐 사용하기)

by devjh 2020. 10. 10.
반응형

이번게시글에서는 외부 스레드에서 UI스레드를 제어하는방법에 대해 포스팅하겠습니다.

 

예제코드입니다.

1. MainWindow.xaml

<Window x:Class="DispatcherTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:DispatcherTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="1*"></ColumnDefinition>
            <ColumnDefinition Width="1*"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Button Grid.Column="0" Height="30" Width="80" Name="myBtn" Click="myBtn_Click" Content="버튼"></Button>
        <TextBlock Grid.Column="1" Height="30" Width="80" Name="myTextBlock" Text="0" TextAlignment="Center"></TextBlock>
    </Grid>
</Window>

화면을 분할하여 Button과 TextBlock을 넣어주었습니다.

 

2. MainWindow.cs 

namespace DispatcherTest
{
    /// <summary>
    /// MainWindow.xaml에 대한 상호 작용 논리
    /// </summary>
    public partial class MainWindow : Window
    {
        Thread myThread;
        
        public MainWindow()
        {
            InitializeComponent();
        }

        private void myBtn_Click(object sender, RoutedEventArgs e)
        {
            myThread = new Thread(IncreaseNum);
            myThread.Start();
        }

        private void IncreaseNum()
        {
            for (int i = 0; i < 10; i++)
            {
                myTextBlock.Text = (i + 1).ToString();
                Thread.Sleep(300);
            }
        }
    }
}

버튼클릭시 스레드를 생성해 myTextBlock의 Text를 0.3초마다 1씩 증가시켜주도록 하였습니다.

 

 

3. 실행화면

 

System.InvalidOperationException: '다른 스레드가 이 개체를 소유하고 있어 호출한 스레드가 해당 개체에 액세스할 수 없습니다.'

 

혹은

 

Cross-thread operation not valid: Control accessed from a thread other than the thread it was created on. 

 

의 예외가 발생하며 프로그램이 종료되어버립니다.

 

UI스레드의 개체를 myThread라는 외부스레드에서 컨트롤하려고 했기때문에 생긴 예외입니다.

 

WPF의 UI를 그리는 스레드는 STA(single-thread-apartment) 스레드로

 

System.Windows.Threading.DispatcherObject를 통해 실현되어

 

UI를 그린 본인만 접근할 수 있도록 되어있습니다.

 

4. 해결방법

Dispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>
{
    myTextBlock.Text = (i + 1).ToString();
}));

UI스레드가 소유하고있는 MyTextBlock에 접근하는 로직을 해당 코드로 감싸줍니다.

 

WPF에서는 DispatcherObject로 만든 자신의 스레드(MainWindow의 UI스레드)만 내부 UI개체들에 접근하여 내용을 수정할 수 있지만

 

DIspatcher의 이벤트큐에 UI를 변경할 로직을 넣어두면 UIThread도 제어가 가능합니다.

 

따라서 위의 방식으로 Dispatcher 이벤트 큐에 해당 로직을 넣어 실행시켜줘야 합니다.

 

Dispatcher.Invoke의 첫번째 매개변수는 중요도(우선순위), 두번째 매개변수는 대리자 형식이며

 

두번쨰 매개변수에는 보통 반환형이 없는 delegate형식인 Action과 람다식을 사용하곤 합니다.

 

반응형

댓글