외부적으로는 같은 기능을 수행하되 내부적으로는 약간 다른 로직을 가진 2개 이상의 Class들을 설계 하기 위해 Abstract Class를 이용하였다.

 

CommonGallery라는 공통의 Abstract Class를 만들었다. CommonGallery는 UserControl과 IContent를 상속 받았다.

 

 

 

    public interface IContent

    {

        event Action ContentTouchDownEvent; 

        void NotifyActivate(bool state);

        void SetWorkDirectory(string workPath);

 

    }

 

IContent.cs

 

 

SetWorkDirectory 메서드는 동일한 동작을 하고 NotifyActivate메서드는 각 Gallery마다 초기화 및 종료가 다르므로 abstract 메서드로 설정하였다. 

 

 

public abstract class CommonGallery : UserControl, IContent

    {

        /// <summary>

        /// Touch 발생하면 호출됩니다.

        /// </summary>

        public event Action ContentTouchDownEvent;

 

        #region Ctor

        public CommonGallery()

        {

            this.PreviewTouchDown += delegate(object sender, System.Windows.Input.TouchEventArgs e)

            {

                if (this.ContentTouchDownEvent != null)

                    this.ContentTouchDownEvent();

            };

            this.PreviewMouseLeftButtonDown += delegate(object sender, System.Windows.Input.MouseButtonEventArgs e)

            {

                if (this.ContentTouchDownEvent != null)

                    this.ContentTouchDownEvent();

            };

        }

        #endregion

 

        /// <summary>

        /// 갤러리에 상황을 알려줍니다.

        /// </summary>

        /// <param name="state"></param>

        public abstract void NotifyActivate(bool state);

/// <summary>

        /// 작업 경로를 설정합니다.

        /// </summary>

        /// <param name="workPath"></param>

        public void SetWorkDirectory(string workPath)

        {

            GalleryManager.Instance.ImageFolderPath = workPath;

        }

    } 

CommonGallery.cs

 

 문자열을 이용한 포토갤러리인 StringPhotoGallery Class에 CommonGallery 상속받은 후 Xaml을 확인할 결과 아래의 메세지가...두둥!

 

"CommonGallery"의 인스턴스를 만들 수 없습니다.

 

 지져분하지만.......CommonGallery Class를 아래와 같이 수정 후 정상적으로 작동하게 되었다.

 

해결방법 1. DEBUG 모드일때에는 abstract 클래스를 class로 임시 변경

 

 

#if DEBUG

    public class CommonGallery : UserControl, IContent

#else

        public abstract class CommonGallery : UserControl, IContent

#endif

 

    {

        /// <summary>

        /// Touch 발생하면 호출됩니다.

        /// </summary>

        public event Action ContentTouchDownEvent;

 

        #region Ctor

#endregion

 

#if DEBUG

        /// <summary>

        /// 갤러리에 상황을 알려줍니다.

        /// </summary>

        /// <param name="state"></param>

        public virtual void NotifyActivate(bool state)

        {

        }

#else

        /// <summary>

        /// 갤러리에 상황을 알려줍니다.

        /// </summary>

        /// <param name="state"></param>

        public abstract void NotifyActivate(bool state);

#endif

        /// <summary>

        /// 작업 경로를 설정합니다.

        /// </summary>

        /// <param name="workPath"></param>

        public void SetWorkDirectory(string workPath)

        {

            GalleryManager.Instance.ImageFolderPath = workPath;

        }

    } 

수정한 CommonGallery.cs

 

abstract Class의 인스턴스를 만들 수 없다면 Debug 모드일때에는 디자인을 확인해야하므로 일반 class와 virtual 메서드를 이용하고, Release 모드일때에는 abstract class로 사용하였다.

 

 정말 지저분하다......ㅠㅠ

 

 

 

해결방법 2. TypeDescriptionProvider 사용

[키즈님 블로그 출처] http://blog.naver.com/kizrael/220475105952

 

테스트 해보니 잘 동작하네요.
단지 abstract class를 상속받는 클래스에 컨트롤을 추가하고 컴파일해도
디자이너에 반영이 안되어서 visual studio를 재시작하면 되네요.

 

되는것 처럼 보이다가......다시 컴파일하면 디자이너 에러 생김...T_T

 

 

[TypeDescriptionProvider(typeof(AbstractControlDescriptionProvider<AbstractControl, UserControl>))]

public abstract class AbstractControl : UserControl

{

 

}

public class AbstractControlDescriptionProvider<TAbstract, TBase> : TypeDescriptionProvider

{

    public AbstractControlDescriptionProvider()

        : base(TypeDescriptor.GetProvider(typeof(TAbstract)))

    {

    }

 

    public override Type GetReflectionType(Type objectType, object instance)

    {

        if (objectType == typeof(TAbstract))

            return typeof(TBase);

 

        return base.GetReflectionType(objectType, instance);

    }

 

    public override object CreateInstance(IServiceProvider provider, Type objectType, Type[] argTypes, object[] args)

    {

        if (objectType == typeof(TAbstract))

            objectType = typeof(TBase);

 

        return base.CreateInstance(provider, objectType, argTypes, args);

    }

} 

 

 

 

 

 

 

 

 

 /// <summary>

        /// WebBrowser 컨트롤의 Script Error 메세지박스를 띄우지 않습니다.

        /// </summary>

        private void InjectDisableScript()

        {

            //HTMLDocumentClass doc = this.webBrowser.Document as HTMLDocumentClass;

            HTMLDocument doc = this.webBrowser.Document as HTMLDocument;

 

            //Questo crea lo script per la soprressione degli errori

            IHTMLScriptElement scriptErrorSuppressed = (IHTMLScriptElement)doc.createElement("SCRIPT");

            scriptErrorSuppressed.type = "text/javascript";

            scriptErrorSuppressed.text =

            @"function noError() {

                return true;

            }

            window.onerror = noError;";

 

            IHTMLElementCollection nodes = doc.getElementsByTagName("head");

            foreach (IHTMLElement elem in nodes)

            {

                //Appendo lo script all'head cosi è attivo

                IHTMLDOMNode head = (IHTMLDOMNode)elem;

                head.appendChild((IHTMLDOMNode)scriptErrorSuppressed);

 

            }

 

        }

 

 

Surface sdk 2.0 에 있는 SurfaceListBox를 사용하는데

 

SurfaceListBoxItem의 배경색상이 불투명한 하얀색으로 나와서 구글링 결과 아래의 코드를 찾았다.

 

 

 

 

        <Style x:Key="BgTransparentStyle" TargetType="{x:Type my:SurfaceListBoxItem}"  >

            <Style.Triggers>

                <Trigger Property="IsSelected" Value="true">

                    <Setter Property="Foreground" Value="Transparent" />

                    <Setter Property="Background" Value="Transparent" />

                </Trigger>

                <Trigger Property="IsFocused" Value="true">

                    <Setter Property="Foreground" Value="Transparent" />

                    <Setter Property="Background" Value="Transparent" />

                </Trigger>

                <Trigger Property="IsEnabled" Value="true">

                    <Setter Property="Foreground" Value="Transparent" />

                    <Setter Property="Background" Value="Transparent" />

                </Trigger>

                <Trigger Property="IsMouseOver" Value="true">

                    <Setter Property="Foreground" Value="Transparent" />

                    <Setter Property="Background" Value="Transparent" />

                </Trigger>

            </Style.Triggers>

        </Style> 

 

                 ..................

 

        <my:SurfaceListBox Width="1080" Height="1480" ItemContainerStyle="{StaticResource BgTransparentStyle}"/>

 

 

 

 

[이미지1] 스타일 적용 전

 

 

 

 

 

 

[이미지2] 스타일 적용 후

 

if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))

{

     return;

}

 

ScrollViewer에서 현재 보여지고 있는 부분을 표시하기 위한 내용입니다.

참조: http://www.thejoyofcode.com/WPF_ScrollViewer_Thumbnail.aspx

 

 

 

[이미지 1 결과 화면]

 

 

 

 

소스 코드입니다.

 

<Window x:Class="HowToLayoutPreview.MainWindow"

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

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

        Title="MainWindow" Height="350" Width="525">

    <Grid>

        <ScrollViewer x:Name="myScrollViewer" Height="246" HorizontalAlignment="Left" Margin="7,37,0,0" VerticalAlignment="Top" Width="255" HorizontalScrollBarVisibility="Visible">

            <Grid>

                <Image Source="/HowToLayoutPreview;component/Images/Chrysanthemum.jpg" Height="338" Width="357"></Image>

                <Button Margin="137,139,174,164">아하</Button>

            </Grid>

        </ScrollViewer>

        <Viewbox DataContext="{Binding ElementName=myScrollViewer}" Margin="294,0,0,0">

            <Grid>

                <Rectangle Width="{Binding Content.ActualWidth}" Height="{Binding Content.ActualHeight}">

                    <Rectangle.Fill>

                        <VisualBrush Visual="{Binding Content}" />

                    </Rectangle.Fill>

                </Rectangle>

                <Border BorderThickness="1" BorderBrush="Black" Background="#88FFFF00" Width="{Binding ViewportWidth}" Height="{Binding ViewportHeight}" HorizontalAlignment="Left" VerticalAlignment="Top">

                    <Border.RenderTransform>

                        <TranslateTransform X="{Binding HorizontalOffset}"  Y="{Binding VerticalOffset}" />

                    </Border.RenderTransform>

                </Border>

            </Grid>

        </Viewbox>

    </Grid>

</Window>  

 

 

프로젝트 파일입니다.

HowToLayoutPreview.zip

 

 

 

WPF 응용 프로그램에서 테스트 했을 때에는

아무 문제 없이 잘돌아가던 프로그램이..

UserControl로 변경하여 Winform에서 호스팅하면...

리소스가 없는 문제가 발생한다.

 

Application.Current 개체가 null..........ㅠㅠ

구글링 결과 간단하였다.

 

Application 개체 한번 생성해주고 리소스 불러오면 된다.

 

 

        /// <summary>

        /// Winform에서 호스팅시 Resource Load한다.

        /// </summary>

        private void EnsureApplicationResources()

        {

            if (Application.Current == null)

            {

                new Application();

                ResourceDictionary skinDict = Application.LoadComponent(new Uri(@"/DVCONSOLE;component/Resources\Skins\xxxx.xaml", UriKind.Relative)) as ResourceDictionary;

                Application.Current.Resources = skinDict;

            }

        }

 

 

 

 

 

참조 : Managing Application Resources when WPF is Hosted

MVVM 패턴을 익히는 도중 문득 이런 생각이 들었다.

 

ViewModel에서 행동을 처리하는데.....자식창을 어떻게 띄우지.......?ㅠㅠ

 

친절한 MVVMLight에서는 ViewModelLocator Class를 제공해준다.

 

GalaSoft.MvvmLight.Messaging.Messenger.Default.Register를 이용해서 Message등록을 해주고

GalaSoft.MvvmLight.Messaging.Messenger.Default.Unregister를 이용해서 Message등록을 해지한다.

 

 

Page이동도 이런식으로 구현하면 되려나.......흠........

ViewModel끼리 ViewModelLocator를 이용해 접근하는게 맞는진 모르겠지만..

프로젝트 소스를 첨부합니다.

 

 

 

 

vs2012로 작성되었습니다.

 

 

 

HowToMsgControl.zip

컬렉션 뷰 작업
WPF 컨트롤은 데이터 컬렉션에 바인딩될 때 컬렉션 자체에 직접 바인딩되지는 않습니다. 대신 해당 컬렉션을 자동으로 래핑하는 뷰에 암시적으로 바인딩됩니다. 이 뷰는 ICollectionView 인터페이스를 구현하며 여러 구체적인 구현 중 하나일 수 있습니다(예: ListCollectionView).
컬렉션 뷰는 여러 가지 역할을 합니다. 컬렉션 뷰는 컬렉션의 현재 항목을 추적하는데, 이러한 항목은 일반적으로 목록 컨트롤의 활성/선택된 항목으로 해석됩니다. 또한 컬렉션 뷰는 목록 항목의 정렬, 필터링 및 그룹화에 대한 포괄적 수단을 제공합니다. 컬렉션을 둘러싼 하나의 뷰에 여러 컨트롤을 바인딩하여 서로 조화를 이루도록 할 수 있습니다. 다음 코드는 ICollectionView의 기능 일부를 보여 줍니다.
 
// Get the default view wrapped around the list of Customers.
ICollectionView view = CollectionViewSource.GetDefaultView(allCustomers);

// Get the Customer selected in the UI.
Customer selectedCustomer = view.CurrentItem as Customer;

// Set the selected Customer in the UI.
view.MoveCurrentTo(someOtherCustomer);

 

컬렉션 뷰의 CurrentItem 속성과 동기화된 상태를 유지하려면 목록 상자, 콤보 상자 및 목록 뷰와 같은 모든 목록 컨트롤의 IsSynchronizedWithCurrentItem 속성이 true로 설정되어야 합니다. 이 속성은 추상 Selector 클래스가 정의합니다. 이 속성이 true로 설정되지 않으면 목록 컨트롤에서 항목을 선택해도 컬렉션 뷰의 CurrentItem이 업데이트되지 않고 CurrentItem에 새 값을 할당해도 해당 목록 컨트롤에 이 값이 반영되지 않습니다.

 

Data and WPF: 데이터 바인딩과 WPF를 사용한 데이터 표시 사용자 지정

자동 증가하는 Number를 주기 위해 LoadRow 이벤트를 등록 후 이벤트가가 발생하면 해당 Row Index+1의 값을 RowHeader로 설정한다.

 

 

 

        /// <summary>

        /// Row 개체가 생성되면 발생합니다.

        /// </summary>

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

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

        private void table_LoadingRow(object sender, DataGridRowEventArgs e)

        {

            e.Row.Header = (e.Row.GetIndex()+1).ToString();

        } 

 

 

 

 

            for (int i = 0; i < this.table.Items.Count; i++)

            {

                var obj = this.table.ItemContainerGenerator.ContainerFromIndex(i);

                if (Validation.GetHasError(obj) == true)

                {

                    MessageBox.Show(string.Format("테이블 {0}번째 데이터가 잘못 입력되었습니다.",i.ToString()), "에러", MessageBoxButton.OK, MessageBoxImage.Error);

                    return;

                }

            } 

 

+ Recent posts