페북에 올라온 내용을 확인하기 위해서 앱을 열어보니 동일한 현상이 발생합니다.


https://social.msdn.microsoft.com/Forums/vstudio/en-US/d5ca935d-cec3-4a3b-9c22-a452c6e7a0f3/visual-statesblendwindows-10-fall-creators-updatewindows-visual-states-and-data-tabs?forum=visualstudiogeneral


이 문제에 관해서 질문은 올라왔는데..해결 방법은 아직 없네요..


그래서, 일단 아래 화면처럼 버전을 변경합니다.

빌드를하고,, 비주얼스튜디오를 종료 합니다.




다시 프로젝트를 열면 자동으로 load가 않되는데.. 그걸 마우스 오른쪽 클릭해서 Reload project를 선택해서 불러 옵니다.

그러면 아래 내용 처럼 사용이 가능합니다.




이 방법으로도 않된다면..좀 기다려야 할 것 같습니다. ㅜㅜ 


블로그 이미지

kaki104

/// Microsoft MVP - Windows Development - Apr 2014 ~ Mar 2018 /// email : kaki104@daum.net, twitter : @kaki104, facebook : https://www.facebook.com/kaki104 https://www.facebook.com/groups/w10app/

페이스북에 올라온 민원 해결을 위해서 간단하게 프로젝트를 만들었습니다.

Q. 텍스트박스에 숫자를 입력하면 자동으로 컴마를 찍어주고, 백스페이스를 누르면 삭제가 되도록 하고 싶습니다.~

A. 일단 여러분들이 의견 주셨습니다.

우선 컨버터를 이용한 방법이 이야기가 되어서, 저도 컨버터를 이용해서 처리를 할려고 해봤는데..

컨버터는 프로퍼티 체인지 이벤트가 발생했을 경우에 컨버터가 값을 변경해 주는 역할을 합니다...그런데.. TextBlock에는 뷰모델에서 변경된 내용을 바로 화면에 이쁘게 뿌려주는데.. 텍스트박스에서는 키가 입력되면, 그 내용을 바로 뷰모델에 값을 넣어주는 역할만을 하고, 프로퍼티가 변경된 내용을 화면에 다시 뿌려주지는 않습니다.

StringFormatConverter.cs

using System;
using Windows.UI.Xaml.Data;
namespace App1
{
    public class StringFormatConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, string language)
        {
            var format = parameter as string;
            if (!string.IsNullOrEmpty(format) && value is string)
            {
                int.TryParse(value.ToString(), out var number);
                return string.Format(format, number);
            }
            return value;
        }
        public object ConvertBack(object value, Type targetType, object parameter, string language)
        {
            if (value == null) return null;
            int.TryParse(value.ToString().Replace(",", ""), out var number);
            return number.ToString();
        }
    }
}

음..그래서 결국 비헤이비어를 만들어서 처리를 해보았습니다.

NumberTextBoxBehavior.cs

using Windows.System;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;
using Microsoft.Xaml.Interactivity;
namespace App1
{
    public class NumberTextBoxBehavior : Behavior<TextBox>
    {
        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.KeyUp += AssociatedObject_KeyUp;
        }
        private void AssociatedObject_KeyUp(object sender, KeyRoutedEventArgs e)
        {
            switch (e.Key)
            {
                case VirtualKey.Number0:
                case VirtualKey.Number1:
                case VirtualKey.Number2:
                case VirtualKey.Number3:
                case VirtualKey.Number4:
                case VirtualKey.Number5:
                case VirtualKey.Number6:
                case VirtualKey.Number7:
                case VirtualKey.Number8:
                case VirtualKey.Number9:
                case VirtualKey.Back:
                    var numberText = AssociatedObject.Text;
                    if (string.IsNullOrEmpty(numberText)) return;
                    var number = int.Parse(numberText.Replace(",", ""));
                    var formatString = string.Format("{0:N0}", number);
                    if (formatString != number.ToString())
                    {
                        AssociatedObject.Text = formatString;
                        AssociatedObject.SelectionStart = AssociatedObject.Text.Length;
                    }
                    break;
            }
        }
        protected override void OnDetaching()
        {
            AssociatedObject.KeyUp -= AssociatedObject_KeyUp;
            base.OnDetaching();
        }
    }
}


MainPage.xaml


<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:App1"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:Custom="using:Microsoft.Xaml.Interactivity"
    x:Class="App1.MainPage"
    xmlns:converters="using:Microsoft.Toolkit.Uwp.UI.Converters"
    mc:Ignorable="d"
    x:Name="mainPage">
    <Page.Resources>
        <local:StringFormatConverter x:Key="StringFormatConverter" />
    </Page.Resources>
    <Page.DataContext>
        <local:MainPageViewModel />
    </Page.DataContext>

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <StackPanel VerticalAlignment="Center" HorizontalAlignment="Center">
            <TextBox MinWidth="200" TextAlignment="Right"
                     Text="{Binding Number, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
                <Custom:Interaction.Behaviors>
                    <local:NumberTextBoxBehavior />
                </Custom:Interaction.Behaviors>
            </TextBox>

            <TextBlock Text="{Binding Number}" />
            <Button Click="ButtonBase_OnClick" />
        </StackPanel>
    </Grid>
</Page>


결과 화면입니다.



파일

NumberTextBox.zip


블로그 이미지

kaki104

/// Microsoft MVP - Windows Development - Apr 2014 ~ Mar 2018 /// email : kaki104@daum.net, twitter : @kaki104, facebook : https://www.facebook.com/kaki104 https://www.facebook.com/groups/w10app/

Tag Behavior, UWP

DependencyProperty의 개념 소개 및 사용 방법에 대한 포스트 입니다.



Dependency Property와 Attached Property의 차이점이 뭐지? part0

Dependency Property와 Attached Property의 차이점이 뭐지? part1




이미 이전 part1이 공개되었는데.. 사실은 이 포스트가 먼저 작성이 되어야 하는 것이였네요..그래서 part0입니다. 우선 어려운 주제를 다루기에 앞서 이야기를 하나 풀어 보겠습니다.



사람과 사람은 서로 공통점과 차이점이 존재합니다. 차이점에는 얼굴 크기 모양, 눈 컬러, 머리카락 길이 등이 있고, 공통점에는 손가락 10개, 눈 2개, 머리 1개 등의 정보들이 있습니다. 공통적인 정보들은 대부분 평범한 내용들로 구성됩니다.


그런데 말입니다. 누군가 당신한테 인간의 유전자 수는 몇개 인가요? 라고 물어 본다면 당신은 그 질문의 대답을 어디서 가지고 올까요? 어떤 사람은 유전자 수를 종이에 적어서 뒷 주머니에 넣고 다니고 있어서, 그 질문을 하면 바로 찾아서 알려줄 수 있을 것 입니다. 하지만, 일반적으로 그런 정보를 종이에 적어서 가지고 다니는 사람은 없을 것이라는 생각이 드네요


만약, 그런 사소한 정보들까지 모두 가지고 다녀야 한다면, 아마도 제 뒷주머니의 크기는 아주 큰 백과사전이 들어갈 수 있을 정도가 되어야 할지도 모릅니다. 


그래서, 누군가가 이런 아이디어를 내 놓습니다. 인간에 대한 공통적인 정보는 대부분은 동일하며, 거의 변하지 않는다. 그렇다면, 그런 정보들을 한 곳에 저장해 놓고 찾아서 보면 되지 않을까? 그리고, 예외적인 정보만 종이에 적어서 뒷 주머니에 넣고 다니면서 본다면??

그래서, 사람에 대한 공통적인 정보는 공용 저장소에 저장하고 예외적인 정보는 로컬 저장소에 저장하게 되었다는...이야기가...쿨럭;; (참고 포스트 중 일부를 인용했습니다.)



0. 참고


What is a dependency property?

Dependency Properties

Dependency properties overview


1. 개요


DependencyProperty직역하면 종속속성이라고 합니다. 이 속성을 사용하기 위해서는 DependencyObject를 상속 받은 클래스나 컨트롤에서만 사용이 가능합니다. 이 녀석은 WPF(Windows Presentation Foundation)에서 처음 등장했으며, 이 후 Silverlight, UWP 앱 등 XAML을 기반으로 하는 개발 환경에서 다양하게 사용되고 있습니다. 



2. DependencyProperty 의 기본적인 구성


1) Dependency property: 여기서 이야기 하는 종속속성은 DependencyProperty를 이야기 합니다.


2) Dependency property identifier: 종속속성 식별자는 IsSpinningProperty를 이야기 합니다. 이 식별자는 종속속성의 값을 저장하고, 불러올 때 키 값으로 사용됩니다.


3) CLR(Common Language Runtime) "wrapper": Public bool IsSpinning... 부분을 이야기 하며, 이 종속속성에 값을 넣거나 불러올 때 코드나 XAML에서 실제로 이용하게 됩니다. 그래서, 절대로 내용을 수정하면 않됩니다.


public static readonly DependencyProperty IsSpinningProperty =
    DependencyProperty.Register(
    "IsSpinning", typeof(Boolean),
    typeof(MyCode)
    );
public bool IsSpinning
{
    get { return (bool)GetValue(IsSpinningProperty); }
    set { SetValue(IsSpinningProperty, value); }
}



쉽게 DependencyProperty를 만드는 방법은 Ctrl+K,X를 입력 후에 NetFX30을 선택하시면 선택하는 메뉴가 출력됩니다. part1에 자세한 사항이 나오니 참고하시면 됩니다.



3. DependencyProperty가 GetValue의 동작원리


간단한 예제를 통해서 알아 보도록 하겠습니다.


MainPage.xaml


<Page
    x:Class="PropertySample.Views.MainPage"
    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:commons="using:PropertySample.Commons"
    Style="{StaticResource PageStyle}"
    mc:Ignorable="d">
    <Grid
        x:Name="ContentArea"
        Margin="{StaticResource MediumLeftRightMargin}">

        <Grid.RowDefinitions>
            <RowDefinition x:Name="TitleRow" Height="48"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <TextBlock
            x:Name="TitlePage"
            x:Uid="Main_Title"
            Style="{StaticResource PageTitleStyle}" />

        <StackPanel Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Center">
            <
TextBlock Text="Part0" Margin="10"/>
            <TextBlock Text="Part1" Margin="10"/>
        </StackPanel>

    </Grid>
</Page>


StackPanel 내부에 2개의 TextBlock을 배치했습니다. 너무 붙어있지 못하도록 마진을 10씩 넣었습니다. 겨울이라 서로 멀리 떨어져 있도록...




그런데, TextBlock의 글씨 크기를 더 크게 만들고 싶네요..여기에는 2가지 방법이 있습니다.


하나는 각 TextBlock에 FontSize를 추가하는 일반적인 방법입니다. 물론 스타일로 지정해도 가능합니다.~ 


그렇다면, 다른 하나는??


<Page
    x:Class="PropertySample.Views.MainPage"
    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:commons="using:PropertySample.Commons"
    Style="{StaticResource PageStyle}"
    mc:Ignorable="d" FontSize="30">


Page에 FontSize를 추가하는 것 입니다. 


Page에 FontSize를 추가 했는데..어떻게 TextBlock의 FontSize가 커질까요?? 


이 비밀의 열쇄는 처음에 사람으로 이야기를 했던 부분에 있습니다. 


우선 FontSize를 찾아 내는 방법 입니다.



다음은 FontFamily를 찾아 내는 방법 입니다.



이렇게 GetValue가 호출이 될 때 값을 찾아서 반환하는 것을 resolved dynamically 이라고 합니다.


결과는 다음과 같습니다.


4. DependencyProperty를 사용하면 어떤 장점이 있을까요?


어떤 장점이 있길래 이런 복잡한 방법으로 데이터를 가지고 올까요?


1) 메모리 사용량 감소 : 화면에 모든 컨트롤들이 자신의 프로퍼티 값을 가지고 있다면,, 그 양이 얼마나 커질까요? 하지만, 이런 방법으로 누군가 프로퍼티의 값을 요청하면 찾아보고 알려줄 수 있기 때문에 메모리를 더 적게 사용할 수 있습니다.


2) 값 상속 : 비주얼 트리의 하위 컨트롤들은 상위 컨트롤이 가지고 있는 프로퍼티의 값을 상속 받을 수 있습니다. Style들을 이용해서 다양한 형태로 값을 상속 시킬 수 있습니다.


3) 값 변경 알림 사용 : DependencyProperty는 값이 변경될 때 MVVM pattern에서 처럼 INotifyPropertyChanged 이벤트를 발생 시키며, DataBinding도 연결할 수 있고, Callback을 등록해서 사용할 수 있는 다양한 기능을 제공합니다.



5.  다음 항목들을 참고하세요


사용자 정의 컨트롤을 만드는 방법에 대한 포스트 입니다. Part1~Part4번까지 있습니다.

Custom Control 만들기 Part1



6. 소스

https://github.com/kaki104/PropertySample


블로그 이미지

kaki104

/// Microsoft MVP - Windows Development - Apr 2014 ~ Mar 2018 /// email : kaki104@daum.net, twitter : @kaki104, facebook : https://www.facebook.com/kaki104 https://www.facebook.com/groups/w10app/

옛날 옛날 언제인지도 기억나지 않는 시절에 생긴 의문이 바로 이 것이였습니다. 그동안, 그냥 저냥 구렁이 담넘어가 듯이 넘어가다가 진행 중인 프로젝트에서 각 DependencyProperty, AttachedProperty들을 대량으로 사용하고 있어서 이번 기회에 정리를 하기위해 포스트를 하려고 합니다.



0. 참고

Understanding WPF Dependency Property and Attached Property

Dependency properties overview

Attached Properties Overview


셈플 환경은 VisualStudio 2017, UWP 앱입니다. 하지만, 일반적인 내용이기 때문에 WPF에서도 사용이 가능합니다.



1. 공통점


1) 만드는 방법이 비슷합니다. 코드에서 Ctrl+K,X -> NetFX30 -> DependencyProperty, AttachedProperty 2개 중에 하나를 선택하면 기본 골격이 만들어 집니다.


코드 비하인드에서 아래와 같은 화면이 나오면서 입력 할 수 있습니다.





2) DependencyProperty, AttachedProperty의 타입은 모두 DependencyProperty입니다. 그래서, Binding을 사용할 수 있으며  PropertyChanged 이벤트가 발생합니다.



DependencyProperty의 Type은 DependencyProperty이며, Register를 이용해서 등록됩니다.


        public string GuideText
        {
            get { return (string)GetValue(GuideTextProperty); }
            set { SetValue(GuideTextProperty, value); }
        }

        // Using a DependencyProperty as the backing store for GuideText.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty GuideTextProperty =
            DependencyProperty.Register("GuideText", typeof(string), typeof(GuideTextBox), new PropertyMetadata(null));



AttachedProperty의 Type은 DependencyProperty이며, RegisterAttached를 이용해서 등록됩니다.


        public static string GetBindablePassword(DependencyObject obj)
        {
            return (string)obj.GetValue(BindablePasswordProperty);
        }

        public static void SetBindablePassword(DependencyObject obj, string value)
        {
            obj.SetValue(BindablePasswordProperty, value);
        }

        // Using a DependencyProperty as the backing store for BindablePassword.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty BindablePasswordProperty =
            DependencyProperty.RegisterAttached("BindablePassword", typeof(string), typeof(ExtensionPropertys), new PropertyMetadata(null));




3) 이 녀석들의 용도는 컨트롤, 비헤이비어, 컨버터 등등에 기존에 없는 사용자 정의 속성을 추가해서 사용하는 것 입니다.




2. AttachedProperty의 특징


1) AttachedProperty에 대한 설명을 할 때 사용하는 컨트롤이 WPF에서는 DockPanel 입니다. 그런데 UWP에서는 기본적으로는 제공하지 않으니.. Canvas 컨트롤를 이용하는 방법을 살펴 보도록 하겠습니다. 이 두 컨트롤의 특징은 하위 아이템들이 자신의 위치를 지정할 때 부모의 프로퍼티값을 직접 변경하는 구조라는 것입니다.


아래 빨간색 Rectangle가 보입니다. 이녀석의 위치를 왼쪽에서 100, 위에서 100의 위치로 이동을 시키기 위해서 어떻게 할까요?



Canvas.Top="100" Canvas.Left="100" 이라는 값을 지정하면 해당 위치로 이동 합니다. 여기서 Canvas.Top, Canvas.Left가 바로 AttachedProperty입니다. 자식 컨트롤에서 부모 컨트롤의 속성을 변경해야 내 위치가 변경되는 구조를 가지고 있습니다.


아마도, 이런 구조를 가지는 이유는 Canvas는 어떤 컨트롤이 자식으로 등록될지, 몇개가 등록될지에 대한 관리를 하지 않거나 필요성을 느끼지 못하기 때문인 것 같습니다. 


너~의 위치는 너 스스로 알려줘라..그러면 그 값을 기준으로 화면에 뿌려는 줄께~의 느낌이네요.



혹시 이런식으로 프로퍼티를 사용해야 한다면, AttachedProperty를 사용하셔야 합니다.



2) 서비스 성격의 AttachedProperty 만들기


AttachedProperty는 일반 클래스, 컨트롤, 비헤이비어, 컨버터 등등에 추가하고, 어디서든 값 입력/조회가 가능한 범용성을 가지고 있습니다. 그래서, 이것을 두고 서비스 성격의 프로퍼티라고 이야기를 합니다.


예를 들어서 보면, 비밀번호를 입력하는 PasswordBox는 보안을 이유로 Password 프로퍼티에 바인딩이 불가능 합니다. 또한, PasswordBox를 상속받는 컨트롤을 만들 수도 없습니다. 역시 보안상의 문제이겠죠? 이런 경우에 문제를 해결하는 가장 쉬운 방법은 AttachedProperty를 이용하는 것입니다.


우선 아래와 같이 2개의 AttachedProperty를 만들어 줍니다. BindablePassword는 바인딩을 하기 위한 프로퍼티이고, IsPasswordBindable는 바인딩을 할지 말지 여부를 정하는 녀석입니다. 여기서! 왜 두개나 필요하나요?? 라는 궁금증을 가질 수 있을 것 같습니다. 


그 이유는 BindablePassword 프로퍼티 한개만 가지고는 PasswordBox와 연결을 해서 값을 가지고 올 수 없기 때문입니다. 값이 변경이 되면 변경된 값을 넣어야 하는데..이벤트 연결이 않되어 있기 때문입니다. 그래서, IsPasswordBindable 프로퍼티가 True가 되면, PasswordChanged 이벤트 핸들을 추가하고, 그 이벤트 핸들러 내에서 BindablePassword에 Password 값을 변경하는 형태로 이용합니다.



ExPropertys.cs


    public class ExPropertys
    {
        public static string GetBindablePassword(DependencyObject obj)
        {
            return (string)obj.GetValue(BindablePasswordProperty);
        }

        public static void SetBindablePassword(DependencyObject obj, string value)
        {
            obj.SetValue(BindablePasswordProperty, value);
        }

        // Using a DependencyProperty as the backing store for BindablePassword.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty BindablePasswordProperty =
            DependencyProperty.RegisterAttached("BindablePassword", typeof(string), typeof(ExPropertys), new PropertyMetadata(null));



        public static bool GetIsPasswordBindable(DependencyObject obj)
        {
            return (bool)obj.GetValue(IsPasswordBindableProperty);
        }

        public static void SetIsPasswordBindable(DependencyObject obj, bool value)
        {
            obj.SetValue(IsPasswordBindableProperty, value);
        }

        // Using a DependencyProperty as the backing store for IsPasswordBindable.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty IsPasswordBindableProperty =
            DependencyProperty.RegisterAttached("IsPasswordBindable", typeof(bool), typeof(ExPropertys), new PropertyMetadata(false, IsPasswordBindableChanged));

       

        private static void IsPasswordBindableChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            //이렇게 사용하는 것은 저도 처음 입니다. VisualStudio 2017에서만 가능할 듯 합니다.
            if (!(d is PasswordBox passwordBox)) return;

            if ((bool) e.NewValue)
            {
                passwordBox.PasswordChanged += PasswordBox_PasswordChanged;
            }
            else
            {
                passwordBox.PasswordChanged -= PasswordBox_PasswordChanged;

            }
        }

        private static void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
        {
            var passwordBox = (PasswordBox) sender;
            SetBindablePassword(passwordBox, passwordBox.Password);

        }
    }    


MainPage.xaml


<Page
    x:Class="PropertySample.Views.MainPage"
    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:commons="using:PropertySample.Commons"
    Style="{StaticResource PageStyle}" FontSize="20"
    mc:Ignorable="d">
    <Grid
        x:Name="ContentArea"
        Margin="{StaticResource MediumLeftRightMargin}">

        <Grid.RowDefinitions>
            <RowDefinition x:Name="TitleRow" Height="48"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <TextBlock
            x:Name="TitlePage"
            x:Uid="Main_Title"
            Style="{StaticResource PageTitleStyle}" />

        <Grid
            Grid.Row="1"
            Background="{ThemeResource SystemControlPageBackgroundChromeLowBrush}">

            <StackPanel VerticalAlignment="Center">
                <TextBlock Text="Property Sample" Margin="20"/>
                <TextBlock Text="PasswordBox"/>
                <PasswordBox x:Name="passwordBox" commons:ExPropertys.IsPasswordBindable="True"/>
                <TextBlock Text="Password you entered"/>
                <TextBlock Text="{Binding (commons:ExPropertys.BindablePassword), ElementName=passwordBox}"/>
            </StackPanel>
        </Grid>
    </Grid>
</Page>


실행 결과





3) AttachedProperty는 단독으로 사용할 수도 있기 범용성이 좋지만, XAML Style에서 Trigger를 사용해서 값을 직접 비교하는 것은 불가능 합니다. 이는 실제 값(Value)를 저장하고 사용하기 위해서 2개의 메소드로 구성되어 있기 때문입니다. 


아래와 같은 2개의 메소드로 구분되어 있는 이유는 값이 설정된 인스턴스의 CLR 네임스페이스의 일부가 아니기 때문입니다.

passwordBox에 값을 저장하고는 있지만, 저장하고 불러올 때 사용하는 이름을 passwordBox는 모릅니다. 그러니 직접 모든 이름을 다 입력해 줘야지만 사용할 수 있습니다.


        public static string GetBindablePassword(DependencyObject obj)
        {
            return (string)obj.GetValue(BindablePasswordProperty);
        }

        public static void SetBindablePassword(DependencyObject obj, string value)
        {
            obj.SetValue(BindablePasswordProperty, value);
        }


        private static void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e)
        {
            var passwordBox = (PasswordBox) sender;
            SetBindablePassword(passwordBox, passwordBox.Password);
        }



4) AttachedProperty를 단독으로 사용하는 경우 이벤트 핸들러 추가/제거를 하는 시점을 특정하기가 어렵다는 것입니다. 위의 예에서 이벤트 핸들러를 제거하기 위해서는, PasswordBox의 Unloaded 이벤트 핸들을 추가로 걸어서 처리를 해야합니다. 



3. 소스


UWP로 만들어진진 소스입니다. Visual Studio 2017에서 오픈 가능합니다.


PropertySample_part1.zip



4. 포스트 쓰는데..


시간이 너무 오래 걸렸네요..처음에 가벼운 마음으로 시작했는데..ㅋㅋ 옆에 계시는 분이 기대를 한다고 해서..신경을 쓰다보니..

빨리 Part2 진행 하도록 하겠습니다.



블로그 이미지

kaki104

/// Microsoft MVP - Windows Development - Apr 2014 ~ Mar 2018 /// email : kaki104@daum.net, twitter : @kaki104, facebook : https://www.facebook.com/kaki104 https://www.facebook.com/groups/w10app/

저녁마다 앱 만든다고 뚝딱뚝딱하고 있다가, Web Service에서도 사용해야하는 데이터가 있어서, PCL(Portable Class Library)를 추가하려고 했는데.. 않되더군요..쿨럭 이게 무슨일인가하고 내용을 잘 살펴보니 PCL은 Visual Studio 2013까지만 지원을하고 Visual Studio 2017에서는 .Net Standard를 사용하라고 나와있더군요..그래서, 공부도 할겸 포스팅을 하기로 했습니다.



1. .Net Standard가 뭔가요?


과거에 우리는 이렇게 복잡한 세상에서 개발을 진행 했습니다. 닷넷 프레임웍 따로, 닷넷 코어 따로, 자마린 따로..

클래스 라이브러리도 각각 만들어서 사용하고 있었죠..




이런 복잡한 구조를 개선하기 위해 닷넷 스텐다드가 추가되었습니다!


1) 닷넷 스탠다드는 코드를 공유하기 편리합니다.


과거 PCL을 이용해서 코드를 공유하던 것을 쉽게 대체할 수 있습니다.


2) 더 많은 API를 사용할 수 있습니다.


1.6에서 1.3k의 정도의 API를 사용할 수 있었지만, 2.0은 32k의 API 사용이 가능합니다.


3) 닷넷 프레임웍과 호환성이 좋습니다.


대부분의 NuGet package들이 닷넷 프레임웍을 대상으로 만들어져 있습니다. 그래서, 호환성을 높여서 닷넷 스텐다드 2.0을 이용할 경우 NuGet package의 70% 정도를 그대로 사용할 수 있습니다. 




위에서도 보았듯이 지원하는 플랫폼이 상당히 많습니다. 1.0이 나온것이 2016년이였는데(1.0 출시 안내).. 어느덧 2.0이..그동안 PCL만 사용했었는데..이제는 이 녀석을 이용해서 개발을 해야 할 것 같습니다.


.NET 표준 1.0 1.1 1.2 1.3 1.4 1.5 1.6 2.0
.NET Core 1.0 1.0 1.0 1.0 1.0 1.0 1.0 2.0
.NET Framework(.NET Core 1.x SDK 포함) 4.5 4.5 4.5.1 4.6 4.6.1 4.6.2
.NET Framework(.NET Core 2.0 SDK 포함) 4.5 4.5 4.5.1 4.6 4.6.1 4.6.1 4.6.1 4.6.1
Mono 4.6 4.6 4.6 4.6 4.6 4.6 4.6 5.4
Xamarin.iOS 10.0 10.0 10.0 10.0 10.0 10.0 10.0 10.14
Xamarin.Mac 3.0 3.0 3.0 3.0 3.0 3.0 3.0 3.8
Xamarin.Android 7.0 7.0 7.0 7.0 7.0 7.0 7.0 8.0

Universal Windows Platform (UWP)

10.0 10.0 10.0 10.0 10.0 16299 16299 16299
8.0 8.0 8.1
Windows Phone 8.1 8.1 8.1
Windows Phone Silverlight 8.0


UWP 지원에 대한 부분은 여기를 참고하세요. 

대충 아시겠죠? 이제 직접 사용해 보도록 하겠습니다.


닷넷 스탠다드 2.0에서 사용가능한 API 조회는 여기서~



2. .NET Standard 클래스 라이브러리 만들기


Windows Template Studio를 이용해서 Blank, Basic MVVM pattern 프로젝트를 생성합니다.


혹시 Windows Template Studio를 모르시는 분은 여기를 클릭하세요


이제 .Net Standard 프로젝트를 여기에 추가해줍니다.


솔루션에서 마우스 오른쪽 Add -> New Project...



.NET Standard선택 -> Class Libaray (.NET Standard) 선택 -> 이름 입력 후 OK



추가된 프로젝트 속성을 확인해보면 .NET Standard 2.0이라고 표시된 것을 확인할 수 있습니다.




3. UWP앱이랑 연결을 시키기..


UWP앱에서 마우스 오른쪽 -> Add -> Reference...선택



Projects -> NetStandard.Standard 프로젝트를 체크 하고 OK를 누르면 완료 입니다.



빌드를 해보도록 하죠.


이런..빌드를 했더니, 오류가 쫘르륵!! 위의 표에서 보면 이유를 알 수 있습니다. .NET Standard 2.0은 Windows 10 Fall Creators Update 버전만 지원합니다. 그래서, UWP 앱의 최소 지원 버전을 변경해 주어야 합니다.



UWP앱의 속성에 들어갓가서 아래와 같이 최소 버전을 Fall Creators Update로 변경해 주시고 다시 빌드를 하시면 완료됩니다.




4. DataSet 사용이 가능??


닷넷의 오래된 기능으로 메모리에 데이터를 저장하는 DataSet을 사용할 수 있었습니다. 그런데, 그동안 UWP에서는 지원이 앙되던 녀석이 였는데, .NET Standard 2.0에서 부활 했습니다.~


아래와 같이 TestClass를 만들어 보도록 하겠습니다.

* 아래 코드는 여기서 복사했습니다.


using System.Data;

namespace NetStandard.Standard.Helpers
{
    public static class TestClass
    {
        public static DataSet GetXmlFromDataSet()
        {
            // Create two DataTable instances.
            var table1 = new DataTable("patients");
            table1.Columns.Add("name");
            table1.Columns.Add("id");
            table1.Rows.Add("sam", 1);
            table1.Rows.Add("mark", 2);

            var table2 = new DataTable("medications");
            table2.Columns.Add("id");
            table2.Columns.Add("medication");
            table2.Rows.Add(1, "atenolol");
            table2.Rows.Add(2, "amoxicillin");

            // Create a DataSet and put both tables in it.
            var set = new DataSet("office");
            set.Tables.Add(table1);
            set.Tables.Add(table2);
           
            // Visualize DataSet.
            return set;
        }
    }
}




이 녀석을 UWP의 ViewModel에서 호출해서 사용해 보겠습니다.


리샤퍼를 사용하시는 경우에는 업데이트를 해야 인텔리 센스가 정상 동작하는 것 같습니다. 참고해주세요~

JetBrains ReSharper Ultimate 2017.2.2  Build 109.0.20171006.122324
ReSharper 2017.2.20171006.123800


using Windows.UI.Xaml.Navigation;
using NetStandardSample.Helpers;
using NetStandardSample.Services;
using NetStandard.Standard.Helpers;


namespace NetStandardSample.ViewModels
{
    public class MainViewModel : Observable
    {
        private string _xmlText;

        public MainViewModel()
        {
            NavigationService.Navigated += NavigationService_Navigated;
        }

        public string XmlText
        {
            get => _xmlText;
            set => Set(ref _xmlText, value);
        }

        private void NavigationService_Navigated(object sender, NavigationEventArgs e)
        {
            var set = TestClass.GetXmlFromDataSet();
            if (set == null) return;
            XmlText = set.GetXml();

        }
    }
}


MainPage.xaml


MainPage.xaml.cs에 


//public MainViewModel ViewModel { get; } = new MainViewModel(); 문장은 주석 처리했습니다.


<Page
    x:Class="NetStandardSample.Views.MainPage"
    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:viewModels="using:NetStandardSample.ViewModels"
    Style="{StaticResource PageStyle}"
    mc:Ignorable="d">
    <Page.DataContext>
        <viewModels:MainViewModel />
    </Page.DataContext>

    <Grid
        x:Name="ContentArea"
        Margin="{StaticResource MediumLeftRightMargin}">

        <Grid.RowDefinitions>
            <RowDefinition x:Name="TitleRow" Height="48" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>

        <TextBlock
            x:Name="TitlePage"
            x:Uid="Main_Title"
            Style="{StaticResource PageTitleStyle}" />

        <Grid
            Grid.Row="1"
            Background="{ThemeResource SystemControlPageBackgroundChromeLowBrush}">
            <TextBox Text="{Binding XmlText, Mode=TwoWay}" AcceptsReturn="True" />
        </Grid>
    </Grid>
</Page>


실행 결과는 아래와 같습니다. 정상적으로 DataSet을 반환 받아서, 메소드까지 실행해서 xml형태로 화면에 출력했습니다.


그렇다면.. 다른 프로젝트에서도 동일하게 동작할까용?



콘솔 프로젝트를 추가하고 사용해 보았습니다. 결과는 대만족입니다.




5. 소스


NetStandard.Sample.zip



PS. 그런데..Fall Creators Update UWP에서는 property marker가 제대로 동작하지 않는 버그가 있습니다. 신고는 했구용 다음번 업데이트 때 개선될 것으로 예상합니다. 흐흐


블로그 이미지

kaki104

/// Microsoft MVP - Windows Development - Apr 2014 ~ Mar 2018 /// email : kaki104@daum.net, twitter : @kaki104, facebook : https://www.facebook.com/kaki104 https://www.facebook.com/groups/w10app/

티스토리 툴바