2020년 8월 14일 금요일

(C#, WPF동영상강좌)WPF이벤트, 라우팅(RoutedEvent), 버블링(Bubbling), 터널링(Tunneling) 이벤트

 

(C#, WPF동영상강좌)WPF이벤트이벤트의 라우팅(RoutedEvent), 버블링(Bubbling) 이벤트터널링(Tunneling이벤

 http://ojc.asia/bbs/board.php?bo_table=WPF&wr_id=141


https://youtu.be/jf0j3biwolk




이벤트 라우팅은 어떤 이벤트가 컨트롤의 하위 또는 상위로 전달되는 것을 이야기 하며 WPF에서 광범위하게 이용되는 방법입니다.

 

n  이벤트 라우팅은 세가지로 분류할 수 있는데 이벤트가 발생했을 때 현재 컨트롤에서 상위로 올라가면서 전달되는 경우를 버블링 이벤트(Bubbleing Event)”라고 하고 반대로 자식 컨트롤로 전달되는 경우를 터널링(Tunneling Event) 이벤트라고 합니다하나의 엘리먼트에서만 발생하게 된다면 다이렉트 이벤트(Direct Event)라고 합니다.

 

n  터널링 이벤트의 경우 접두사로 preview를 붙이는데, PreviewMouseDown, PreviewDragDown 등으로 쓰여지는 것들을 Tunneling 이벤트라고 이해하면 됩니다.

 

n  터널링 이벤트는 최상위 부모 컨트롤로부터 자기자신까지 이벤트가 진행되는 것인데이벤트가 자식 요소에게 전달되기 전에 부모의 이벤트가 먼저 호출되므로 부모 이벤트가 먼저 호출할 수 있는 기회를 제공하며자식의 이벤트 발생을 막거나 자식이 벤트 처리 전에 부모 요소가 무엇인가 수행할 필요가 있는 경우에 유용 합니다.

 

0d2077c16809810d445dcb277f0fa7d8_1597449
 

 

n  이벤트 핸들러 메소드는 RoutedEventArgs 매개변수를 가지는데 Source 속성을 통해 실제 이벤트를 발생시킨 요소에 대한 참조를 제공하며이 속성은 여러 요소에서 발생한 이벤트를 동일한 방법으로 처리하고자 할 때 특히 유용합니다.

 

<Border Height="50" Width="300" BoderBrush="Gray" BorderThickness="1">

<StackPanel Background="LightGray" Orientation=

"Horizontal" Button.Click="CommonClickHandler">

    <Button Name="YesButton" Width="Auto" >Yes</Button>

    <Button Name="NoButton" Width="Auto" >No</Button>

    <Button Name="CancelButton" Width="Auto" >Cancel</Button>

  </StackPanel>

</Border>

 

//sender는 이벤트를 등록한 객체

//e.Source는 이벤트 발생 객체

private void CommonClickHandler(object sender, RoutedEventArgs e)

{

  FrameworkElement feSource = e.Source as FrameworkElement;

  switch (feSource.Name)  //버튼의 Name 속성의 값

  {

    case "YesButton":

      // do something here ...

      break;

    case "NoButton":

      // do something ...

      break;

    case "CancelButton":

      // do something ...

      break;

  }

}

 

 

n  WPF의 라우팅된 이벤트에서 object sender는 이벤트를 등록한 객체, RoutedEventArgs의 Source 속성은 실제 이벤트를 발생시킨 객체를 의미합니다.

n  WPF의 이벤트 라우팅 모델은 자동으로 이벤트를 상위 객체로 라우팅 시켜줍니다.(버블링 이벤트)

 

 

n  비주얼 스튜디오 -> WPF 응용프로그램 프로젝트명 : EventRoutingTest

n  MainWindow.xaml

 

<Window x:Class = "WPFRoutedEvents.MainWindow"

   xmlns = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x = "http://schemas.microsoft.com/winfx/2006/xaml"

   Title = "MainWindow" Height = "450" Width = "604"

   ButtonBase.Click  = "Window_Click" >

    <Grid>

        <StackPanel Margin = "20" ButtonBase.Click = "StackPanel_Click">

            <StackPanel Margin = "10">

                <TextBlock Name = "txt1" FontSize = "18" Margin = "5" Text = "This is a TextBlock 1" />

                <TextBlock Name = "txt2" FontSize = "18" Margin = "5" Text = "This is a TextBlock 2" />

                <TextBlock Name = "txt3" FontSize = "18" Margin = "5" Text = "This is a TextBlock 3" />

            </StackPanel>

            <Button Margin = "10" Content = "Click me" Click = "Button_Click" Width = "80"/>

        </StackPanel>

    </Grid>

</Window>

 

n  MainWindow.xaml.cs

 

using System.Windows;

 

namespace WPFRoutedEvents

{

    public partial class MainWindow : Window

    {

        public MainWindow()

        {

            InitializeComponent();

        }

 

        private void Button_Click(object sender, RoutedEventArgs e)

        {

            txt1.Text = "Button is Clicked";

        }

 

        private void StackPanel_Click(object sender, RoutedEventArgs e)

        {

            txt2.Text = "Click event is bubbled to Stack Panel";

        }

 

        private void Window_Click(object sender, RoutedEventArgs e)

        {

            txt3.Text = "Click event is bubbled to Window";

        }

    }

}

 

예제에서 버튼을 클릭시 클릭 이벤트가 버블링 되어 상위 객체로 라우팅 됩니다즉 버튼 클릭 이벤트 실행 -> 스택 패널 클릭 이벤트 실행 -> 윈도우 클릭 이벤트 순서로 됩니다.

 

n  실행화면

 

0d2077c16809810d445dcb277f0fa7d8_1597449
 

 

n  상위 객체로 라우팅 되는 것을 막으려면 RoutedEventArgs의 Handled 속성을 true로 설정하면 된다버튼 클릭 이벤트에 “e.Handled = true” 라고 하면 버튼까지만스택 패널 클릭 이벤트에 “e.Handled = true” 라고 한다면 스택 패널 까지만 이벤트가 전달됩니다.

n  Preview 이벤트에 Handled 속성을 지정하면 Preview 이벤트의 터널링이 중단될 뿐만아니라 버블링 이벤트의 발생도 중단 됩니다.

 

private void StackPanel_Click(object sender, RoutedEventArgs e) {

   txt2.Text = "Click event is bubbled to Stack Panel";

   e.Handled = true;

}

 

 

n  이번에는 Tunneling Event에 대해 실습을 해보자.

n  MainWindow.xaml

<Window x:Class="EventRoutingTest.MainWindow"

        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        Title="Events Demo" Height="215" Width="343"

        PreviewMouseDown="Window_PreviewMouseDown">

    <StackPanel Height="120" HorizontalAlignment="Center"

                    VerticalAlignment="Center" Width="200"

                    Background="Transparent"                    

                    PreviewMouseDown="StackPanel_PreviewMouseDown">

            <Border Margin="20" Padding="5" Background="LightYellow"

          BorderBrush="SteelBlue" BorderThickness="3,5,3,5" CornerRadius="3"

          VerticalAlignment="Top">

                <Button  Margin ="20" Height="50" Width="50"

                     PreviewMouseDown="Button_PreviewMouseDown"/>

            </Border>

        </StackPanel></Window>

 

n  MainWindow.xaml.cs

using System.Windows;

 

namespace EventRoutingTest

{

 

    public partial class MainWindow : Window

    {

        string mouseActivity = string.Empty;

 

        public MainWindow()

        {

            InitializeComponent();

        }

 

        private void Button_PreviewMouseDown(object sender, RoutedEventArgs e)

        {

            mouseActivity = "PreviewMouseDown Button \n";

            MessageBox.Show(mouseActivity);

        }

 

         private void StackPanel_PreviewMouseDown(object sender, RoutedEventArgs e)

        {

            mouseActivity = "PreviewMouseDown StackPanel \n";

            MessageBox.Show(mouseActivity);

        }

 

        private void Window_PreviewMouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)

        {

            mouseActivity = "PreviewMouseDown Window \n";

            MessageBox.Show(mouseActivity);

        }

 

    }

}

 

n  실행화면

0d2077c16809810d445dcb277f0fa7d8_1597449
 

 

n  Window -> StackPanel -> Button의 구조를 가지며 터널링 이벤트는 이벤트명 앞에 Peview가 붙습니다.

n  만약 버튼에서 클릭하면 Window -> StackPanel -> Button의 PreviewMouseDown 이벤트가 호출되고 StackPanel에서 클릭하면 Window -> StackPanel의 PreviewMouseDown 이벤트가 호출됩니다즉 최상위 객체에서 클릭한 객체까지 이벤트가 내려가면서 라우팅 되는 것입니다.

2020년 7월 26일 일요일

(자바스크립트/모바일카메라/웹캠/바코드스캔/손전등제어/FlashLight)QuaggsJS/JavaScript기반 Barcode Scan, SpringBoot, JSP 예제

(자바스크립트/모바일카메라/웹캠/바코드스캔/손전등제어/FlashLight)QuaggsJS/JavaScript기반 Barcode Scan, SpringBoot, JSP 예제

 

- QuaggsJS를 이용하여 스프링부트, JSP에서 작성한 예제 입니다.

- 이전에 올려드린 바코드 스캔 기능에 휴대폰 손전등(FlashLight)을 제어하는 기능이 추가 되었습니다.

- 본 예제에서는 Code_93 바코드를 대상으로 하였습니다. (QuaggsJS에서는 추가로 다양한 형태의 바코드를 지원 합니다.)

- 바코드 스캔을 할때는 휴대폰 손전등이 켜지고 스캔이 끝나면 손전등이 꺼집니다.

- PC의 크롬 웹브라우저, 스마트폰/안드로이드기반 PDA 크롬에서 테스트 하였습니다.

 

1. JavaScript 기반 웹페이지에서 바코드를 인식하기 위한 quaggaJS 라이브러리를 다운받자.

 

https://serratus.github.io/quaggaJS/ 

 

ZIP 파일을 다운받아서 압축을 풀자.

 

 

2. 스프링부트로 demo 라는 이름의 플젝을 생성하고 패키지를 com.example.demp 라고 하자.

 

3. DemoApplication.java

package com.example.demo;

 

import org.apache.catalina.connector.Connector;

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;

import org.springframework.boot.web.servlet.server.ServletWebServerFactory;

import org.springframework.context.annotation.Bean;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RestController;

 

@SpringBootApplication

public class DemoApplication {

 

public static void main(String[] args) {

SpringApplication.run(DemoApplication.class, args);

}

@Bean

    public ServletWebServerFactory serveltContainer(){

        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();

        tomcat.addAdditionalTomcatConnectors(createStandardConnector());

        return tomcat;

    }

 

    private Connector createStandardConnector(){

        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");

        connector.setPort(8080);

        return connector;

    }

 

}



4. MainController.java

package com.example.demo;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
 
@Controller
public class MainController {
 
    @RequestMapping("/")
    public String jsp() throws Exception {
        return "main";
    }
}


5. src/main 아래에 webapp/WEB-INF/jsp 폴더 생성

6. jsp폴더에 main.jsp 작성

<!DOCTYPE html>
<html lang="en">

<head>
    <title></title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style>
        /* In order to place the tracking correctly */
        canvas.drawing, canvas.drawingBuffer {
            position: absolute;
            left: 0;
            top: 0;
        }
    </style>
</head>

<body>
    <!-- Div to show the scanner -->
    <div id="scanner-container"></div>
    <input type="button" id="btn" value="Start/Stop the scanner" />

    <!-- Include the image-diff library -->
    <script src="/js/quagga.js"></script>

    <script>
        var _scannerIsRunning = false;

        function startScanner() {            
            Quagga.init({
                inputStream: {
                    name: "Live",
                    type: "LiveStream",
                    target: document.querySelector('#scanner-container'),
                    constraints: {
                        width: 640,
                        height: 480,
                        facingMode: "environment"                            
                    },
                },
                decoder: {
                    readers: [
                        "code_93_reader"
                    ],
                    debug: {
                        showCanvas: true,
                        showPatches: true,
                        showFoundPatches: true,
                        showSkeleton: true,
                        showLabels: true,
                        showPatchLabels: true,
                        showRemainingPatchLabels: true,
                        boxFromPatches: {
                            showTransformed: true,
                            showTransformedBox: true,
                            showBB: true
                        }
                    }
                },

            }, function (err) {
                if (err) {
                    console.log(err);
                    return
                }

                console.log("Initialization finished. Ready to start");
                Quagga.start();

                // Set flag to is running
                _scannerIsRunning = true;
            });
            
           
            Quagga.onProcessed(function (result) {

             var track = Quagga.CameraAccess.getActiveTrack();
                track.applyConstraints({advanced: [{torch:true}]}); //Torch is on
                
                var drawingCtx = Quagga.canvas.ctx.overlay,
                drawingCanvas = Quagga.canvas.dom.overlay;

                if (result) {
                    if (result.boxes) {
                        drawingCtx.clearRect(0, 0, parseInt(drawingCanvas.getAttribute("width")), parseInt(drawingCanvas.getAttribute("height")));
                        result.boxes.filter(function (box) {
                            return box !== result.box;
                        }).forEach(function (box) {
                            Quagga.ImageDebug.drawPath(box, { x: 0, y: 1 }, drawingCtx, { color: "green", lineWidth: 2 });
                        });
                    }

                    if (result.box) {
                        Quagga.ImageDebug.drawPath(result.box, { x: 0, y: 1 }, drawingCtx, { color: "#00F", lineWidth: 2 });
                    }

                    if (result.codeResult && result.codeResult.code) {
                        Quagga.ImageDebug.drawPath(result.line, { x: 'x', y: 'y' }, drawingCtx, { color: 'red', lineWidth: 3 });
                    }
                }
            });


            Quagga.onDetected(function (result) {
                console.log("Barcode detected and processed : [" + result.codeResult.code + "]", result);
                alert("Barcode detected and processed : [" + result.codeResult.code + "]")
                var track = Quagga.CameraAccess.getActiveTrack();
                track.applyConstraints({advanced: [{torch:false}]}); //Torch is off
            });
        }


        // Start/stop scanner
        document.getElementById("btn").addEventListener("click", function () {
            if (_scannerIsRunning) {
             var track = Quagga.CameraAccess.getActiveTrack();
                track.applyConstraints({advanced: [{torch:false}]}); //Torch is off
                Quagga.stop();                
            } else {
                startScanner();
            }
        }, false);
    </script>
</body>

</html>

7. webapp 아래 js 폴더에 다운받은 quaggaJS dist 폴더의 quagga.js를 복사, src 폴더의 analytics, common, ,,,,, reader 모든 폴더를 js 아래로 복사

1f7ba28fb9535b4a5e06862a8b1d87e9_1595665
 

8. 자바스크립트기반 quagga 에서 바코드 스캔이 가능하려면 https 로 웹서비스가 기동되어야 하는데 스프링부트에서는 커맨드창에서 workspace아래 demo 프로젝트 폴더로 이동하여 다음명령을 실행하여 서버 인증서를 만들자.

 

C:\bpr\workspace\demo>keytool -genkey -alias springbootssl -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 4000

 

키 저장소 비밀번호 입력:

이름과 성을 입력하십시오.

  [Unknown]:  jclee

조직 단위 이름을 입력하십시오.

  [Unknown]:  jclee

조직 이름을 입력하십시오.

  [Unknown]:  jclee

구/군/시 이름을 입력하십시오?

  [Unknown]:  seoul

시/도 이름을 입력하십시오.

  [Unknown]:  seoul

이 조직의 두 자리 국가 코드를 입력하십시오.

  [Unknown]:  kr

CN=jclee, OU=jclee, O=jclee, L=seoul, ST=seoul, C=kr이(가) 맞습니까?

  [아니오]:  y

 

9. src/main/resource 아래의 application.properties 파일

 

spring.mvc.view.prefix=/WEB-INF/jsp/

spring.mvc.view.suffix=.jsp

server.ssl.key-store=keystore.p12

server.ssl.key-store-type=PKCS12

server.ssl.key-store-password=tatata01

server.ssl.key-alias=springbootssl

server.port=8443

 

10. DemoApplication.java에서 Ctrl + F11 로 스프링부트 프로젝트를 시작

 

 

  .   ____          _            __ _ _

 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \

( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \

 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )

  '  |____| .__|_| |_|_| |_\__, | / / / /

 =========|_|==============|___/=/_/_/_/

 :: Spring Boot ::        (v2.3.1.RELEASE)

 

2020-07-25 17:07:13.065  INFO 4888 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication on DESKTOP-CMQ34NP with PID 4888 (C:\bpr\workspace\demo\target\classes started by user in C:\bpr\workspace\demo)

2020-07-25 17:07:13.068  INFO 4888 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default

2020-07-25 17:07:13.856  INFO 4888 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8443 (https) 8080 (http)

2020-07-25 17:07:13.873  INFO 4888 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]

2020-07-25 17:07:13.874  INFO 4888 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.36]

2020-07-25 17:07:14.097  INFO 4888 --- [           main] org.apache.jasper.servlet.TldScanner     : At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.

2020-07-25 17:07:14.102  INFO 4888 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext

2020-07-25 17:07:14.102  INFO 4888 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 994 ms

2020-07-25 17:07:14.260  INFO 4888 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'

2020-07-25 17:07:15.237  INFO 4888 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8443 (https) 8080 (http) with context path ''

2020-07-25 17:07:15.246  INFO 4888 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 2.518 seconds (JVM running for 3.308)

2020-07-25 17:07:23.626  INFO 4888 --- [nio-8443-exec-5] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring DispatcherServlet 'dispatcherServlet'

2020-07-25 17:07:23.626  INFO 4888 --- [nio-8443-exec-5] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'

2020-07-25 17:07:23.631  INFO 4888 --- [nio-8443-exec-5] o.s.web.servlet.DispatcherServlet        : Completed initialization in 5 ms

 

11. localhost:8080 형태는 https가 아니더라도 실행이 되며 휴대폰/PDA IP형태의 접근은 https로 접근해야 한다.

 

웹브라우저에서 확인은 http://localhost:8080 으로 접근하여 확인하자.

(여러가지 바코드 종류중 code_93 바코드로 테스트 했구요, quggaJS에서는 여러가지 형태를 지원합니다.)

 

1f7ba28fb9535b4a5e06862a8b1d87e9_1595666
12. PDA, 또는 스마트폰에서는 https://192.168.0.5:8443 으로 접근해서 확인하자. (제 웹서버 아이피가 192.168.0.5 입니다. 휴대폰설정, 사이트설정에서 카메라 접근 허용해야 하구요~)

 

1f7ba28fb9535b4a5e06862a8b1d87e9_1595666 

 

 

(C#교육동영상)C# ADO.NET 실습 ODP.NET/ODAC 설치 오라클 함수 호출 실습, C#학원, WPF학원, 닷넷학원, 자바학원

  (C#교육동영상)C# ADO.NET 실습  ODP.NET/ODAC 설치  오라클 함수 호출 실습, C#학원, WPF학원, 닷넷학원, 자바학원 https://www.youtube.com/watch?v=qIPU85yAlzc&list=PLxU-i...