dngchn's [WPF]2017. 9. 1. 15:36

ListView를 알람 컨테이너로 사용한다.

각 row가 하나의 알람 항목이 되도록.


알람이니만큼, 빨간 램프를 두었다. 깜빡이면 좋을 듯 하여, 애니메이션을 주었다.

알람이 추가될 때마다 깜빡질을 한다.



위와 같다. 각 알람 항목은 추가될 때마다 램프가 깜빡이기 시작한다.

두어개까지는 괜찮지만, 항목이 많아지다보니, 여기저기서 깜빡꺼리며 난잡하다. 보기 싫다. 거슬린다.


각 깜빡임을 어떻게 하면 동기화 할 수 있을까.

알람이 추가되는 타이밍이 다르니, 깜빡이는 동작의 주기도 다를진데...


(깜빡임의 구현은 해당 이미지의 Opacity를 Doubleanimation을 주어 바뀌도록 했다.)


처음 생각은 시간 동기화.

animation의 시작 시간을 현재 시간의 초를 기준으로 한다면 어떨까..

WPF Double animation의 시작에 대해 이벤트를 통해 할 수 있을 듯 하지만, 막상 어떻게 해야할지 감이 안선다.


두번째 생각은 알람이 추가될때마다 이전 추가된 알람들을 새로 한번에 다시 넣기.

-> 새로운 알람 추가마다, 리스트를 비우고 추가하는 로드는 차치하고서라도,

알람이 많아지면, 아무리 한번에 넣는다하더라도, 먼저 추가되는 것과, 맨 나중에 추가되는 놈의 시간차로

미세한 차이는 생길 듯 하다. 시도 하지 않았지만, 하면 될 듯은 한데, 별로 좋아 보이지 않음.


흠...

구글링 시작..

https://stackoverflow.com/questions/4725680/synchronize-wpf-coloranimation-across-several-controls

위에서 ColinE의 답변 참고.


샘플 코드는 없지만, 괜찮은 생각인 듯. 꼼수같지만, 그래서 더 재밌는 생각!

hidden으로 하나의 컨트롤을 감춰두고, 나머지 추가되는 모든 알람들은 animation에서 Opacity를 각자 시작하는 것이 아니라, WPF의 Element binding을 이용하여 감춰진 컨트롤의 Opacity로 바인딩 걸기.


감춰진 더미 컨트롤 코드

<Image Source="/Assets/red-circle.png" HorizontalAlignment="Center" Visibility="Hidden" x:Name="dummyImage">
    <Image.Triggers>
        <EventTrigger RoutedEvent="Loaded">
            <BeginStoryboard>
                <Storyboard>
                    <DoubleAnimation To="0.2" Duration="0:0:0.5" Storyboard.TargetProperty="Opacity" RepeatBehavior="Forever" AutoReverse="True"/>
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </Image.Triggers>
</Image>


위 더미에 이름을 부여하고 본래 컬럼의 정의를 아래와 같이 바인딩 한다.

<GridViewColumn Width="25">
    <GridViewColumn.CellTemplate>
        <DataTemplate>
            <Image Source="/Assets/red-circle.png" HorizontalAlignment="Center" Margin="0" Opacity="{Binding ElementName=dummyImage, Path=Opacity}"/>
        </DataTemplate>
    </GridViewColumn.CellTemplate>
</GridViewColumn>



효과 확인.



위와 같이 모든 램프가 동기화 되어 같이 밝아지고, 같이 어두워 진다.


난 이런 꼼수가 좋더라~



Posted by dngchn
dngchn's [WPF]2017. 9. 1. 15:18

ListView에 포함된 이미지에 animation을 주자.


<ListView x:Name="AlarmListView" Margin="0,0,0,8" ItemsSource="{Binding AlarmCollection}" HorizontalAlignment="Stretch">
    <ListView.View>
        <GridView>
            <GridViewColumn Header="Arised Time" Width="150" DisplayMemberBinding="{Binding ArisedTime}" />
            <GridViewColumn Width="25">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <Image Source="/Assets/red-circle.png" HorizontalAlignment="Center" Margin="0">
                            <Image.Triggers>
                                <EventTrigger RoutedEvent="Loaded">
                                    <BeginStoryboard>
                                        <Storyboard>
                                            <DoubleAnimation To="0.2" Duration="0:0:0.5" Storyboard.TargetProperty="Opacity" RepeatBehavior="Forever" AutoReverse="True"/>
                                        </Storyboard>
                                    </BeginStoryboard>
                                </EventTrigger>
                            </Image.Triggers>
                        </Image>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>
            </GridViewColumn>
            <GridViewColumn Header="Message" DisplayMemberBinding="{Binding Message}" />
        </GridView>
    </ListView.View>
</ListView>


위와 같이 원하는 개체에 Triggers를 주며 구성해주면 된다.

RoutedEvent는 특별한 거 없이 항상 동작하기를 바라는 마음으로 Loaded를 주었다. 무조건 처음부터 시작하기.

그리고 RepeatBehavior과 AutoReverse를 주어 효과가 계속 반복되도록 하였음에 주의.

'dngchn's [WPF]' 카테고리의 다른 글

컨트롤 마다 animation 동기화(synchronization) 하기  (0) 2017.09.01
ListView Column Resize disable 시키기  (0) 2017.09.01
Listview에 이미지 넣기  (0) 2017.09.01
Convert image to SVG  (1) 2017.07.21
Modern UI for WPF (MUI)  (0) 2017.07.17
Posted by dngchn
dngchn's [WPF]2017. 9. 1. 15:11

ListView의 컬럼 사이즈 조정을 사용자가 하지 못하도록 막자.


<ListView x:Name="AlarmListView" Margin="0,0,0,8" ItemsSource="{Binding AlarmCollection}" HorizontalAlignment="Stretch">
    <ListView.View>
        <GridView>
            <GridView.ColumnHeaderContainerStyle>
                <Style BasedOn="{StaticResource {x:Type GridViewColumnHeader}}" TargetType="{x:Type GridViewColumnHeader}">
                    <Setter Property="IsHitTestVisible" Value="False"/>
                </Style>
          </GridView.ColumnHeaderContainerStyle>                                       
            <GridViewColumn Header="Message" DisplayMemberBinding="{Binding Message}" />
        </GridView>
    </ListView.View>
</ListView>


위와 같이 IsHitTestVisible을 false로 하면 됨.

Posted by dngchn
dngchn's [WPF]2017. 9. 1. 15:07

ListView에 이미지를 넣어 보자.


ListView의 특정 컬럼에 이미지를 넣는다고 


<ListView x:Name="AlarmListView" Margin="0,0,0,8" ItemsSource="{Binding AlarmCollection}" HorizontalAlignment="Stretch">
    <ListView.View>
        <GridView>            
            <GridViewColumn Header="Arised Time" Width="150" DisplayMemberBinding="{Binding ArisedTime}" />
            <GridViewColumn Width="25">
                <GridViewColumn.CellTemplate>
                    <DataTemplate>
                        <Image Source="/Assets/red-circle.png" HorizontalAlignment="Center" Margin="0" Opacity="{Binding ElementName=dummyImage, Path=Opacity}"/>
                    </DataTemplate>
                </GridViewColumn.CellTemplate>                
            </GridViewColumn>                            
            <GridViewColumn Header="Message" DisplayMemberBinding="{Binding Message}" />
        </GridView>
    </ListView.View>
</ListView>


위와 같이 CellTemplate, DataTemplate를 정의하면 된다.

Posted by dngchn
dngchn's [WPF]2017. 7. 21. 14:06

이미지를 벡터타입으로 변환해서 WPF에서 이용하기


이미지를 WPF Xaml에서 Geometry, Path Data등으로 지정하여 사용하고 싶을 때가 있다.

이런 경우 이미지는 로고 등으로 단순하고, 단일 색상으로 이루어져있어야 좋다


구글링을 하며 여러 방법으로 이를 시도해보았다.


1. Microsoft Expression Design 4 이용하기

MS에서 무료로 다운로드 받아 설치/사용이 가능하다.

Import image 메뉴로 새문서에 원하는 이미지를 불러들인 후, Object - Image - Auto Trace Image... 메뉴를 이용하여, 이미지의 외각선을 딴다. 그 후 해당 오브젝트에 우클릭하여(또는 메뉴에서) Export를 선택하고, Format을 SVG나 XAML... 등으로 선택해서 export 시킨 후 해당 파일을 Visual Studio에서 불러낸다.


xaml 또는 SVG 파일 내용 중 <Path>의 d 또는 data 부분을 얻을 수 있다. (여러줄이라면, 끝에 Z를 빼고 이어 붙인다. 물론 마지막 Z는 필요)


2.  온라인에서 간단히 할 수 있다. (https://www.vectorizer.io/)

위에 접속하여 이미지를 upload하고 step별로 적절하게 지정해주면, SVG파일이 만들어지고 이를 다운로드 할 수 있다.

이렇게 얻어진 SVG는 1번에서 처럼 열어서 path부분을 연결해주면, 한줄짜리 path data를 얻어낼 수 있다.



이미지




Path Data


LogoData="M 1475 5097 c-55 -22 -74 -37 -99 -80 -32 -54 -40 -104 -27 -153 6 -22 142 -266 302 -544 160 -278 305 -530 322 -560 l32 -55 -40 24 c-22 13 -154 89 -292 169 -272 156 -307 169 -387 142 -144 -47 -183 -245 -69 -341 16 -13 154 -96 306 -184 l278 -160 -839 -5 -838 -5 -36 -24 c-50 -35 -80 -84 -86 -141 -7 -71 5 -116 40 -156 65 -74 0 -68 925 -74 l831 -5 -591 -340 c-334 -192 -606 -355 -627 -376 -69 -67 -77 -179 -19 -259 50 -69 146 -98 228 -69 17 6 285 157 594 336 601 347 622 359 614 346 -2 -4 -73 -127 -157 -272 -84 -146 -159 -279 -166 -297 -49 -117 37 -259 164 -272 88 -8 152 26 203 107 l29 45 0 457 0 457 -38 74 c-47 93 -65 168 -65 278 0 273 203 517 478 575 88 18 142 18 230 0 275 -58 478 -302 478 -575 0 -110 -18 -185 -65 -278 l-38 -74 0 -456 0 -456 176 -305 c97 -168 188 -319 202 -335 35 -37 97 -66 145 -66 72 0 149 48 185 115 23 45 20 131 -8 185 -12 25 -158 279 -323 565 -165 286 -302 524 -305 529 -3 5 -1 7 4 4 5 -3 137 -79 294 -169 l285 -164 67 0 c57 0 72 4 110 30 104 73 118 213 30 307 -14 15 -152 101 -308 190 l-282 163 831 5 c925 6 860 0 925 74 35 40 47 85 40 156 -6 57 -36 106 -86 141 l-36 24 -838 5 -839 5 597 344 c352 203 610 358 630 379 88 91 58 254 -58 313 -32 17 -55 20 -102 17 -59 -4 -77 -13 -656 -347 -327 -189 -599 -346 -603 -348 -12 -7 -6 4 160 291 163 281 173 303 173 358 0 110 -89 197 -202 198 -111 0 -133 -25 -329 -364 l-164 -285 -5 443 -5 442 -28 42 c-76 115 -258 115 -334 0 l-28 -42 -5 -442 -5 -443 -320 556 c-176 306 -333 569 -349 585 -53 54 -140 72 -211 45 z M 2445 3425 c-76 -31 -135 -91 -163 -168 -38 -100 -22 -206 43 -292 l35 -45 0 -1460 0 -1460 200 0 200 0 0 1460 0 1460 35 45 c93 122 83 289 -24 396 -86 87 -211 111 -326 64 z"




적용 모습




Posted by dngchn
dngchn's [WPF]2017. 7. 17. 18:41

Modern UI for WPF (MUI)

0.

https://mui.codeplex.com/ (15th, 2017까지만 오픈)

https://github.com/firstfloorsoftware/mui


2017.07.17 현재 공식 버전: 1.0.9 (https://github.com/firstfloorsoftware/mui/release 에서 다운로드)

(NuGet 설치시 1.0.11버전이 ModernUI.WPF.Xrm 이름으로 보이나 이는 공식버전 아닌 branch된 버전 임.


1.0.9버전을 다운로드 받으면, FirstFloor.ModernUI.App 이름으로 완벽한 샘플을 소스채 받을 수 있다.


1.

가장 처음, Hello, World 처럼, 손쉽게 MUI를 적용된 WPF창을 만드려면,

새로운 프로젝트 시작시, MUI Template을 선택하면 된다.

지원 Template이 Visual Studio 2013까지 있다.

https://marketplace.visualstudio.com/items?itemName=KoenZwikstra.ModernUIforWPFTemplates

2017버전을 쓰는 상태에서 설치하니, 뭔가 좀 이상한데..

Modern UI WPF Application과

Modern UI WPF Navigation Application 두개의 템플릿만 보인다.


첫번 째 것은 단일창 형태의 WPF Window를 MUI 스타일로 만들어 준다.

두번 째 것은 Group Menu를 두어 이동(navigate)가능한 형태의 앱을 만들어 준다.


2.

첨부된 'Modern UI for WPF 통합정리본'내에 '친절한 샘플들' 폴더의 솔루션을 살펴보면,

각각 간단한 창부터 mvvm까지 고려한 다양한 형태의 샘플들을 볼 수 있으며,

각각은 어떻게 코딩해야 하는지에 대해서 친절하게 UI에 그 설명을 나타내준다.


3.

1.0.9 최신 소스를 다운받아 솔루션을 열어보면, 다양한 컨트롤과 레이아웃 등에 대해 모든 소스를 제공한다.


4.

첨부의 'Waf와 결합본'에는 WPF Application Framework와 결합한 형태의 MUI 샘플을 제공한다(내가 만듦).






Posted by dngchn
dngchn's [WPF]2017. 5. 31. 11:52

Collection을 노출하는 방법

Collection을 외부에 노출하는 작업은 종종 필요하고, 다양한 방법이 있겠지만 몇가지 이슈사항을 고려하여 아래와 같은 방법을 추천한다.


Example 1 -> 좋은 예

public class AddressBook
{
    private readonly List<Contact> contacts;

    public AddressBook()
    {
        this.contacts = new List<Contact>();
    }

    public IReadOnlyList<Contact> Contacts { get { return contacts; } }

    public void AddContact(Contact contact)
    {
        contacts.Add(contact);
    }

    public void RemoveContact(Contact contact)
    {
        contacts.Remove(contact);
    }
}

1. 위에서와 같이 IReadOnlyList<T> 또는 IReadOnlyCollection<T>를 이용하여 collection을 외부에 노출시킨다.

순서가 중요하면 IReadOnlyList<T>를 사용,

순서가 중요하지 않다면 IReadOnlyListCollection<T>를 사용.


2. Collection을 조작할 수 있는 메서드를 제공할 것.

이렇게 해야 collection 조작에 대한 컨트롤을 유지할 수 있다(여기저기 다른 곳에서 collection 조작 금지)

이러한 메서드들은 단순하게 collection을 조작하는 일 외에 추가적인 처리를 더할 수 있다(Add메서드에 유일성을 검사하기 위한 판단 추가 등).


3. IEnumerable<T>를 쓰지 말아라, collection을 노출하기 위한 목적이라면.

동작에는 문제 없으나, 성능적인 이슈가 문제 될 수 있음.

 IEnumerable<T>가 단순 collection이 아닌 query를 나타낸다면, 해당 구문 실행 시 마다, 중복되어 query문이 실행될 여지 있음.

* 이에 대한 경고를 'Resharper'에서는 "Possible multiple enumeration of IEnumerable"과 같이 주고 있음.



Example 2 -> 나쁜 예

// This example shows how you should not to expose a collection!
public class AddressBook
{
    public AddressBook()
    {
        Contacts = new List<Contact>();
    }
        
    // Property should not expose the concrete collection type and avoid providing a public setter.
    public List<Contact> Contacts { get; set; }
}

1. 위에서와 같이 구체적인 타입(Contact과 같이)으로 외부에 노출하지 말것.

collection의 타입을 변경해야 한다면 이는 의존성을 증가 시킨다.

외부에서 구체적인 타입의 collection을 얻을 수 있다면 직접 collection을 조작할 수도 있어 collection 조작에 대한 control을 잃어버릴 수 있다.


2. 외부에서 collection을 교체해버리도록 하지 말것.

외부의 코드는 collection의 타입(List or ...)을 고를 책임이 없어야 한다.



IReadOnlyCollection을 사용하면 외부에서 collection을 조작할 수 없다. 그러나 이 의미가 collection 자체가 더이상 변경될 수 없을을 뜻하지는 않는다. 만약 정말 collection이 변하지 말길 바란다면 Immutable Collection을 사용해라.


-끝.


* 참조: http://waf.codeplex.com/discussions/579918

Posted by dngchn