티스토리 뷰

반응형

실버라이트 개발시에는 Trigger와 Behavior를 이용해서 MVVM pattern으로 Loosly-coupled한 개발이 가능했는데, Windows 8으로 오면서 Blend에서 Trigger와 Behavior가 존재하지 않아서 cs페이지에 이벤트 코딩을 추가해야 하는 경우가 많아 졌다.

그래서, Custom Behavior를 직접 만들어서 추가하고, 사용하는 방법에 대해서 알아보도록 하겠다.

 

Custom Behavior 장점

1) xaml.cs에 컨트롤에 대한 이벤트 코딩을 할 필요가 없다. 

2) 각 화면별로 중복되는 cs코드가 있는 경우 코드를 줄이는 역할을 한다.

3) 좀 더 개선된 Behavior를 만들 수 있다면, ViewModel의 Command를 직접 호출하는 Behavior도 만들어서 사용 한다면, 더 좋은 효과를 얻을 수 있다.

 

Custom Behavior의 단점

1) 블랜드에서 직접 Drag&Drop으로 사용 할 수 없다. 수동으로 처리해야 한다.

 

참고 & 필수 패키지

WinRT XAML Toolkit

http://winrtxamltoolkit.codeplex.com/

Using Behaviors in Windows 8 Store Apps

http://blogs.u2u.be/diederik/post/2012/10/28/Using-Behaviors-in-Windows-8-Store-Apps.aspx

 

1. Split App(XAML) 새 프로젝트를 생성한다.

여기서 만들 Behavior는 참고 포스트에서 다루고 있는 TextBox 컨트롤에 포커스가 되었을 때 모든 텍스트를 선택하는 것이다. 다른 점은 WinRT XAML Toolkit에 있는 Behavior를 이용하고, 내용도 약간 수정을 했다.

 

프로젝트에 WinRT XAML Toolkit을 설치한다.

 

 

2. SelectAllOnFocusBehavior.cs

 

    public class SelectAllOnFocusBehavior : Behavior<TextBox>
    {
        /// <summary>
        /// 디자인 타임 프로퍼티 추가
        /// </summary>
        public static readonly DependencyProperty SelectAllOnFocusProperty =
            DependencyProperty.RegisterAttached
            (
                "SelectAllOnFocus",
                typeof(bool),
                typeof(SelectAllOnFocusBehavior),
                new PropertyMetadata(false, OnSelectAllOnFocus)
            );

 

        /// <summary>
        /// 접근 프로퍼티 추가 - 소스에서 이 부분 수정 필요
        /// </summary>
        public bool SelectAllOnFocus
        {
            get { return (bool)GetValue(SelectAllOnFocusProperty); }
            set { SetValue(SelectAllOnFocusProperty, value); }
        }


        /// <summary>
        /// 프로퍼티 변경 이벤트 처리
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>
        protected static void OnSelectAllOnFocus(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var source = (SelectAllOnFocusBehavior)d;
            source.SelectAllOnFocus = (bool)e.NewValue;
        }

        /// <summary>
        /// 생성자
        /// </summary>
        public SelectAllOnFocusBehavior()
        {
            SelectAllOnFocus = true;
        }

        /// <summary>
        /// 컨트롤에 Attach될 때
        /// </summary>
        protected override void OnAttached()
        {
            base.OnAttached();
            //AssociatedObject는 Behavior<TextBox> 문장에 의해서 TextBox가 됨
            if (AssociatedObject != null)
            {
                //GetFocus 이벤트 처리 추가
                AssociatedObject.GotFocus += AssociatedObject_GotFocus;
            }
        }

        /// <summary>
        /// 컨트롤에서 Detach될 때
        /// </summary>
        protected override void OnDetaching()
        {
            if (AssociatedObject != null)
            {
                //GetFocus 이벤트 처리 제거
                AssociatedObject.GotFocus -= AssociatedObject_GotFocus;
            }
            base.OnDetaching();
        }

        /// <summary>
        /// GetFoucs 처리
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AssociatedObject_GotFocus(object sender, RoutedEventArgs e)
        {
            if (AssociatedObject != null)
            {
                if (SelectAllOnFocus)
                {
                    //모든 텍스트 선택 메소드 실행
                    AssociatedObject.SelectAll();
                }
            }
        }
    }

 

위의 코딩 방식은 사용자 정의 컨트롤을 만들 때에도 사용이 되므로 익혀 놓으면 도움이 된다.

F6을 눌러서 빌드를 한다.

 

3. ItemsPage.xaml

 

네임스페이스 추가해준다.

    xmlns:interactivity="using:WinRTXamlToolkit.Interactivity"

 

타이틀 부분에 아래 코드를 추가해 준다.

        <!-- Back button and page title -->
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="Auto"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <Button x:Name="backButton" Click="GoBack" IsEnabled="{Binding Frame.CanGoBack, ElementName=pageRoot}" Style="{StaticResource BackButtonStyle}"/>
            <TextBlock x:Name="pageTitle" Grid.Column="1" Text="{StaticResource AppName}" IsHitTestVisible="false" Style="{StaticResource PageHeaderTextStyle}"/>
            <TextBox Grid.Column="1" HorizontalAlignment="Center" VerticalAlignment="Center" Width="200">
                <interactivity:Interaction.Behaviors>
                    <local:SelectAllOnFocusBehavior SelectAllOnFocus="{Binding IsSelectAllFocus, Mode=TwoWay}"/>
                </interactivity:Interaction.Behaviors>

            </TextBox>

            <CheckBox x:Name="checkBox" Grid.Column="1" HorizontalAlignment="Right" Content="Select All Text" Margin="0,0,50,0"/>

        </Grid>

디자인 타임에 프로퍼티를 보면 SelectAllOnFocus 프로퍼티를 볼 수 있다.

(비헤이비어 프로퍼티에 접근이 않되면 인터렉션 부분을 다른 영역으로 이동해서 바인딩하고 다시 원래 위치로 이동한다)

 

 

CheckBox는 SelectAllOnFocus 프로퍼티를 변경하기 위해 추가했다.

 

4. ItemsPage.xaml.cs

 

    public sealed partial class ItemsPage : BehaviorSample.Common.LayoutAwarePage
    {
        public ItemsPage()
        {
            this.InitializeComponent();

            checkBox.Click +=
                (s, e) =>
                {
                    var cb = e.OriginalSource as CheckBox;
                    this.DefaultViewModel["IsSelectAllFocus"] = cb.IsChecked;
                };

        }

        protected override void LoadState(Object navigationParameter, Dictionary<String, Object> pageState)
        {
            var sampleDataGroups = SampleDataSource.GetGroups((String)navigationParameter);
            this.DefaultViewModel["Items"] = sampleDataGroups;

            //기본 설정
            checkBox.IsChecked = true;
            this.DefaultViewModel["IsSelectAllFocus"] = true;

        }

        void ItemView_ItemClick(object sender, ItemClickEventArgs e)
        {
            var groupId = ((SampleDataGroup)e.ClickedItem).UniqueId;
            this.Frame.Navigate(typeof(SplitPage), groupId);
        }
    }

 

5. 실행화면

 텍스트를 입력 후 텍스트박스에 포커스가 가면 모든 텍스트가 선택되는 것을 확인 할 수 있다.

 

Select All Text 체크박스를 클릭 후 다시 포커스를 이동하면 텍스트가 선택이 되지 않는다.

 

6. 비헤이비어를 잘 활용하면 여러가지 기능을 추가할 수 있다.

다음에 기회가 된다면 추가된 기능을 가지는 비헤이비어 만드는 방법에 대해서도 포스트 하겠다.

 

소스 

BehaviorSample.zip

 

7. PageBehavior

Page의 Load, Unload, SizeChanged 이벤트를 커맨드로 바로 연결해서 사용한다.

 

using System.Windows.Input;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using WinRTXamlToolkit.Interactivity;

 

namespace kaki104.MetroCL.Behaviors
{
    public class PageBehavior : Behavior<Page>
    {
        public static readonly DependencyProperty CommandProperty =
            DependencyProperty.RegisterAttached
            (
                "Command",
                typeof(ICommand),
                typeof(PageBehavior),
                new PropertyMetadata(null, CommandPropertyChanged)
            );

        public ICommand Command
        {
            get { return (ICommand)GetValue(CommandProperty); }
            set { SetValue(CommandProperty, value); }
        }

        private static void CommandPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var target = (PageBehavior)d;
            ICommand oldCommand = (ICommand)e.OldValue;
            ICommand newCommand = target.Command;
            target.OnCommandChanged(oldCommand, newCommand);
        }

        protected virtual void OnCommandChanged(ICommand oldCommand, ICommand newCommand)
        {
            Command = newCommand;

        }

        public static readonly DependencyProperty CommandParameterProperty =
            DependencyProperty.RegisterAttached
            (
                "CommandParameter",
                typeof(object),
                typeof(PageBehavior),
                new PropertyMetadata(null, OnCommandParameterChanged));

        public object CommandParameter
        {
            get { return (object)GetValue(CommandParameterProperty); }
            set { SetValue(CommandParameterProperty, value); }
        }

        private static void OnCommandParameterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var target = (PageBehavior)d;
            object oldCommandParameter = (object)e.OldValue;
            object newCommandParameter = target.CommandParameter;
            target.OnCommandParameterChanged(oldCommandParameter, newCommandParameter);
        }

        protected virtual void OnCommandParameterChanged(object oldCommandParameter, object newCommandParameter)
        {
            CommandParameter = newCommandParameter;
        }

        public static readonly DependencyProperty EventNameProperty =
            DependencyProperty.RegisterAttached
            (
                "EventName",
                typeof(string),
                typeof(PageBehavior),
                new PropertyMetadata(null, OnEventNameChanged));

        public string EventName
        {
            get { return (string)GetValue(EventNameProperty); }
            set { SetValue(EventNameProperty, value); }
        }

        private static void OnEventNameChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var target = (PageBehavior)d;
            string oldEventName = (string)e.OldValue;
            string newEventName = target.EventName;
            target.OnEventNameChanged(oldEventName, newEventName);
        }

        protected virtual void OnEventNameChanged(string oldEventName, string newEventName)
        {
            EventName = newEventName;
        }

        public PageBehavior()
        {
        }

        protected override void OnAttached()
        {
            base.OnAttached();

            if (AssociatedObject != null)
            {
                switch (EventName)
                {
                    case "SizeChanged":
                        AssociatedObject.SizeChanged += AssociatedObject_SizeChanged;
                        break;
                    case "Loaded":
                        AssociatedObject.Loaded += AssociatedObject_LoadUnloaded;
                        break;
                    case "Unloaded":
                        AssociatedObject.Unloaded += AssociatedObject_LoadUnloaded;
                        break;
                    default:
                        break;
                }
            }
        }

        void AssociatedObject_LoadUnloaded(object sender, RoutedEventArgs e)
        {
            if (Command != null)
            {
                if (CommandParameter != null)
                    Command.Execute(CommandParameter);
                else
                    Command.Execute(e);
            }
        }

        void AssociatedObject_SizeChanged(object sender, SizeChangedEventArgs e)
        {
            if (Command != null)
            {
                if (CommandParameter != null)
                    Command.Execute(CommandParameter);
                else
                    Command.Execute(e);
            }
        }

        protected override void OnDetaching()
        {
            if (AssociatedObject != null)
            {
                switch (EventName)
                {
                    case "SizeChanged":
                        AssociatedObject.SizeChanged -= AssociatedObject_SizeChanged;
                        break;
                    case "Loaded":
                        AssociatedObject.Loaded -= AssociatedObject_LoadUnloaded;
                        break;
                    case "Unloaded":
                        AssociatedObject.Unloaded -= AssociatedObject_LoadUnloaded;
                        break;
                }
            }

            base.OnDetaching();
        }
    }
}

사용예

 

xmlns:i="using:WinRTXamlToolkit.Interactivity"
xmlns:b="using:kaki104.MetroCL.Behaviors"

 

    <i:Interaction.Behaviors>
        <b:PageBehavior EventName="SizeChanged" Command="{Binding PageSizeChangedCommand, Mode=OneWay, Source={StaticResource Locator}}" />
        <b:PageBehavior EventName="Loaded" Command="{Binding LoadedCommand, Mode=OneWay}" />
        <b:PageBehavior EventName="Unloaded" Command="{Binding UnloadedCommand, Mode=OneWay}" />
    </i:Interaction.Behaviors>

 

반응형

'Previous Platforms > Samples' 카테고리의 다른 글

HubApp using a dynamic style app development  (0) 2013.08.01
Using Task, Async, Await  (0) 2013.07.28
Text file encoding sample  (0) 2012.11.28
WinRT File Based Database sample  (0) 2012.11.12
Tile + BackgroundTask = LiveTile APP!  (0) 2012.10.30
댓글