시계 Gadget 만들기 [언제나 WPF]

안녕하세요. 언제나 휴일, 언휴예요.

이번에는 작은 기능을 수행하는 도구인 Gadget을 만들어 봅시다.

Gadget(가젯)은 Widget, Winget, Webget, Appget 등의 여러가지 형태로 불리죠. 암튼 작은 기능을 수행하는 도구예요.

 

시계 가젯
시계 가젯

그림에 보시는 것처럼 가젯은 보통 타이틀 바가 없는 형태로 많이 만들죠. 그리고 딱딱한 사각형보다 타원이나 별 등의 모양 등으로 만든 것들도 있고요.

먼저 “WPF 앱 ” 프로젝트를 생성하세요.

윈도우에 타이틀 바가 없게 하려면 WindowStyle을 None으로 지정합니다. 그리고 사각 영역을 투명하게 AllowsTransparency를 True 지정하고 Background를 Transparent로 지정합니다.

Window 요소의 특성(Attribute)을 설정하세요.

<Window x:Class="시계_Gadget.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:시계_Gadget"
        mc:Ignorable="d"
        AllowsTransparency="True"
        Topmost="True"
        WindowStyle="None"
        Background="Transparent"

        Title="MainWindow" Height="200" Width="150">

 이제 창은 투명하게 안 보이는 상태입니다.

 여기에서는 타원 형태에 날짜, 시각 정보 및 닫힘 버튼을 추가할 거예요. 이러한 것들을 배치하기 위해 Grid에 Row를 6개로 설정합시다.

Grid에 Row를 추가하려면 Grid.RowDefinitions를 추가하고 내부에 RowDefinition 개체를 추가합니다.

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>

이제 Ellipse 개체를 Grid 내부에 추가합니다.

우리는 Ellipse 개체를 Grid의 전체 Row를 차지하게 할 거예요. Grid.RowSpan 값을 6으로 지정합니다.

Opacity 값을 통해 투명도(0~1)를 지정할 수 있어요.

        <Ellipse            
            Fill="LightCyan" 
                 Grid.Row="0"
                 Grid.RowSpan="5"
             Opacity="0.4" 
/>

이제 세 개의 텍스트 블록을 추가합니다. 하나는 날짜, 하나는 시각, 하나는 타이틀(광고)입니다.

HorizontalAlignment값을 Center로 지정하여 글씨가 가운데로 배치할 수 있게 하세요.

출력할 내용은 Text 속성을 이용하여 지정합니다.

        <TextBlock Grid.Row="1"
                   HorizontalAlignment="Center"
                   Foreground="Red"
                   FontWeight="Heavy">https://ehpub.co.kr
        </TextBlock>

        
        <TextBlock x:Name="tb_date" 
                   Grid.Row="2"
                   HorizontalAlignment="Center"
                   Text="yyyy-mm-dd"/>
        <TextBlock x:Name="tb_time" 
                   Grid.Row="3"
                   HorizontalAlignment="Center"
                   Text="오전 hh:mm:ss"
                   />

버튼을 하나 추가하세요. Row값은 5로 지정합시다. 버튼에 내용은 Content 특성으로 지정합니다. 그리고 버튼을 클릭했을 때 창을 닫을 수 있게 Click 이벤트 핸들러를 등록하세요.

            <Button x:Name="btn_close" 
                    Grid.Row="4"
                Content="닫기"
                    Background="LightCyan"
                    BorderBrush="Red"
                HorizontalAlignment="Center"                 
                 Click="Btn_close_Click"
                Width="40"
                    Height="30"/>

Ellipse에 마우스 드래그를 할 수 있게 MouseDown 이벤트 핸들러를 지정하세요.

창이 로드된 후에 1초 주기로 일시를 설정할 수 있게 해야 합니다. Window에도 Load 이벤트 핸들러를 지정하세요.

        <Ellipse            
            Fill="LightCyan" 
                 Grid.Row="0"
                 Grid.RowSpan="5"
             Opacity="0.4" 
             MouseDown="Ellipse_MouseDown"/>

Ellipse에 마우스 드래그를 할 수 있게 MouseDown 이벤트 핸들러를 지정하세요.

창이 로드된 후에 1초 주기로 일시를 설정할 수 있게 해야 합니다. Window에도 Load 이벤트 핸들러를 지정하세요.

<Window x:Class="시계_Gadget.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:시계_Gadget"
        mc:Ignorable="d"
        AllowsTransparency="True"
        Topmost="True"
        WindowStyle="None"
        Background="Transparent"
        Loaded="Window_Loaded"
        Title="MainWindow" Height="200" Width="150">

버튼을 클릭했을 때 창을 닫을 수 있게 Close 메서드를 호출합니다.

        private void Btn_close_Click(object sender, RoutedEventArgs e)
        {
            Close();
        }

Ellipse 영역에서 마우스 드래그 할 수 있게 DragMove 메서드를 호출합니다.

        private void Ellipse_MouseDown(object sender, MouseButtonEventArgs e)
        {
            DragMove();
        }

Load 이벤트 핸들러에서는 Timer 개체를 하나 생성합니다.

여기에서는 Windows.Threading.DispatcherTimer 개체를 사용할게요.

타이머 주기는 1초로 지정합니다.

주의할 점은 타이머 이벤트는 타이머 개체를 시작한 후 1초 뒤에 발생합니다.

Window 로드할 때 현재 일시를 설정해 주어야 1초의 공백을 피할 수 있어요.

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            SetDateTime();
            DispatcherTimer timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromSeconds(1);
            timer.Tick += Timer_Tick;
            timer.Start();
        }
        private void SetDateTime()
        {
            tb_date.Text = DateTime.Now.ToShortDateString();
            tb_time.Text = DateTime.Now.ToLongTimeString();
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            SetDateTime();
        }

이제 실행해 보면 잘 동작하는 것을 확인할 수 있어요.

테스트할 때 TextBlock 주변에 마우스 클릭하여 드래그할 수 없으니 Ellipse의 빈 공간을 통해 드래그하세요.

다음은 MainWindow.xaml 소스 내용입니다.

<Window x:Class="시계_Gadget.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:시계_Gadget"
        mc:Ignorable="d"
        AllowsTransparency="True"
        Topmost="True"
        WindowStyle="None"
        Background="Transparent"
        Loaded="Window_Loaded"
        Title="MainWindow" Height="200" Width="150">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="1"
                   HorizontalAlignment="Center"
                   Foreground="Red"
                   FontWeight="Heavy">https://ehpub.co.kr
        </TextBlock>
        <Ellipse            
            Fill="LightCyan" 
                 Grid.Row="0"
                 Grid.RowSpan="5"
             Opacity="0.4" 
             MouseDown="Ellipse_MouseDown"/>
            <Button x:Name="btn_close" 
                    Grid.Row="4"
                Content="닫기"
                    Background="LightCyan"
                    BorderBrush="Red"
                HorizontalAlignment="Center"                 
                 Click="Btn_close_Click"
                Width="40"
                    Height="30"/>
        
        <TextBlock x:Name="tb_date" 
                   Grid.Row="2"
                   HorizontalAlignment="Center"
                   Text="yyyy-mm-dd"/>
        <TextBlock x:Name="tb_time" 
                   Grid.Row="3"
                   HorizontalAlignment="Center"
                   Text="오전 hh:mm:ss"
                   />
    </Grid>
</Window>

다음은 MainWindow.cs 소스 코드 내용입니다.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

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

        private void Ellipse_MouseDown(object sender, MouseButtonEventArgs e)
        {
            DragMove();
        }

        private void Btn_close_Click(object sender, RoutedEventArgs e)
        {
            Close();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            SetDateTime();
            DispatcherTimer timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromSeconds(1);
            timer.Tick += Timer_Tick;
            timer.Start();
        }

        private void SetDateTime()
        {
            tb_date.Text = DateTime.Now.ToShortDateString();
            tb_time.Text = DateTime.Now.ToLongTimeString();
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            SetDateTime();
        }
    }
}