2Bbear's knowledge workshop

WPF 라이프사이클

Code/C#2019. 4. 7. 22:24

MSDN에서 제공하는 WPF 어플리케이션의 라이프 사이클에 대한 그림이다.
MSDN에서 제공하는 Window의 라이프사이클에 대한 그림이다.

window의 경우 화면이 출력되는 지점이 ContentRendered 이후이기 때문에 Window안에 있는 Control에 접근하려고 할때는 ContentRendered 이후에 접근하는 것이 좋을 것이다.

Deactivated는 화면에 사용자가 상호작용 한 이벤트들이 있기 때문에 바로 Window가 생성된 이후에 바로 실행되게 하고 싶다면 Deactivated에 코드를 작성하면 안된다.

목표 :

간단하게 [작업] , [로딩바 형태] ,[로딩바 출력 위치] ,[실제 좌표 x ,y] 이 값 만으로 간단하게 작업 을 처리하는 도중에 로딩바가 출력되는 기능을 만들려고한다.

 

미리보는 형태 :

-호출 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
LoadingWindow loadingWindow = new LoadingWindow(
    new LoadingUserControl() ,
    ()=>{
            int a =0;
            for(int i =0;i<10;i++)
            {
                a+=1;
                Thread.Sleep(100);
            }
            return a.ToString();
        } ,
        (object  taskResult)=>{
            this.Tilte=(string) taskResult;
            return true;
        } ,
        this.Top + (this.Height /2-100 ,
        this.Left +(this.Width /2 )-100
        );
 
loadingWindow.ShowDialog(); 
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs

이런 식으로 (로딩바컨트롤, 작업, UI출현작업, 출현위치 y, 출현위치 x)

 

======

-흐름

async를 이용하여 Data를 비동기적으로 처리하고 await을 통해 데이터가 끝나는 것을 기다린다. 그 사이에 로딩바는 계속 출력되고 있는 상태인 것이다.

자동적으로 await이 끝나자마자 다시 MainWindow의 UI를 수정하게 하여 데이터가 적용되게 한다. 

그 사이에 MainWindow에는 어떠한 이벤트도 발생하면 안되기 때문에 Loading을 Modal로 출력시킨다.

따라서 MainWindow는 잠기게 되고 안전하게 작업을 처리 할 수 있게된다.

 

-코드

LoadingControl.xaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<UserControl x:Class="기는 알아서 해주세여"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             
             >
    <Grid HorizontalAlignment="Center" VerticalAlignment="Center" >
        <Image x:Name="imgLoading" HorizontalAlignment="Center" Height="130" VerticalAlignment="Center" Width="130" Source="Images/lodingLogo001.png"/>
        <Label Content="Loading" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="20" FontWeight="Bold" />
    </Grid>
</UserControl>
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs

LoadingControl.xaml.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
 
namespace 이건 알아서 해주세여
{
    /// <summary>
    /// </summary>
    public partial class LoadingControl : UserControl
    {
        public LoadingControl()
        {
            InitializeComponent();
            // 애니메이션 설정
            DoubleAnimation dba1 = new DoubleAnimation();  // 애니메이션 생성
            dba1.From = 0;   // start 값
            dba1.To = 360;   // end 값
            dba1.Duration = new Duration(TimeSpan.FromSeconds(1));  // 1.5초 동안 실행
            dba1.RepeatBehavior = RepeatBehavior.Forever;  // 무한 반복
 
            RotateTransform rt = new RotateTransform();
            imgLoading.RenderTransform = rt;
            rt.CenterX += imgLoading.Width / 2;
            rt.CenterY += imgLoading.Height / 2;
 
            rt.BeginAnimation(RotateTransform.AngleProperty, dba1);   // 변경할 속성값, 대상애니매이션
            
        }
    }
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs

 

LoadingWindow.xaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<Window x:Class=여기는 알아서 해주세여"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             WindowStyle="None"
             AllowsTransparency="True"
             ContentRendered="Window_ContentRendered"
            Width="300" Height="300"
        >
    <Window.Background>
        <SolidColorBrush Opacity="0" Color="White"/>
    </Window.Background>
    <ContentControl Name="LoadingImageContent">
        
    </ContentControl>
</Window>
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs

 

Loadingwindow.xaml.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
 
namespace 여기는 알아서 해주세여
{
    /// <summary>
    /// </summary>
    public partial class LoadingWindow : Window
    {
        Func<object> taskMethod;
        Func<objectbool> logicLambda;
        /// <summary>
        /// 작업을 하면서 로딩창이 뜨는 생성자
        /// <param name="_loadingControl"> 어떠한 로딩화면을 출력할 것인지</param>
        /// <param name="_taskMethod"> UI에 간섭하지 않고 시간이 오래걸리는 작업을 할당해야함</param>
        /// <param name="_logicLambda"> _taskMethod에서 나온 결과값을 어떻게 UI로 처리 할 것인지</param>
        /// <param name="_windowTop"> 해당 화면이 출력될 Y위치</param>
        /// <param name="_windowLeft"> 해당 화면이 출력될 X위치</param>
        public LoadingWindow(ContentControl _loadingControl,Func<object> _taskMethod, Func<objectbool> _logicLambda,double _windowTop, double _windowLeft)
        {
            InitializeComponent();
            LoadingImageContent.Content = _loadingControl;
            taskMethod = _taskMethod;
            logicLambda = _logicLambda;
            this.Top = _windowTop;
            this.Left = _windowLeft;
        }
 
        private async void DoTask(Func<object> _taskMethod, Func<objectbool> logicLambda)
        {
            Task<object> task1 = Task<object>.Run(_taskMethod);
            logicLambda(await task1);
            Close();
        }
 
 
        private void Window_ContentRendered(object sender, EventArgs e)
        {
            DoTask(taskMethod, logicLambda);
        }
    }
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs

MainWindow.xaml.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
/// <summary>
        /// 로딩바가 출력되는지 테스트하는 코드, 로딩바를 테스트하고 싶다면 해당 메소드를 호출하기만 하면 됩니다.
        /// </summary>
        private void TestLoading()
        {
            Config.CommonWindow.Loading.LoadingWindow loadingWindow=new Config.CommonWindow.Loading.LoadingWindow(
                new NH80.Config.CommonWindow.Loading.LoadingControl()
                , ()=>{
                    int a = 0;
 
                    for (int i = 0; i < 50; i++)
                    {
                        a += i;
                        Thread.Sleep(100);
                    }
                    return a.ToString();
                }
                ,(object taskResult)=>{
                    this.Title = (string)taskResult; //task의 결과 값이 특정한 Type일때 해당 Type으로 강제 형변환 해야함
                    return true;
                }
                , this.Top + (this.Height / 2- 100
                , this.Left + (this.Width / 2- 100
                );
 
            loadingWindow.ShowDialog();
        }
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs

-설명

Main에서는 간단하게 LoadingWindow를 생성하고 showdialog를 호출하면 끝난다.

다만 생성하는 과정에서 

1 param : 어떤 로딩 화면을 출력 시킬 것인지

2 param : 어떤 작업을 실행 시킬 것인지. ui에 간섭하면 안된다. 오직 data만 다루는 작업이어야 한다. 간단하게 메소드 이름만 넣어도 충분하다. 반환형이 void 인 것은 안된다. 

3 param : UI에 적용되게 UI에 간섭하는 코드를 작성한다.

4 param : 로딩바가 출력될 y 위치

5 param : 로딩바가 출력될 x위치

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<Style x:Key="TopReportCheckBox" TargetType="CheckBox">
        <Setter Property="FontSize" Value="14"/>
        <Setter Property="Foreground" Value="#6D6E71" /> <!-- 이건 옆에 글자 색상을 정함 -->
        <Setter Property="BorderBrush" Value="AliceBlue"/> <!-- 이 부분이 사각형의 색상을 바꾸는 부분인거 같다.-->
        <Setter Property="SnapsToDevicePixels" Value="True"/> <!-- 이게 뭘까....-->
        <Setter Property="OverridesDefaultStyle" Value="True"/> <!-- 기본 체크박스 스타일을 상속받겠다는 의미-->
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="CheckBox">
                    <BulletDecorator Background="Transparent"> <!-- 게시판 데코레이터를 이용한다-->
                        <BulletDecorator.Bullet>
                            <Border x:Name="Border" Width="13" Height="13" CornerRadius="0" Background="White" BorderThickness="1" BorderBrush="#FF4590FD"  >
                                <Path Width="10" Height="10"  x:Name="CheckMark" SnapsToDevicePixels="False" Stroke="#FF4590FD" StrokeThickness="2" Data="M 0 0 L 5 9 M 5 9 L 9 0" /> <!-- 이 부분이 체크 할때 생겨나는 체크 표시를 나타내는 V 표시를 그리는 부분 -->
                            </Border>
                        </BulletDecorator.Bullet>
                        <ContentPresenter Margin="4,0,0,0"  VerticalAlignment="Center" HorizontalAlignment="Left" RecognizesAccessKey="True"/>
                    </BulletDecorator>
                    <ControlTemplate.Triggers>
                        <Trigger Property="IsChecked" Value="false">
                            <Setter TargetName="CheckMark" Property="Visibility" Value="Collapsed"/>
                        </Trigger>
                        <Trigger Property="IsChecked" Value="{x:Null}">
                            <Setter TargetName="CheckMark" Property="Data" Value="M 0 7 L 7 0" />
                        </Trigger>
                        <Trigger Property="IsMouseOver" Value="true">
                            <Setter TargetName="Border" Property="Background" Value="#808080" />
                        </Trigger>
                        <Trigger Property="IsPressed" Value="true">
                            <Setter TargetName="Border" Property="Background" Value="#E0E0E0" />
                            <Setter TargetName="Border" Property="BorderBrush" Value="#606060" />
                        </Trigger>
                        <Trigger Property="IsEnabled" Value="false">
                            <Setter TargetName="Border" Property="Background" Value="#EEEEEE" />
                            <Setter TargetName="Border" Property="BorderBrush" Value="#AAAAAA" />
                            <Setter Property="Foreground" Value="#888888"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<Border Margin="0,8,16,8" CornerRadius="20">
                <Image  Source="../Resource\Images\Icons\RightIcon\그룹.png"  Width="34" Height="34">
                    <dxmvvm:Interaction.Behaviors>
                        <dxmvvm:EventToCommand EventName="MouseDown" Command="{Binding Command_GroupInfoMouseDown}"/>
                    </dxmvvm:Interaction.Behaviors>
                    <Image.ToolTip>
                        <ToolTip Content="그룹 정보" Style="{StaticResource CustomToolTip}"/>
                    </Image.ToolTip>
                </Image>
                <Border.Style>
                    <Style TargetType="Border">
                        <Setter Property="Background" Value="Transparent"/>
                        <Style.Triggers>
                            <Trigger Property="Border.IsMouseOver" Value="True">
                                <Setter Property="Background" Value="#FF8A8B8D"/>
                            </Trigger>
                        </Style.Triggers>
                    </Style>
                </Border.Style>
            </Border>
http://colorscripter.com/info#e" target="_blank" style="color:#4f4f4f; text-decoration:none">Colored by Color Scripter
http://colorscripter.com/info#e" target="_blank" style="text-decoration:none; color:white">cs

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<UserControl.Resources>
        <Style x:Key="CustomToolTip" TargetType="{x:Type ToolTip}" BasedOn="{StaticResource {x:Type ToolTip}}">
            <Setter Property="FontSize" Value="9"/>
            <Setter Property="FontFamily" Value="Noto Sans Light"/>
            <Setter Property="Foreground" Value="#ffffff"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ToolTip}" >
                        <Grid Height="30">
                            <Viewbox Stretch="Fill"  >
                                <Path Data="M 1.5,0 L 3,3 0,3 Z" Width="3" Height="5" Fill="#FF6D6E71" />
 
                            </Viewbox>
                            <Border Background="#6d6e71" CornerRadius="10" Height="20">
                                <Grid VerticalAlignment="Center">
                                    <Grid.ColumnDefinitions>
                                        <ColumnDefinition Width="8"/>
                                        <ColumnDefinition Width="1*"/>
                                        <ColumnDefinition Width="8"/>
                                    </Grid.ColumnDefinitions>
                                    <ContentPresenter Grid.Column="1" ContentSource="{Binding}"/>
                                </Grid>
                            </Border>
                            
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </UserControl.Resources>
cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            DateTime dt = DateTime.Now;
 
            string text= (e.AddedItems[0as ComboBoxItem).Content as string
            if(text==null || BottomTextBoxDate==null)
            {
                return;
            }
            if (text.Equals("이번주"))
            {
                dt = DateTime.Today.AddDays(Convert.ToInt32(DayOfWeek.Monday) - Convert.ToInt32(DateTime.Today.DayOfWeek));
                BottomTextBoxDate.Text = dt.ToString();
            }
            else if (text.Equals("이번달"))
            {
                dt = DateTime.Today.AddDays(1 - DateTime.Today.Day);
 
                BottomTextBoxDate.Text = dt.ToString();
            }
            else if (text.Equals("올해"))
            {
                dt = DateTime.Today.AddDays(1 - DateTime.Today.Day);
                dt = dt.AddMonths(1 - DateTime.Today.Month);
                BottomTextBoxDate.Text = dt.ToString();
            }
            else
            {
 
            }
        }
cs



e 매개변수를 사용합시다.

UserControl1 dialog = new UserControl1();

            Window window = new Window { Title="커스텀",

            Content=dialog};

            window.ShowDialog();


이렇게 씁시다.



예를 들어 프로젝트 구조가

위 사진처럼 CLM_RibbonView에서 다른 클래스 라이브러리에 있는 CLM_RibbonStyle.xaml을 참조 하고 싶을때 사용하는 방법입니다.


- CLM_RibbonView

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<UserControl x:Class="CLM.Views.CLM_RibbonView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:CLM.Views"
             xmlns:p="clr-namespace:CLM.Properties"
             xmlns:customstyle="clr-namespace:NH80.Config;assembly=NH80.Config"
             mc:Ignorable="d" 
             VerticalAlignment="Top"
             Height="110"
             >
    <UserControl.Resources>
        <!--<ResourceDictionary Source="../Style/CLM_RibbonStyle.xaml"/>-->
        <!--<ResourceDictionary Source="../../../NH80.Config/Style/CLM_RibbonStyle.xaml"/>-->
        <!--<ResourceDictionary  Source="/NH80.Config;../Style/CLM_RibbonStyle.xaml"/>-->
        <!--<ResourceDictionary Source="../../NH80.Config\Style\CLM_RibbonStyle.xaml"></ResourceDictionary>-->
        <ResourceDictionary Source="pack://application:,,,/NH80.Config;component/Style/CLM_RibbonStyle.xaml"/>
 
    </UserControl.Resources>
cs


먼저 UserCibtrol로 만들어진 해당 뷰는 내부적으로 상단에 ResourceDictionary를 받아 올 수 있는 코드를 추가해 주었습니다. 

주석은 저의 여러가지 시도들이구요. 

결과적으로는 pack을 이용한 uri 참조가 성공적으로 빌드 되고 프로그램에서 작동했습니다.



-CLM_RibbonViewStyle

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    >
 
    <!--Private-->
 
    <!--Public-->
    <!--Ribbon 메뉴버튼 스타일-->
    <Style x:Key="roundbutton" TargetType="{x:Type Button}">
        <Setter Property="Width" Value="60" />
        <Setter Property="Margin" Value="0" />
        <Setter Property="Height" Value="68" />
        <!--<Setter Property="Background" Value="Transparent"/>        --><!--배경색-->
        <Setter Property="Background" Value="Transparent"/>
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type Button}">
                    <Border>
                        <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="IsMouseOver" Value="True">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type Button}">
                            <Border>
                                <Grid >
                                    <Rectangle Name="m_Rectangle" Fill="#FFDDDDDD" Stroke="{x:Null}" RadiusX="5" RadiusY="5" />
                                    <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
                                </Grid>
                            </Border>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Trigger>
        </Style.Triggers>
    </Style>
 
cs



Ribbonview에서 참조해야 하는 xaml 파일은 style이 지정되어 있는 파일로써 ResourceDictionary로 만들어져 있습니다.



=============

설명


해당 클래스 라이브러리를 기본적으로 ../.. 등의 키워드로 참조하지 못하는 이유는, 어셈블리를 찾을 수 없기 때문입니다. 그렇기 때문에 Path 값을 상대적으로 지정해주면서 어셈블리를 명을 주어야 올바르게 프로그램에서 작동 할 수 있습니다.


그 과정에서 pack이라는 url 찾는 키워드를 이용하여 어셈블리를 넘겨주면서 파일을 불러왔습니다.