반응형

DrawingBrush 로 정의된 DrawingGroup 을 그리게되는데

Viewport 로 화면에 표시되는 비율을 정의할 수 있으며

TileMode 를 이용해 어떻게 표시될지를 정의할 수있습니다.

 

아래 코드와 같이 사각형 안에 단순히 X 표시를 하는 Geometry 를 정의하여 도형을 체울 수 있습니다.

                <Rectangle Width="100" Height="100" Stroke="Black" StrokeThickness="1">

                    <Rectangle.Fill>

                        <DrawingBrush >

                            <DrawingBrush.Drawing>

                                <DrawingGroup>

                                    <DrawingGroup.Children>

                                        <GeometryDrawing Geometry="M0,0.1 L0.1,0 1,0.9, 0.9,1z" Brush="Blue" />

                                        <GeometryDrawing Geometry="M0.9,0 L1,0.1 0.1,1 0,0.9z" Brush="Blue" />

                                    </DrawingGroup.Children>

                                </DrawingGroup>

                            </DrawingBrush.Drawing>

                        </DrawingBrush>

                    </Rectangle.Fill>

                </Rectangle>

 

 

이를 Viewport 를 이용하여 비율을 조정할 수 있습니다.

(참고 : https://docs.microsoft.com/ko-kr/dotnet/api/system.windows.media.tilebrush.viewport?view=netcore-3.1)

 

                <Rectangle Width="100" Height="100" Stroke="Black" StrokeThickness="1">

                    <Rectangle.Fill>

                        <DrawingBrush Viewport="0,0,0.5,0.5">

                            <DrawingBrush.Drawing>

                                <DrawingGroup>

                                    <DrawingGroup.Children>

                                        <GeometryDrawing Geometry="M0,0.1 L0.1,0 1,0.9, 0.9,1z" Brush="Blue" />

                                        <GeometryDrawing Geometry="M0.9,0 L1,0.1 0.1,1 0,0.9z" Brush="Blue" />

                                    </DrawingGroup.Children>

                                </DrawingGroup>

                            </DrawingBrush.Drawing>

                        </DrawingBrush>

                    </Rectangle.Fill>

                </Rectangle>

 

값을 보면 알겠지만 ViewPort 의 뒤 두자리 수가 가로,세로 비율이라고 보면 됩니다. 0.5 이므로 반으로 줄어들어 표시가 됩니다.

 

 

이제 여기서 TileMode 를 이용해 Tile 로 정의하면 타일형태로 체워지게됩니다.

(참고 : https://docs.microsoft.com/ko-kr/dotnet/api/system.windows.media.tilebrush.tilemode?view=netcore-3.1)

 

                <Rectangle Width="100" Height="100" Stroke="Black" StrokeThickness="1">

                    <Rectangle.Fill>

                        <DrawingBrush Viewport="0,0,0.5,0.5" TileMode="Tile">

                            <DrawingBrush.Drawing>

                                <DrawingGroup>

                                    <DrawingGroup.Children>

                                        <GeometryDrawing Geometry="M0,0.1 L0.1,0 1,0.9, 0.9,1z" Brush="Blue" />                                        <GeometryDrawing Geometry="M0.9,0 L1,0.1 0.1,1 0,0.9z" Brush="Blue" />

                                    </DrawingGroup.Children>

                                </DrawingGroup>

                            </DrawingBrush.Drawing>

                        </DrawingBrush>

                    </Rectangle.Fill>

                </Rectangle>

 

 

 

아래는 위 설명한 내용을 바탕으로 만든 예시입니다.

 

<Window x:Class="DrawingBrushExample.MainWindow"

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

        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

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

        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

        mc:Ignorable="d"

        Title="MainWindow" Height="450" Width="800">

    <StackPanel>

        <StackPanel Margin="10">

            <StackPanel Orientation="Horizontal">

                <Rectangle Width="100" Height="100" Stroke="Black" StrokeThickness="1">

                    <Rectangle.Fill>

                        <DrawingBrush >

                            <DrawingBrush.Drawing>

                                <DrawingGroup>

                                    <DrawingGroup.Children>

                                        <GeometryDrawing Geometry="M0,0.1 L0.1,0 1,0.9, 0.9,1z" Brush="Blue" />

                                        <GeometryDrawing Geometry="M0.9,0 L1,0.1 0.1,1 0,0.9z" Brush="Blue" />

                                    </DrawingGroup.Children>

                                </DrawingGroup>

                            </DrawingBrush.Drawing>

                        </DrawingBrush>

                    </Rectangle.Fill>

                </Rectangle>

 

                <Rectangle Width="100" Height="100" Stroke="Black" StrokeThickness="1">

                    <Rectangle.Fill>

                        <DrawingBrush Viewport="0,0,0.5,0.5" TileMode="Tile">

                            <DrawingBrush.Drawing>

                                <DrawingGroup>

                                    <DrawingGroup.Children>

                                        <GeometryDrawing Geometry="M0,0.1 L0.1,0 1,0.9, 0.9,1z" Brush="Blue" />

                                        <GeometryDrawing Geometry="M0.9,0 L1,0.1 0.1,1 0,0.9z" Brush="Blue" />

                                    </DrawingGroup.Children>

                                </DrawingGroup>

                            </DrawingBrush.Drawing>

                        </DrawingBrush>

                    </Rectangle.Fill>

                </Rectangle>

 

                <Rectangle Width="100" Height="100" Stroke="Black" StrokeThickness="1">

                    <Rectangle.Fill>

                        <DrawingBrush Viewport="0,0,0.1,0.1" TileMode="Tile">

                            <DrawingBrush.Drawing>

                                <DrawingGroup>

                                    <DrawingGroup.Children>

                                        <GeometryDrawing Geometry="M0,0.1 L0.1,0 1,0.9, 0.9,1z" Brush="Blue" />

                                        <GeometryDrawing Geometry="M0.9,0 L1,0.1 0.1,1 0,0.9z" Brush="Blue" />

                                    </DrawingGroup.Children>

                                </DrawingGroup>

                            </DrawingBrush.Drawing>

                        </DrawingBrush>

                    </Rectangle.Fill>

                </Rectangle>

 

            </StackPanel>

        </StackPanel>

    </StackPanel>

</Window>

 

 

반응형
반응형

마우스 드래그시 사각형을 그리고 사각형 주변으로 길이를 표시합니다.

 

MainWindow.xaml

<Window x:Class="WpfApp.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:WpfApp"

        mc:Ignorable="d"

        Title="MainWindow" Height="450" Width="800" Background="Gray">

    <Grid Background="#01000000">

        <Canvas IsHitTestVisible="False" IsEnabled="False">

            <!-- Rectangle -->

            <ContentControl x:Name="RectAdder" Visibility="Collapsed">

                <ContentControl.ContentTemplate>

                    <DataTemplate>

                        <Grid >

                            <Rectangle

                                x:Name="Rect" SnapsToDevicePixels="True"

                                Fill="#770000FF" Stroke="White" StrokeThickness="1" StrokeDashArray="1 1 1 3" />

                            <!--위쪽-->

                            <Grid x:Name="DrawingHSizeAdorner">

                                <Rectangle

                                    Width="1" Height="15" Fill="White" SnapsToDevicePixels="True"

                                    IsHitTestVisible="False" IsEnabled="False"

                                    HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,-18,0,0"

                                    Tag="TopLeft"/>

                                <Rectangle

                                    Height="1" Width="{Binding ActualWidth, ElementName=Rect}"

                                    Fill="White" SnapsToDevicePixels="True"

                                    IsHitTestVisible="False" IsEnabled="False"

                                    HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,-10,0,0"

                                    Tag="TopCenter"/>

                                <Rectangle

                                    Width="1" Height="15" Fill="White" SnapsToDevicePixels="True"

                                    IsHitTestVisible="False" IsEnabled="False"

                                    HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,-18,0,0"

                                    Tag="TopRight"/>

                                <TextBlock

                                    Text="{Binding ActualWidth, ElementName=Rect, StringFormat='0.00 mm'}" Height="15" Width="{Binding ActualWidth, ElementName=Rect}"

                                    SnapsToDevicePixels="True" IsHitTestVisible="False" IsEnabled="False"

                                    HorizontalAlignment="Center" VerticalAlignment="Top"

                                    TextAlignment="Center" Foreground="White" FontSize="9"

                                    Margin="0,-25,0,0"

                                    ClipToBounds ="True"/>

                            </Grid>

                            <!--아래쪽-->

                            <Grid x:Name="DrawingBHSizeAdorner" >

                                <Rectangle

                                    Width="1" Height="15" Fill="White" SnapsToDevicePixels="True"

                                    IsHitTestVisible="False" IsEnabled="False"

                                    HorizontalAlignment="Left" VerticalAlignment="Bottom"

                                    Margin="0,0,0,-18"

                                    Tag="BottomLeft"/>

                                <Rectangle

                                    Height="1" Width="{Binding ActualWidth, ElementName=Rect}"

                                    Fill="White" SnapsToDevicePixels="True"

                                    IsHitTestVisible="False" IsEnabled="False"

                                    HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,-10"

                                    Tag="BottomCenter"/>

                                <Rectangle

                                    Width="1" Height="15" Fill="White" SnapsToDevicePixels="True"

                                    IsHitTestVisible="False" IsEnabled="False"

                                    HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,0,-18"

                                    Tag="BottomRight"/>

                                <TextBlock

                                    Text="{Binding ActualWidth, ElementName=Rect, StringFormat='0.00 mm'}" Height="15" Width="{Binding ActualWidth, ElementName=Rect}"

                                    SnapsToDevicePixels="True" IsHitTestVisible="False" IsEnabled="False"

                                    HorizontalAlignment="Center" VerticalAlignment="Bottom"

                                    TextAlignment="Center" Foreground="White" FontSize="9"

                                    Margin="0,0,0,-25"

                                    ClipToBounds ="True"/>

                            </Grid>

                            <!--왼쪽-->

                            <Grid x:Name="DrawingLVSizeAdorner" Margin="10,0,0,0">

                                <Rectangle Width="15" Height="1" Fill="White" SnapsToDevicePixels="True"

                                           IsHitTestVisible="False" IsEnabled="False"

                                           HorizontalAlignment="Left"

                                           VerticalAlignment="Top"

                                           Margin="-27,0,0,0"

                                           Tag="LeftTop"/>

                                <Rectangle Height="{Binding ActualHeight, ElementName=Rect}" Width="1" Fill="White" SnapsToDevicePixels="True"

                                           IsHitTestVisible="False" IsEnabled="False"

                                           HorizontalAlignment="Left" VerticalAlignment="Center" Margin="-20,0,0,0"

                                           Tag="LeftCenter"/>

                                <Rectangle Width="15" Height="1" Fill="White" SnapsToDevicePixels="True"

                                           IsHitTestVisible="False" IsEnabled="False"

                                           HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="-27,0,0,0"

                                           Tag="LeftBottom"/>

                                <StackPanel Margin="-35,70,0,0" HorizontalAlignment="Left" Height="25"

                                            SnapsToDevicePixels="True" IsHitTestVisible="False" IsEnabled="False"

                                            VerticalAlignment="Center">

                                    <StackPanel.RenderTransform>

                                        <RotateTransform Angle="270"/>

                                    </StackPanel.RenderTransform>

                                    <TextBlock Text="{Binding ActualHeight, ElementName=Rect, StringFormat='0.00 mm'}" Height="25" Foreground="White" FontSize="9"/>

                                </StackPanel>

                            </Grid>

                            <!--오른쪽-->

                            <Grid x:Name="DrawingVSizeAdorner" Margin="10,0,0,0">

                                <Rectangle

                                    Width="15" Height="1" Fill="White" SnapsToDevicePixels="True"

                                    IsHitTestVisible="False" IsEnabled="False"

                                    HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,0,-18,0"

                                    Tag="RightTop"/>

                                <Rectangle Height="{Binding ActualHeight, ElementName=Rect}" Width="1" Fill="White" SnapsToDevicePixels="True"

                                           IsHitTestVisible="False" IsEnabled="False"

                                           HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,-11,0"

                                           Tag="RightCenter"/>

                                <Rectangle Width="15" Height="1" Fill="White" SnapsToDevicePixels="True"

                                           IsHitTestVisible="False" IsEnabled="False"

                                           HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,-18,0"

                                           Tag="RightBottom"/>

                                <StackPanel Margin="0,0,-70,0" HorizontalAlignment="Right" Height="25"

                                            SnapsToDevicePixels="True" IsHitTestVisible="False" IsEnabled="False"

                                            VerticalAlignment="Center">

                                    <StackPanel.RenderTransform>

                                        <RotateTransform Angle="90"/>

                                    </StackPanel.RenderTransform>

                                    <TextBlock Text="{Binding ActualHeight, ElementName=Rect, StringFormat='0.00 mm'}" Height="25" Foreground="White" FontSize="9"/>

                                </StackPanel>

                            </Grid>

                            <TextBlock Text="+" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" />

                        </Grid>

                    </DataTemplate>

                </ContentControl.ContentTemplate>

            </ContentControl>

        </Canvas>

    </Grid>

</Window>

 

 

MainWindow.xaml.cs

using System;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Input;

 

namespace WpfApp

{

    /// <summary>

    /// MainWindow.xaml에 대한 상호 작용 논리

    /// </summary>

    public partial class MainWindow : Window

    {

        /// <summary>

        /// 좌측 마우스 클릭 시 포인트 위치입니다.

        /// </summary>

        Point? downPoint;

 

        /// <summary>

        /// 그려질 사각형입니다.

        /// </summary>

        Rect addRect;

 

        public MainWindow()

        {

            InitializeComponent();

        }

 

        /// <summary>

        /// 마우스 좌측 버튼 클릭 시 이벤트입니다.

        /// </summary>

        /// <param name="e"></param>

        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)

        {

            var pos = e.GetPosition(this);

            downPoint = pos;

 

            this.CaptureMouse();

        }

 

        /// <summary>

        /// 마우스 이동 시 이벤트입니다.

        /// </summary>

        /// <param name="e"></param>

        protected override void OnPreviewMouseMove(MouseEventArgs e)

        {

            base.OnPreviewMouseMove(e);

 

            if (Mouse.LeftButton == MouseButtonState.Pressed)

            {

                var pos = e.GetPosition(this);

 

                this.RectAdder.Visibility = Visibility.Visible;

 

                this.addRect = new Rect(new Point(downPoint.Value.X, downPoint.Value.Y), new Point(pos.X, pos.Y));

 

                Canvas.SetLeft(this.RectAdder, this.addRect.X);

                Canvas.SetTop(this.RectAdder, this.addRect.Y);

                this.RectAdder.Width = Math.Abs(this.addRect.Width);

                this.RectAdder.Height = Math.Abs(this.addRect.Height);

            }

        }

 

        /// <summary>

        /// 마우스를 놓을때 발생되는 이벤트입니다.

        /// </summary>

        /// <param name="e"></param>

        protected override void OnMouseUp(MouseButtonEventArgs e)

        {

            this.ReleaseMouseCapture();

        }

    }

}

 

 

 


반응형
반응형

EventAggregator 는 이벤트들을 구독하거나 구독기능을 제공하며

컴포넌트 간의 이벤트 처리를 직접적인 참조 없이 가능하게 해줍니다.

 

MainWindow.xaml

<Window x:Class="WpfAppPrism.MainWindow"

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

        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

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

        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

        mc:Ignorable="d"

        Title="MainWindow" Height="450" Width="800">

    <StackPanel VerticalAlignment="Center">

        <Button Name="testButton" Content="Click!" Height="100" Width="200" HorizontalAlignment="Center" Click="testButton_Click"/>

        <Label Content=""/>

        <Button Name="unsubscribeButton" Content="Unsubscribe" Height="100" Width="200" HorizontalAlignment="Center" Click="unsubscribeButton_Click"/>

    </StackPanel>

</Window>

 

 

MainWindow.xaml.cs

using Microsoft.Practices.Composite.Events;

using Microsoft.Practices.Composite.Presentation.Events;

 

using System.Windows;

 

namespace WpfAppPrism

{

    /// <summary>

    /// string 을 인자를 가지는 이벤트 입니다.

    /// CompositePresentationEvent 는 이벤트 게시 및 구독을 관리하는 클래스입니다.

    /// </summary>

    public class SampleStringEvent : CompositePresentationEvent<string> { }

 

    public partial class MainWindow : Window

    {

        /// <summary>

        /// EventAggregator 입니다.

        /// </summary>

        EventAggregator aggregator = new EventAggregator();

 

        /// <summary>

        /// 구독 토큰입니다.

        /// </summary>

        private SubscriptionToken subscriptionToken;

 

        public MainWindow()

        {

            InitializeComponent();

 

            // 이벤트를 구독합니다.

            this.aggregator.GetEvent<SampleStringEvent>().Subscribe(StringAction1);

 

            //이벤트를 구독하고 구독취소를 위해 SubscriptionToken 을 받습니다.

            this.subscriptionToken = this.aggregator.GetEvent<SampleStringEvent>().Subscribe(StringAction2);

        }

 

        /// <summary>

        /// 이벤트 1 입니다.

        /// </summary>

        /// <param name="s"></param>

        private void StringAction1(string s)

        {

            MessageBox.Show($"{s}.StringAction1");

        }

 

        /// <summary>

        /// 이벤트 2 입니다.

        /// </summary>

        /// <param name="s"></param>

        private void StringAction2(string s)

        {

            MessageBox.Show($"{s}.StringAction2");

        }

 

        /// <summary>

        /// test 버튼 클릭 이벤트입니다.

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void testButton_Click(object sender, RoutedEventArgs e)

        {

            // Subscribe 된 이벤트를 실행합니다.

            this.aggregator.GetEvent<SampleStringEvent>().Publish("testButton");

        }

 

        /// <summary>

        /// Unsubscribe 버튼 클릭 이벤트입니다.

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void unsubscribeButton_Click(object sender, RoutedEventArgs e)

        {

            // 이벤트 하나를 Unsubscribe 합니다.

            this.aggregator.GetEvent<SampleStringEvent>().Unsubscribe(this.subscriptionToken);

        }

    }

}

 

 

 

우선 Click! 버튼을 클릭하면 "testButton.StringAction2" 메세지 박스가 나온후 "testButton.StringAction1" 메세지 박스가 나타난다.

구독한 순서 역순으로 이벤트는 발생된다.

 

여기서 Unsubscribe 버튼을 클릭하면 "testButton.StringAction1" 메세지 박스만 나타난다.




 

 

반응형
반응형

참고 : https://prismlibrary.com/docs/event-aggregator.html


Event Aggregator

Prism library 는 application 상에서 느슨하게 커플링된 component 간의 communication 을 위한 event mechanism 을 제공하는데, event aggrgator 서비스에 기반을 둔 이 mechanism 은 publisher 나 subscriber 가 event를 통해 서로간에 직접적인 참조 없이 communication 이 가능하게 해준다.

EventAggregator 는 동시에 데이터를 주고 받을 수 있는 (multicasting) publish/subscribe 기능을 제공하기도 한다. 이 말은 하나의 event 를 발생시키는 여러개의 publisher 가 있을 수도 있고, 하나의 event 를 수신하는 여러개의 subscriber 들이 있을 수 있다는 의미이다. controller 나 presenter 같은 business logic code 사이에서 message 를 보낼 때 그리고 여러 모듈들 사이에서 event 를 발생시키는 EventAggregator 를 생각해보자.

Prism Library 에서 만들어진 Event 는 typed event 이다. 이 말은 application 이 돌기 전에 compile 시점에서 error 를 감지할 수 있다는 의미이다. 또한 Prisim Library 에서 EventAggregator 는 subscriber 나 publisher 가 특정한 EventBase 를 마음대로 위치시킬 수 있도록 해주기도 하며, 여러개의 publisher / 여러개의 subscriber 를 지원 가능하다.

IEventAggregator

EventAggregator 클래스는 container 에서 service 로서 제공되고, IEventAggregator 인터페이스를 통해 얻을 수 있다. event aggregator 는 event 를 위치시키거나 만들어 내고 event 들의 collection 을 유지하는 역할을 한다.

public interface IEventAggregator { TEventType GetEvent<TEventType>() where TEventType : EventBase; }

EventAggregator 는 현재 생성되어 있지 않다면, 첫 번째 access 시점에 event 를 생성한다. 이로 인해 publisher 나 subscriber 는 해당 event가 사용가능한지 여부를 결정할 필요가 없다.

PubSubEvent

publisher 와 subscriber 를 실제로 연결해주는 것은 PubSubEvent class 인데, Prism library 안의 유일한 EventBase class 의 구현체이다. 이 class 는 subscriber 의 리스트를 유지 관리하고 subscriber 에거 보내는 event 들을 처리한다.

PubSubEvent class 는 generic type으로 정의된, payload type을 인수로 받는 generic class 인데 compile 시점에 event 연결이 잘 될 수 있도록 publisher 와 subscriber 가 적절한 method 를 제공하게 해준다. 아래는 PubSubEvent class 의 일부 정의를 보여준다.

Note

PubSubEvent 는 Prism.Core NuGet package 에 있는 Prism.Events 라는 namespace 에 정의되어 있다.

Creating an Event

PubSubEvent<TPayload> 는 application 또는 module 의 특정 이벤트에 대한 base class 가 되도록 만들어졌다. TPayload 는 event 의 payload 타입인데, payload 라는 것은 event 가 발생했을 때(publish) subscriber 에게 전달되는 argument 이다.

예를 들어, 다음의 코드는 TickerSymbolSelectedEvent 를 보여주고 있는데, payload 는 company symbol를 포함하는 string 값이다. 이 클래스의 구현이 비어 있는 것에 주목하라.

public class TickerSymbolSelectedEvent : PubSubEvent<string>{}

Note

Composite application 상에서 event 들은 주로 여러 module 상에서 공유되는 경우가 많기 때문에 보통은 common 한 곳에 정의한다. "Core" 나 "Infrastructure" 프로젝트 같은 shared assembly 에 정의하는 것이 보통이다.

Publishing an Event

Publisher 는 EventAggregator 로부터 event를 받고 publish method 를 호출함으로써 event 를 발생시킨다. EventAggregtor 에 access 하기 위해서는 class constructor 에 IEventAggregator 타입을 인자로 받도록 추가해서 dependency injection 을 사용할 수 있다.

public class MainPageViewModel { IEventAggregator _eventAggregator; public MainPageViewModel(IEventAggregator ea) { _eventAggregator = ea; } }

다음 코드는 TickerSymbolSelectedEvent 를 publish 하는 예를 보여준다.

_eventAggregator.GetEvent<TickerSymbolSelectedEvent>().Publish("STOCK0");

Subscribing to Events

Subscriber 는 pubSubEvent class 의 overload 가능한 Subscirbe method 중 하나를 이용해서 event 에 참여할 수 있다.

public class MainPageViewModel { public MainPageViewModel(IEventAggregator ea) { ea.GetEvent<TickerSymbolSelectedEvent>().Subscribe(ShowNews); } void ShowNews(string companySymbol) { //implement logic } }

PubSubEvents 를 구독할 수 있는 몇 가지 방법이 있다. 최선의 방법을 결정하는데 다음의 기준들이 도움이 될 것이다.

- event 가 발생했을 때 UI Elements 를 update 하는 경우 : UI thread 에서 해당 event 를 받도록 subscribe 한다.

- event 를 filtering 할 경우 : subscribing 할 때 filter delegate 을 제공한다.

- event 관련하여 performance 고려가 필요한 경우 : subscribing 할 때 strongly referenced delegate를 사용하고 PubSubEvent 에서 manually unsubscribing 한다.

- 위의 그 어떤 것도 적용하기 힘든 경우 : default subscription 을 사용한다.

다음 section에서 이러한 option 들에 대해 알아본다.

Subscribing on the UI Thread

subscriber 는 종종 event에 대한 응답으로 UI Elements를 업데이트할 필요가 있는데, WPF에서는 오직 UI thread 만이 UI elements를 업데이트 할 수가 있다.

기본적으로 subscriber는 publisher 의 thread 상에서 event를 받는다. 만약 publisher 가 UI thread 로부터 event를 보내게 되면 subscriber 는 UI를 업데이트 할 수 있다. 하지만 만약 publisher 의 thread 가 background thread 이면 subscriber 는 직접적으로 UI elements 를 업데이트 할 수가 없다. 이런 경우 subscriber 는 Dispatcher class 를 이용해서 UI thread 상에서 업데이트 되도록 scheduling 을 할 필요가 있다.

Prism Library 에서 제공하는 PubsubEvent 는 subscriber가 UI thread 에서 event 를 자동으로 수신하도록 해줄 수 있는데, subscriber는 이 부분을 다음 코드와 같이 subscription 때 명시해 준다.

public class MainPageViewModel { public MainPageViewModel(IEventAggregator ea) { ea.GetEvent<TickerSymbolSelectedEvent>().Subscribe(ShowNews, ThreadOption.UIThread); } void ShowNews(string companySymbol) { //implement logic } }

ThreadOption 다음과 같다.

- PublisherThread : publisher의 thread 에서 event 를 받도록 하려면 이 option을 사용한다. (default setting)

- BackgroundThread : .NET Framework thread-pool thread 에서 event 를 비동기적으로 받도록 하려면 이 option을 사용한다.

- UITrhead : UI thread 에서 event 를 받도록 하려면 이 option을 사용한다.

Note

PubSubEvent 가 UI thread 상에서 subscriber 에게 event 를 발생시키도록 하기 위해서는 EventAggregator 가 반드시 UI thread 상에서 초기에 생성되어지도록 해야 한다.

Subscription Filtering

subscriber 가 발행된 모든 event들을 하나하나 처리할 필요가 없을 수도 있는데, 이런 상황에서는 subscriber 가 filter parameter 를 사용할 수 있다. filter parameter 는 System.Predicate<TPayLoad> 형식이고, 발생된 event 의 payload 가 subscriber 의 callback method가 실행되어야 하는 조건인지 아닌지 판단해주는 delegate 이기도 하다. 만약 payload 가 조건에 부합하지 않으면 subscriber의 callback 은 실행되지 않게 된다.

이러한 filter 대게 lambda expression으로 구현된다.

public class MainPageViewModel { public MainPageViewModel(IEventAggregator ea) { TickerSymbolSelectedEvent tickerEvent = ea.GetEvent<TickerSymbolSelectedEvent>(); tickerEvent.Subscribe(ShowNews, ThreadOption.UIThread, false, companySymbol => companySymbol == "STOCK0"); } void ShowNews(string companySymbol) { //implement logic } }

Note

Subscribe method 는 추후 해당 event 에 대한 subscription 을 제거하는데 사용되는 Prism.Events.SubscriptionToken 타입의 subscription token 이라는 것을 리턴한다. 이 token 은, callback delegate 으로서 anonymous delegate(익명 대리자) 또는 lambda expression 을 사용했을 때 특별히 유용하게 사용할 수 있다. 또는 다른 filter 로 동일한 event 를 구독하고 있을 때도 마찬가지이다.


반응형
반응형

이미지를 특정 모양 부분만 보이도록 하는 방법입니다.

Clip 에 패스미니언어를 이용하면됩니다.

 

<Window x:Class="WpfApp3.MainWindow"

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

        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

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

        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

        mc:Ignorable="d"

        Title="MainWindow" Height="450" Width="600">

    <Image

         Clip="M 220 170 L 170 390 L 370 390 L 320 170 A 70 70 0 1 0 220 170"

         Stretch="None"

         Source="http://img2.yna.co.kr/etc/inner/KR/2019/09/09/AKR20190909107200009_04_i_P4.jpg" />

</Window>

 

 

반응형
반응형

CollectionView 는 필터를 적용할수 있습니다.

bool 값을 반환하는 메서드를 만들어 Filter 에 적용시키면됩니다.

 

bool 값을 반환하는 메서드를 아래 처럼 정의하고

* "K" 로 시작하는 값만 필터링하게 합니다.

 

        bool NameFilter(object obj)

        {

            return (obj as Person).Name.StartsWith

            (

                "K",

                StringComparison.CurrentCultureIgnoreCase

            );

        }

 

CollectionView 의 Filter = NameFilter; 로 적용하면 됩니다.

 

아래는 예시 코드입니다.

 

Person.cs

using System.ComponentModel;

 

namespace WpfApp

{

    public class Person : INotifyPropertyChanged

    {

        /// <summary>

        /// 속성변경 이벤트입니다.

        /// </summary>

        public event PropertyChangedEventHandler PropertyChanged;

 

        /// <summary>

        /// 이름입니다.

        /// </summary>

        string name = "";

 

        /// <summary>

        /// 별명입니다.

        /// </summary>

        string nickName = "";

 

        /// <summary>

        /// 이름입니다.

        /// </summary>

        public string Name

        {

            set

            {

                this.name = value;

                OnPropertyChanged(nameof(Name));

            }

            get { return name; }

        }

 

        /// <summary>

        /// 별명입니다.

        /// </summary>

        public string NickName

        {

            set

            {

                nickName = value;

                OnPropertyChanged(nameof(NickName));

            }

            get { return nickName; }

        }

 

        /// <summary>

        /// 속성 값이 변경될 때 발생합니다.

        /// </summary>

        /// <param name="propertyName"></param>

        protected virtual void OnPropertyChanged(string propertyName)

        {

            if (PropertyChanged != null)

            {

                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

            }

        }

    }

}

 

 

MainWindow.xaml

<Window x:Class="WpfApp3.MainWindow"

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

        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

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

        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

        mc:Ignorable="d"

        Title="MainWindow" Height="450" Width="400">

    <StackPanel>

        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="10">

            <TextBox Name="searchTextBox" Text="" Width="200" VerticalContentAlignment="Center"/>

            <Button Name="searchButton" Content="Search" Padding="5" Margin="5" Click="searchButton_Click"/>

        </StackPanel>

        <TextBox

            Margin="12" Height="50" VerticalContentAlignment="Center"

            Text="{Binding Name,

                   Mode=TwoWay,

                   UpdateSourceTrigger=PropertyChanged}" />

        <TextBox

            Margin="12" Height="50" VerticalContentAlignment="Center"

            Text="{Binding NickName,

                   Mode=TwoWay,

                   UpdateSourceTrigger=PropertyChanged}" />

        

        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">

            <Button Name="prevButton" Content="Prev" Padding="10" Margin="10" Click="prevButton_Click"/>

            <Button Name="nextButton" Content="Next" Padding="10" Margin="10" Click="nextButton_Click"/>

        </StackPanel>

    </StackPanel>

</Window>

 

 

MainWindow.xaml.cs

using System;

using System.Collections.ObjectModel;

using System.ComponentModel;

using System.Linq;

using System.Windows;

using System.Windows.Data;

 

namespace WpfApp3

{

    public partial class MainWindow : Window

    {

        /// <summary>

        /// 데이터 collectionView 입니다.

        /// </summary>

        ICollectionView collectionView;

 

        public MainWindow()

        {

            InitializeComponent();

 

            ObservableCollection<Person> datas = new ObservableCollection<Person>();

 

            datas.Add(new Person() { Name = "Kang",  NickName = "Super" });

            datas.Add(new Person() { Name = "An",    NickName = "Father" });

            datas.Add(new Person() { Name = "Jang",  NickName = "Marvel" });

            datas.Add(new Person() { Name = "Joo",   NickName = "Mother" });

            datas.Add(new Person() { Name = "Kim",   NickName = "DC" });

 

            this.collectionView = CollectionViewSource.GetDefaultView(datas);

            this.collectionView.Filter = NameFilter;

            this.collectionView.CurrentChanged += CollectionView_CurrentChanged; ;

 

            this.collectionView.SortDescriptions.Add(new SortDescription(nameof(Person.Name), ListSortDirection.Ascending));

 

            this.DataContext = this.collectionView;

 

            // 처음값으로 이동시킵니다.

            this.collectionView.MoveCurrentToFirst();

        }

 

        /// <summary>

        /// 이전 값으로 이동하는 버튼 클릭이벤트입니다.

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void prevButton_Click(object sender, RoutedEventArgs e)

        {

            this.collectionView.MoveCurrentToPrevious();

        }

 

        /// <summary>

        /// 다음 값으로 이동하는 버튼 클릭이벤트입니다.

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void nextButton_Click(object sender, RoutedEventArgs e)

        {

            this.collectionView.MoveCurrentToNext();

        }

 

        /// <summary>

        /// CollectionView 현재값이 변경되었을때 발생되는 이벤트입니다.

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="args"></param>

        private void CollectionView_CurrentChanged(object sender, EventArgs e)

        {

            // 이전/다음 항목 존재여부에 따라 이전/다음 버튼을 활성화하거나 비활성화 합니다.

            this.prevButton.IsEnabled = this.collectionView.CurrentPosition > 0;

            this.nextButton.IsEnabled = this.collectionView.CurrentPosition < this.collectionView.Cast<object>().Count() - 1;

        }

 

        /// <summary>

        /// Search 버튼 클릭이벤트입니다.

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void searchButton_Click(object sender, RoutedEventArgs e)

        {

            // Refresh 하여 필터 적용된 내용이 보이도록합니다.

            this.collectionView.Refresh();

        }

 

        /// <summary>

        /// 이름 기준 필터입니다.

        /// </summary>

        /// <param name="obj"></param>

        /// <returns></returns>

        bool NameFilter(object obj)

        {

            return (obj as Person).Name.StartsWith

            (

                this.searchTextBox.Text,

                StringComparison.CurrentCultureIgnoreCase

            );

        }

    }

}

 

 

결과


결과를 보면 내용중에 필터링 된 내용만 볼수있도록 처리가 됩니다.


반응형
반응형

CollectionView 에 담겨진 데이터를 화면에 하나씩 나타내도록 하여

탐색하는 코드를 알아봅니다.

 

Person.cs

using System.ComponentModel;

 

namespace WpfApp

{

    public class Person : INotifyPropertyChanged

    {

        /// <summary>

        /// 속성변경 이벤트입니다.

        /// </summary>

        public event PropertyChangedEventHandler PropertyChanged;

 

        /// <summary>

        /// 이름입니다.

        /// </summary>

        string name = "";

 

        /// <summary>

        /// 별명입니다.

        /// </summary>

        string nickName = "";

 

        /// <summary>

        /// 이름입니다.

        /// </summary>

        public string Name

        {

            set

            {

                this.name = value;

                OnPropertyChanged(nameof(Name));

            }

            get { return name; }

        }

 

        /// <summary>

        /// 별명입니다.

        /// </summary>

        public string NickName

        {

            set

            {

                nickName = value;

                OnPropertyChanged(nameof(NickName));

            }

            get { return nickName; }

        }

 

        /// <summary>

        /// 속성 값이 변경될 때 발생합니다.

        /// </summary>

        /// <param name="propertyName"></param>

        protected virtual void OnPropertyChanged(string propertyName)

        {

            if (PropertyChanged != null)

            {

                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

            }

        }

    }

}

 

 

MainWindow.xaml

<Window x:Class="WpfApp.MainWindow"

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

        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

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

        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

        mc:Ignorable="d"

        Title="MainWindow" Height="450" Width="400">

    <StackPanel>

        <TextBox

            Margin="12" Height="50" VerticalContentAlignment="Center"

            Text="{Binding Name,

                   Mode=TwoWay,

                   UpdateSourceTrigger=PropertyChanged}" />

        <TextBox

            Margin="12" Height="50" VerticalContentAlignment="Center"

            Text="{Binding NickName,

                   Mode=TwoWay,

                   UpdateSourceTrigger=PropertyChanged}" />

        

        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">

            <Button Name="prevButton" Content="Prev" Padding="10" Margin="10" Click="prevButton_Click"/>

            <Button Name="nextButton" Content="Next" Padding="10" Margin="10" Click="nextButton_Click"/>

        </StackPanel>

    </StackPanel>

</Window>

 

 

MainWindow.xaml.cs

using System;

using System.Collections.ObjectModel;

using System.ComponentModel;

using System.Linq;

using System.Windows;

using System.Windows.Data;

 

namespace WpfApp

{

    public partial class MainWindow : Window

    {

        /// <summary>

        /// 데이터 collectionView 입니다.

        /// </summary>

        ICollectionView collectionView;

 

        public MainWindow()

        {

            InitializeComponent();

 

            ObservableCollection<Person> datas = new ObservableCollection<Person>();

 

            datas.Add(new Person(){ Name = "Kang",  NickName = "Super" });

            datas.Add(new Person(){ Name = "An",    NickName = "Father" });

            datas.Add(new Person(){ Name = "Jang",  NickName = "Marvel" });

 

            this.collectionView = CollectionViewSource.GetDefaultView(datas);

            this.collectionView.CurrentChanged += CollectionView_CurrentChanged; ;

 

            this.collectionView.SortDescriptions.Add(new SortDescription(nameof(Person.Name), ListSortDirection.Ascending));

 

            this.DataContext = this.collectionView;

 

            // 처음값으로 이동시킵니다.

            this.collectionView.MoveCurrentToFirst();

        }

 

        /// <summary>

        /// 이전 값으로 이동하는 버튼 클릭이벤트입니다.

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void prevButton_Click(object sender, RoutedEventArgs e)

        {

            this.collectionView.MoveCurrentToPrevious();

        }

 

        /// <summary>

        /// 다음 값으로 이동하는 버튼 클릭이벤트입니다.

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="e"></param>

        private void nextButton_Click(object sender, RoutedEventArgs e)

        {

            this.collectionView.MoveCurrentToNext();

        }

 

        /// <summary>

        /// CollectionView 현재값이 변경되었을때 발생되는 이벤트입니다.

        /// </summary>

        /// <param name="sender"></param>

        /// <param name="args"></param>

        private void CollectionView_CurrentChanged(object sender, EventArgs e)

        {

            // 이전/다음 항목 존재여부에 따라 이전/다음 버튼을 활성화하거나 비활성화 합니다.

            this.prevButton.IsEnabled = this.collectionView.CurrentPosition > 0;

            this.nextButton.IsEnabled = this.collectionView.CurrentPosition < this.collectionView.Cast<object>().Count() - 1;

        }

    }

}

 

 


 

반응형
반응형

CollectionView 를 이용해 다양한 기능을 처리할수 있습니다.

여기선 데이터 정렬하는 기능을 알아봅니다.

 

먼저 데이터로 사용할 Class 를 정의합니다.

 

Person.cs

 

using System.ComponentModel;

 

namespace WpfApp

{

    public class Person : INotifyPropertyChanged

    {

        /// <summary>

        /// 속성변경 이벤트입니다.

        /// </summary>

        public event PropertyChangedEventHandler PropertyChanged;

 

        /// <summary>

        /// 이름입니다.

        /// </summary>

        string name = "";

 

        /// <summary>

        /// 별명입니다.

        /// </summary>

        string nickName = "";

 

        /// <summary>

        /// 이름입니다.

        /// </summary>

        public string Name

        {

            set

            {

                this.name = value;

                OnPropertyChanged(nameof(Name));

            }

            get { return name; }

        }

 

        /// <summary>

        /// 별명입니다.

        /// </summary>

        public string NickName

        {

            set

            {

                nickName = value;

                OnPropertyChanged(nameof(NickName));

            }

            get { return nickName; }

        }

 

        /// <summary>

        /// 속성 값이 변경될 때 발생합니다.

        /// </summary>

        /// <param name="propertyName"></param>

        protected virtual void OnPropertyChanged(string propertyName)

        {

            if (PropertyChanged != null)

            {

                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

            }

        }

    }

}

 

 

MainWindow.xaml

<Window x:Class="WpfApp.MainWindow"

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

        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

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

        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

        mc:Ignorable="d"

        Title="MainWindow" Height="450" Width="800">

    <StackPanel>

        <ListBox Name="listbox" Width="300" Height="300" Margin="24">

            <ListBox.ItemTemplate>

                <DataTemplate>

                    <StackPanel Orientation="Horizontal">

                        <TextBlock Text="{Binding Name}" />

                        <TextBlock Text="      " />

                        <TextBlock Text="{Binding NickName}" />

                    </StackPanel>

                </DataTemplate>

            </ListBox.ItemTemplate>

        </ListBox>

    </StackPanel>

</Window>

 

 

MainWindow.xaml.cs

 

using System.Collections.ObjectModel;

using System.ComponentModel;

using System.Windows;

using System.Windows.Data;

 

namespace WpfApp

{

    public partial class MainWindow : Window

    {

        public MainWindow()

        {

            InitializeComponent();

 

            ObservableCollection<Person> datas = new ObservableCollection<Person>();

 

            datas.Add(new Person(){ Name = "Kang",  NickName = "Super" });

            datas.Add(new Person(){ Name = "An",    NickName = "Father" });

            datas.Add(new Person(){ Name = "Jang",  NickName = "Marvel" });

 

            ICollectionView collectionView = CollectionViewSource.GetDefaultView(datas);

 

            collectionView.SortDescriptions.Add(new SortDescription(nameof(Person.Name), ListSortDirection.Ascending));

 

            this.listbox.ItemsSource = collectionView;

        }

    }

}

 

 

위 코드에서 보듯이 CollectionView 의 SortDescriptions 를 사용하여 정렬기준을 줄수 있습니다.

 

 

 

반응형
반응형

Control 에서 원하는 Control 을 선택하면

상단에 해당 Control 이 표시되고

Dump 의  Template Property 를 선택하면 하단에 ControlTemplate 객체를 XAML 포맷으로 보여준다.



AnalysisControlTemplate.exe


출처 : 찰스 펫졸드

반응형
반응형

ControlTemplate 은 컨트롤의 모습이나 이벤트 처리등을 미리 정의해 놓고 가져다 쓰는 것을 말합니다.

아래 예시를 보시면 아시겠지만 RadioButton 앞의 라디오 박스가 사라지고

선택시 글자를 굵게 표시하고 주변으로 사각형이 그려집니다.

 

<Window x:Class="WpfApp.MainWindow"

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

        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

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

        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

        mc:Ignorable="d"

        Title="MainWindow" Height="450" Width="800">

    <StackPanel>

        <StackPanel.Resources>

            <ControlTemplate

                 x:Key="rectRadioButton"

                 TargetType="{x:Type RadioButton}">

                <Border

                    Name="border"

                    BorderBrush="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"

                    CornerRadius="0"

                    Padding="10"

                    BorderThickness="0"

                    Margin="2"

                    Width="250">

                    <ContentPresenter

                        Content="{TemplateBinding ContentControl.Content}" HorizontalAlignment="Center"/>

                </Border>

                <ControlTemplate.Triggers>

                    <Trigger

                         Property="IsChecked"

                         Value="True">

                        <Setter

                             TargetName="border"

                             Property="BorderThickness"

                             Value="3" />

                        <Setter

                             Property="FontWeight"

                             Value="Bold" />

                    </Trigger>

                </ControlTemplate.Triggers>

            </ControlTemplate>

        </StackPanel.Resources>

        <GroupBox

             HorizontalAlignment="Center"

             VerticalAlignment="Center"

             FontSize="12pt"

             Header="Select Option">

            <StackPanel>

                <RadioButton

                    Template="{StaticResource rectRadioButton}"

                    HorizontalAlignment="Center"

                    Content="Template RadioButton 1"

                    IsChecked="True" />

                <RadioButton

                     Template="{StaticResource rectRadioButton}"

                     HorizontalAlignment="Center"

                     Content="Template RadioButton 2" />

                <RadioButton

                     Template="{StaticResource rectRadioButton}"

                     HorizontalAlignment="Center"

                     Content="Template RadioButton 3" />

                <RadioButton

                     Template="{StaticResource rectRadioButton}"

                     HorizontalAlignment="Center"

                     Content="Template RadioButton 4" />

            </StackPanel>

        </GroupBox>

    </StackPanel>

</Window>

 

 

 

반응형

+ Recent posts