wpf2016. 4. 8. 14:24

DependencyObject ucParent = _parent.Parent;
while (!(ucParent is UserControl))
{
ucParent = LogicalTreeHelper.GetParent(ucParent);
}


하위 UserControl에서 상위 UserControl의 Owner 찾는법

참조 : http://stackoverflow.com/questions/1474438/wpf-get-usercontrol-owner


'wpf' 카테고리의 다른 글

WPF HtmlToXaml에 Image Add하기  (0) 2016.04.14
image sequence 만들기 (Image Animation)  (0) 2016.04.08
Animation 페이지 넘기기 컨트롤  (0) 2016.04.06
[wpf] Object Capture 컨트롤 캡쳐  (0) 2016.04.05
CustomControl ImageButton  (0) 2016.04.04
Posted by 동동(이재동)
wpf2016. 4. 6. 13:47
페이지가 여러개 있을때 페이지 넘기는 효과를 준다. 여러가지 type이 있다.

ContentTemplateSelector가 변경되었을때 이벤트를 이용하기때문에 TempleteSelector를 만들어야 한다.


샘플 파일


사용법 Xaml

<local:AnimatedContentControl AnimationDuration="0:0:1.0" Content="{Binding NavigationState}" AnimateType="Right" Height="200" ContentTemplateSelector="{DynamicResource MainTemplateSelector}">
</local:AnimatedContentControl>

TemplateSelector.cs

public class MainTemplateSelector : DataTemplateSelector
{
public DataTemplate TestTemplate { get; set; }
public DataTemplate LoadingTemplate { get; set; }
public MainTemplateSelector()
{
}
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if ((item as string) == "Loading")
{
return LoadingTemplate;
}
else
{
return TestTemplate;
}
return base.SelectTemplate(item, container);
}
}


AnimationContentControl.cs
public enum AnimateType
{
Right,
Top,
Down,
Left,
Opacity,
}
/// <summary>
/// A ContentControl that animates the transition between content
/// </summary>
[TemplatePart(Name = "PART_PaintArea", Type = typeof(Shape)),
TemplatePart(Name = "PART_MainContent", Type = typeof(ContentPresenter))]
public class AnimatedContentControl : ContentControl
{
public AnimateType AnimateType
{
get { return (AnimateType)GetValue(AnimateTypeProperty); }
set { SetValue(AnimateTypeProperty, value); }
}
// Using a DependencyProperty as the backing store for AnimateType. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AnimateTypeProperty =
DependencyProperty.Register("AnimateType", typeof(AnimateType), typeof(AnimatedContentControl), new PropertyMetadata(AnimateType.Left));
#region Generated static constructor
static AnimatedContentControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(AnimatedContentControl), new FrameworkPropertyMetadata(typeof(AnimatedContentControl)));
}
#endregion Generated static constructor
public TimeSpan AnimationDuration
{
get { return (TimeSpan)GetValue(AnimationDurationProperty); }
set { SetValue(AnimationDurationProperty, value); }
}
// Using a DependencyProperty as the backing store for AnimationDuration. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AnimationDurationProperty =
DependencyProperty.Register("AnimationDuration", typeof(TimeSpan), typeof(AnimatedContentControl), new PropertyMetadata(TimeSpan.FromSeconds(0.5)));
public IEasingFunction EasingFunction
{
get { return (IEasingFunction)GetValue(EasingFunctionProperty); }
set { SetValue(EasingFunctionProperty, value); }
}
// Using a DependencyProperty as the backing store for EasingFunction. This enables animation, styling, binding, etc...
public static readonly DependencyProperty EasingFunctionProperty =
DependencyProperty.Register("EasingFunction", typeof(IEasingFunction), typeof(AnimatedContentControl), new PropertyMetadata(new PowerEase() { EasingMode = EasingMode.EaseInOut }));
private Shape m_paintArea;
private ContentPresenter m_mainContent;
/// <summary>
/// This gets called when the template has been applied and we have our visual tree
/// </summary>
public override void OnApplyTemplate()
{
m_paintArea = Template.FindName("PART_PaintArea", this) as Shape;
m_mainContent = Template.FindName("PART_MainContent", this) as ContentPresenter;
base.OnApplyTemplate();
}
protected override void OnContentTemplateSelectorChanged(DataTemplateSelector oldContentTemplateSelector, DataTemplateSelector newContentTemplateSelector)
{
base.OnContentTemplateSelectorChanged(oldContentTemplateSelector, newContentTemplateSelector);
if (ContentTemplateSelector != null)
{
ContentTemplate = ContentTemplateSelector.SelectTemplate(Content, null);
}
}
protected override void OnContentTemplateChanged(DataTemplate oldContentTemplate, DataTemplate newContentTemplate)
{
base.OnContentTemplateChanged(oldContentTemplate, newContentTemplate);
}
/// <summary>
/// This gets called when the content we're displaying has changed
/// </summary>
/// <param name="oldContent">The content that was previously displayed</param>
/// <param name="newContent">The new content that is displayed</param>
protected override void OnContentChanged(object oldContent, object newContent)
{
if (m_paintArea != null && m_mainContent != null)
{
m_paintArea.Fill = CreateBrushFromVisual(m_mainContent);
BeginAnimateContentReplacement();
}
if (ContentTemplateSelector != null)
{
ContentTemplate = ContentTemplateSelector.SelectTemplate(newContent, null);
}
base.OnContentChanged(oldContent, newContent);
}
/// <summary>
/// Starts the animation for the new content
/// </summary>
private void BeginAnimateContentReplacement()
{
var newContentTransform = new TranslateTransform();
var oldContentTransform = new TranslateTransform();
m_paintArea.RenderTransform = oldContentTransform;
m_mainContent.RenderTransform = newContentTransform;
m_paintArea.Visibility = Visibility.Visible;
switch (AnimateType)
{
case AnimateType.Top:
newContentTransform.BeginAnimation(TranslateTransform.YProperty, CreateAnimation(-this.ActualHeight, 0));
oldContentTransform.BeginAnimation(TranslateTransform.YProperty, CreateAnimation(0, this.ActualHeight, (s, e) => m_paintArea.Visibility = Visibility.Hidden));
break;
case AnimateType.Down:
newContentTransform.BeginAnimation(TranslateTransform.YProperty, CreateAnimation(this.ActualHeight, 0));
oldContentTransform.BeginAnimation(TranslateTransform.YProperty, CreateAnimation(0, -this.ActualHeight, (s, e) => m_paintArea.Visibility = Visibility.Hidden));
break;
case AnimateType.Left:
newContentTransform.BeginAnimation(TranslateTransform.XProperty, CreateAnimation(-this.ActualWidth, 0));
oldContentTransform.BeginAnimation(TranslateTransform.XProperty, CreateAnimation(0, this.ActualWidth, (s, e) => m_paintArea.Visibility = Visibility.Hidden));
break;
case AnimateType.Right:
newContentTransform.BeginAnimation(TranslateTransform.XProperty, CreateAnimation(this.ActualWidth, 0));
oldContentTransform.BeginAnimation(TranslateTransform.XProperty, CreateAnimation(0, -this.ActualWidth, (s, e) => m_paintArea.Visibility = Visibility.Hidden));
break;
case AnimateType.Opacity:
m_mainContent.BeginAnimation(UIElement.OpacityProperty, CreateAnimation(0, 1));
m_paintArea.BeginAnimation(UIElement.OpacityProperty, CreateAnimation(1, 0, (s, e) => m_paintArea.Visibility = Visibility.Hidden));
break;
default:
break;
}
}
/// <summary>
/// Creates the animation that moves content in or out of view.
/// </summary>
/// <param name="from">The starting value of the animation.</param>
/// <param name="to">The end value of the animation.</param>
/// <param name="whenDone">(optional) A callback that will be called when the animation has completed.</param>
private AnimationTimeline CreateAnimation(double from, double to, EventHandler whenDone = null)
{
var anim = new DoubleAnimation(from, to, AnimationDuration) { EasingFunction = EasingFunction };
if (whenDone != null)
anim.Completed += whenDone;
anim.Freeze();
return anim;
}
/// <summary>
/// Creates a brush based on the current appearnace of a visual element. The brush is an ImageBrush and once created, won't update its look
/// </summary>
/// <param name="v">The visual element to take a snapshot of</param>
private Brush CreateBrushFromVisual(Visual v)
{
if (v == null)
throw new ArgumentNullException("v");
var target = new RenderTargetBitmap((int)this.ActualWidth, (int)this.ActualHeight, 96, 96, PixelFormats.Pbgra32);
target.Render(v);
var brush = new ImageBrush(target) { AlignmentY = AlignmentY.Top, Stretch = Stretch.None };
brush.Freeze();
return brush;
}
}


Posted by 동동(이재동)
wpf2016. 4. 5. 18:16
Usercontrol등 Element등을 캡쳐 할수 있도록 만들었다.

용도는 캠 이미지위에 배경및 각종 clip art들을 얹혀서 캡쳐 하기위한 용도~


public static string SaveCamCapture(string filePath, FrameworkElement targetControl)
{
targetControl.UpdateLayout();
// Get the size of the Visual and its descendants.
Rect rect = VisualTreeHelper.GetDescendantBounds(targetControl);
// Make a DrawingVisual to make a screen
// representation of the control.
DrawingVisual dv = new DrawingVisual();
// Fill a rectangle the same size as the control
// with a brush containing images of the control.
using (DrawingContext ctx = dv.RenderOpen())
{
VisualBrush brush = new VisualBrush(targetControl);
ctx.DrawRectangle(brush, null, new Rect(rect.Size));
}
RenderTargetBitmap targetBitmap = new RenderTargetBitmap((int)(targetControl.ActualWidth), (int)(targetControl.ActualHeight), 96, 96, PixelFormats.Pbgra32);
targetBitmap.Render(dv);
var bitmapEncoder = new PngBitmapEncoder();
bitmapEncoder.Frames.Add(BitmapFrame.Create(targetBitmap));
if (filePath != null)
{
using (var filestream = new FileStream(filePath, FileMode.Create))
{
bitmapEncoder.Save(filestream);
}
}
return filePath;
}





Posted by 동동(이재동)
wpf2016. 4. 4. 11:53


ImageButton.cs


스타일


<Style TargetType="{x:Type c:ImageButton}">

        <Setter Property="Template">

            <Setter.Value>

                <ControlTemplate TargetType="{x:Type c:ImageButton}">

                    <Border x:Name="buttonBorder">

                        <Border.Effect>

                            <DropShadowEffect Opacity="0.0" />

                        </Border.Effect>

                        <Image  Source="{TemplateBinding NormalImage}" x:Name="img" />

                    </Border>

                    <ControlTemplate.Triggers>

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

                            <Setter TargetName="img" Property="Source" Value="{Binding PressImage, RelativeSource={RelativeSource TemplatedParent}}" />

                        </Trigger>

                    </ControlTemplate.Triggers>

                </ControlTemplate>

            </Setter.Value>

        </Setter>

    </Style>


사용예 :


<c:ImageButton x:Name="xStartBtn" Width="300" Height="300" Content="Start" NormalImage="/CartoonMuseum;component/Images/sampleBtn.png" PressImage="/CartoonMuseum;component/Images/samplePressBtn.png" />


Themes/Generic.xaml

<Style TargetType="{x:Type c:ImageButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type c:ImageButton}">
<Border Name="buttonBorder">
<Border.Effect>
<DropShadowEffect Opacity="0.0" />
</Border.Effect>
<Border.Child>
<Image Source="{TemplateBinding NormalImage}" x:Name="img" />
</Border.Child>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="true">
<Setter TargetName="img" Property="Source" Value="{Binding PressImage, RelativeSource={RelativeSource TemplatedParent}}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>








ImageButton.cs

public class ImageButton : Button
{
public ImageSource NormalImage
{
get { return base.GetValue(NormalImageProperty) as ImageSource; }
set { base.SetValue(NormalImageProperty, value); }
}
public static readonly DependencyProperty NormalImageProperty =
DependencyProperty.Register("NormalImage", typeof(ImageSource), typeof(ImageButton));
public ImageSource PressImage
{
get { return base.GetValue(PressImageProperty) as ImageSource; }
set { base.SetValue(PressImageProperty, value); }
}
public static readonly DependencyProperty PressImageProperty =
DependencyProperty.Register("PressImage", typeof(ImageSource), typeof(ImageButton));
public ImageButton()
{
DefaultStyleKey = typeof(ImageButton);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
}
}



Posted by 동동(이재동)
wpf2016. 1. 20. 16:10

PC에서는 잘나오지만 태블릿 해상도에서는 짤려서 나와서

처방을 한 코드


loaded 이벤트
root.Width = 1920;
root.Height = this.ActualHeight / this.ActualWidth * 1920;
xaml 디자인
<Viewbox> <Grid x:Name="root">


'wpf' 카테고리의 다른 글

[wpf] Object Capture 컨트롤 캡쳐  (0) 2016.04.05
CustomControl ImageButton  (0) 2016.04.04
웹캠위에 이미지를 오버랩 하여 스크린샷 찍기  (0) 2015.12.08
[wpf] 핑테스트 가능 코드  (0) 2009.09.29
[wpf] about Thread Pool  (1) 2009.09.24
Posted by 동동(이재동)
wpf2015. 12. 8. 15:46

웹캠 라이브러리는 사람들이 많이 쓰는 Aforge를 사용하였다.


문제는 웸캠 위에 이미지를 덫 씌워야 하는데 워터마크 처럼


그게 일반적인 방법으로는 안되서


 Drawing.Image image = Drawing.Image.FromFile("ct.png");

            System.Windows.Forms.PictureBox pic = new System.Windows.Forms.PictureBox()

            {

                Image = image,

                Height = 300,

                Width = 300,

                BackColor = Color.Transparent,

                SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage

            };


            VideoSourcePlayer.Controls.Add(pic);


보니까 이렇게 Controls에 add할수 있는게 있었다.


아쉽게도 windowsformshost를 사용하기 떄문에 picturebox를 이용하여야하고 투명으로 직접 설정해줘야 한다.


그러면 이렇게 된다.


소스는 여기에



WebcamTest.zip






'wpf' 카테고리의 다른 글

CustomControl ImageButton  (0) 2016.04.04
Height를 Width 비율에 맞게 나오게 하는 공식  (0) 2016.01.20
[wpf] 핑테스트 가능 코드  (0) 2009.09.29
[wpf] about Thread Pool  (1) 2009.09.24
[wpf] Thread Event ManualResetEvent AutoResetEvent  (0) 2009.09.24
Posted by 동동(이재동)
wpf2009. 9. 29. 13:59
내가 만들었지만 간단하다.. 닷넷에서 ping 클래스를 지원해주기 때문이다.
장비가 꺼졌는지 확인을 위해서 만들었다... ㅋㅋ

 private bool MachinePingTest(string ip)
        {
            Ping pingSender = new Ping();
            PingReply reply = pingSender.Send(ip);
            if (reply.Status == IPStatus.Success)
                return false;
            else
                return true;
        }
Posted by 동동(이재동)
wpf2009. 9. 24. 19:40

이번에는 ThreadPool에 대해서 소개할까 합니다혹시 이 부분에 대해서 잘 알고 계신가요아마 잘 알고 있다 보다는 ~들어는 봤고대충 무엇인지는 알아!”라고 답 하시는 분들이 많을 것이라고 대략 추측해봅니다그런데이번에는 대충 무엇인지는 알아!” 보다는 좀 더 자세하고세밀한 부분을 설명하고자 합니다.

솔직히 말씀 드리면본인이 이 ThreadPool에 흥미를 가져 소개하려고 할 때에참고하던 자료 중에 제가 좋아라 하는Jeffery Richter가 쓴 MSND Magazine  Article이 있더라구요. Jeffery Richter의 글은 MSDN 등에 잘 언급되지 않은 내용들을 깊숙이잘 소개하기로 나름 유명하신 분이며또한 .Net 분야에 상당히 내공이 깊어 많은 분들로부터 인정 받고 있기 때문에참고하지 않을 수 없었습니다그래서 이번 소개 글은 이 Article의 번역 본이라기 보다는번역본을 조금 더 이해하기 쉽게 또는 읽기 쉽게 소개하고자 하는 글이니 이점 참고하시기 바랍니다.

그럼 이제 대략 이 글에 대한 앞선 설명을 드렸으니이제부터 한번 시작해 보겠습니다.

 

여러분은 이번 주제인 ThreadPool이 왜 생겨났는지에 대한 내용을 혹시 들으신 적이 있으신가요혹은 ThreadPool이 어떤 기능을 수행하는지 잘 알고 계시나요이 질문에 대해서 정확히 아시는 분은 이 후의 글은 pass하셔도 상관 없겠습니다.

Jeffery Richter에 의하면 Microsoft는 수년 전부터(지금으로부터 따지면 상당히 오래 전에…) Thread가 개발자로 하여금 얼마나 잘 사용되어 지고 있는지 연구했다고 합니다그래서 이 결과를 분석하기 위해 조사를 시작했는데그 결과는 많은 개발자 들이 어플리케이션에서 발생하는 단일 작업을 처리하기 위해 새로운 쓰레드를 생성하고 작업이 완료되는 시점에서 생성된 쓰레드를 종료 시키는 일들이 빈번하게 발생하는 거였습니다.

 

그래서 Microsoft는 생각했답니다이미 생성된 쓰레드는 종료시키지 않고 다만 상태를 중지 시킨 후에 다음 요청이 들어오면 중지된 상태의 쓰레드를 다시 활성화 시켜 재사용 하도록 말이죠.

그렇지 않을 경우에어플리케이션에서 발생하는 작업을 처리하기 위해 쓰레드를 생성할 때 OS 레벨에서는 kernel 객체의 초기화그리고 쓰레드에서 사용되는 stack 메모리의 할당 및 초기화, Windows 운영 체제가 새로운 쓰레드가 생성 될 때 로드 된 assembly에게 DLL_THREAD_ATTACH라는 메시지를 보내기 위해 메모리에 로드 된 페이지의 손실 발생..  등등의 비 효율적인 일들이 벌어집니다.

그리고 나서 쓰레드가 동작을 완료하게 되면그때서야 여기에 할당 된 stack 메모리와 kernel 개체의 자원이 모두 해제됩니다.

이런 동작들을 통한 쓰래드의 생성과 소멸은 당연히 많은 오버헤드를 가져오게 됩니다.

 

이런 연구의 결과로 Microsoft Thread Pool을 만들게 되었고처음으로 Windows 2000에 적용이 되었습니다또한 .Net Framework 팀이 CLR을 설계할 당시에도 그들은 이 Thread Pool을 CLR 안에 탑재하기로 했답니다그 결과로 .Net Framework를 통한 managed 어플리케이션은 Thread Pool의 장점을 그대로 사용할 수 있게 되었을 뿐만 아니라 어플리케이션이 동작하는 운영 체제가 굳이 Windows 2000이 아닌 Windows 98과 같은 하위 버전일지라도 managed 어플리케이션은 Thread Pool을 사용해서 쓰레드를 관리하게 됩니다.

 

CLR이 초기화 될 때, ThreadPool은 아무런 Thread도 생성해서 가지고 있지 않습니다어플리케이션이 실행될 때 필요한 쓰레드를 ThreadPool에 요청하면 ThreadPool은 그때서야 새로운 쓰레드를 하나 생성하고 어플리케이션에 할당합니다그 이후부터 쓰레드가 작업을 완료하게 되면 바로 종료하지 않고 ThreadPool에 다시 반환되어 중지 상태로 ThreadPool에 머물게 됩니다.

그리고 또 다시 어플리케이션으로부터 쓰레드 요청이 들어오면이번에는 새로운 쓰레드를 생성하는 것이 아니라ThreadPool에 의해 중지 상태에 있는 쓰레드를 활성화 시켜 재사용하도록 합니다이런 프로세스는 많은 오버헤드를 감소시킬 수 있습니다.

그런데 이번엔 조금 다른 경우를 한번 생각해 보죠어플리케이션이 ThreadPool에 의해 재사용되는 쓰레드를 사용하기 위해서오랫동안 단위 작업(task)들을 계속해서 큐(queue)에 쌓게 된다면 이것은 매 작업마다 쓰레드를 할당해서 처리하는 경우 보다 성능 면에서는 느릴 수 밖에 없을 것입니다.

 

하지만 이 경우는 어플리케이션이 실행되는 동안 ThreadPool에 생성되어 있는 쓰레드를 계속해서 재사용하기 때문에발생할 수 있는 엄청난 오버헤드를 감소시킨다는 잇점이 있습니다.

그런데 어플리케이션이 큐에 쌓여 있는 작업들을 각각의 쓰레드를 이용해서 처리하는 것 보다 빠르게 실행시키게 하려면, ThreadPool에 더 많은 쓰레드를 추가로 생성하면 될 것 같지만이렇게 새로운 쓰레드를 추가로 생성한다면 오버헤드가 증가되는 결과는 뻔 할 것입니다그렇겠죠?

그러나 어플리케이션은 자신이 실행되는 동안 처리해야 하는 모든 작업을 완료하기 위해우리 의생각과는 다르게 단지 몇 개의 쓰레드만을 요청하기 때문에 별로 문제 될 것이 없다고 합니다이런 점으로 미루어 봤을 때, ThreadPool을 사용하게 됨으로써 어플리케이션의 성능은 전반적으로 향상된다는 결론을 내릴 수 있습니다.

 

그런데위에서 한 말처럼 ThreadPool에 여러 개의 쓰레드를 생성해서 사용했다고 치죠.

만약 어플리케이션이 처리해야 할 작업의 수가 줄어 든다면, ThreadPool에 있는 여러 개의 쓰레드 들은 어떻게 될까요쓸 일도 없는데쓰레드를 종료시키지 않고 보관하고 있다면 이건 분명 리소스 낭비 밖에 안 될 것입니다실제 그렇다면~문제입니다.

하지만 이러한 문제도 이미 Microsoft는 고려했다고 합니다이러한 경우, ThreadPool은 휴면 상태에 있는 쓰레드를 약 40초 동안 유지하고 있습니다그리고 40초가 경과했는데도 어플리케이션이 쓰레드를 요청하지 않아계속해서 정지 상태에 있어야 한다면, ThreadPool은 이러한 조건을 만족하는 쓰레드를 종료시켜 버린다고 합니다.

이렇게 됨으로써, stack 메모리와 kernel 생성에 소비되었던 리소스는 해제되어 사용 가능한 리소스가 더 늘어나게 됩니다.

 

그런데 ThreadPool을 설명하는 원문 글에서 Jeffery Richter는 쓰레드가 유휴 상태로 유지되는 40초라는 시간은 어느 문서에도 명시되어 있지 않는 시간이라고 이야기 합니다나중에 확인되면 수정한다고 하네요.(대략 개념을 정리하는데 도움을 주기 위해 사용된 수치 같습니다.)

 

이 글의 원문은 MSDN Magazine 2003년도 6월에 실린 “The CLR’S Thread Pool”이란 제목의 글이며주소는 아래에 적습니다이 글에는 이런 내용을 기반으로 몇 가지 더 설명하는 글이 있으며예제도 같이 포함되어 있는데이 내용은 다음에 전해 드리기로 하겠습니다이번에는 살짝 개념만 이해하고 계시면 될 것 같네요그럼 저는 여기서 이만 마치겠습니다.

감사합니다. ^o^v

 

*원문 글

http://msdn.microsoft.com/msdnmag/issues/03/06/NET/

Posted by 동동(이재동)
wpf2009. 9. 24. 16:31

지난 시간에는 식사하는 철학자 문제와 뮤텍스에 대해서 소개했으며, 이번에는 뮤텍스의 나머지 부분에 대해서 알아보자. 그전에 이벤트에 대해서 알아보자. 

이벤트 지금까지 살펴본 모니터나 뮤텍스는 하나의 쓰레드가 공유 데이터를 액세스하는데 유용하지만, 여러 개의 쓰레드가 서로의 실행을 방해하지 않으면서 쓰레드간에 메시지를 주고 받으려면 어떻게 해야할까? 

이런 경우를 생각해보자. A 쓰레드가 작업이 끝나면 이 사실을 전달받은 B 쓰레드가 작업을 시작한다. 두 쓰레드간에 데이터를 공유할 필요도 없고, 동기화를 사용할 필요도 없다. 이와 같은 작업을 파이프 라인으로 물이 흘러가는 것과 같다고 해서 pipe-lined execution이라고도 한다. 이 경우에는 단지 대기중인 쓰레드에게 작업이 끝난다는 사실만을 전달해주면 된다. 이런 경우에 이벤트를 사용한다.

 

이벤트는 신호상태(Signaled)와 비신호상태(Non-Signaled) 두 가지 상태를 갖고 있다. 신호상태와 비신호상태라는 것은 마치 등이 하나뿐인 신호등과 같다. 신호등에 대기중인 쓰레드는 등이 켜지지 전(non-signaled)에는 대기하고, 신호등이 켜지면(signaled) 가던 길을 계속 갈 수 있게된다. 신호 메커니즘에 대한 보다 자세한 글은 지난 글을 참고하기 바란다. 

이벤트는 두 가지 종류가 있다. 하나는 AutoReset 이벤트이며 다른 하나는 ManualReset 이벤트이다. 이들 각각의 이벤트는 실제로 닷넷에서 AutoResetEvent와 ManualResetEvent 클래스로 구현되어 있다. 이들 클래스의 생성자는 다음과 같다.
  public ManualResetEvent(bool initialState);  public AutoResetEvent(bool initialState);
initialState는 true나 false를 사용할 수 있으며, 이벤트의 초기상태를 신호상태로 할 것인지, 비신호상태로 할 것인지를 지정한다. 대부분의 경우에 false를 사용한다. 

먼저 AutoResetEvent를 사용하는 방법에 대해서 살펴보자. 이벤트를 생성하려면 다음과 같이 한다. 

여기서는 각각의 작업 3개에 대해서 3개의 이벤트를 생성한다.
  public AutoResetEvent areStep1 = new AutoResetEvent(false);  public AutoResetEvent areStep2 = new AutoResetEvent(false);  public AutoResetEvent areStep3 = new AutoResetEvent(false);
위 예제에서는 false를 사용하기 때문에 비신호상태로 선언한다는 것을 알 수 있다. 비신호상태라는 것은 신호가 될 때까지 신호등 앞에서 기다린다는 의미로 해석하면 된다. 즉, 처음부터 이벤트는 대기상태가 된다. 필자는 쓰레드들의 각각의 작업이 Step1 → Step2 → Step3으로 수행되기를 원한다. 또한 이들 각각의 작업은 데이터를 공유하지 않으며, 단순히 대기중인 쓰레드에게 작업이 끝났다는 사실만을 알려주기 위해 이벤트를 사용한다. 다음은 Step1에 대한 함수이다.
  public void Step1()  {    Console.WriteLine("Processing Step1");    Thread.Sleep(3000);    areStep1.Set();  }
먼저 화면에 Step1을 처리중이라는 메시지를 출력한다. 그리고 실제로 어떤 작업을 수행하는 함수가 들어가야하지만 여기서는 간단히 어떤 작업을 처리하는 것을 에뮬레이트하기 위해 Thread.Sleep(3000)을 사용하여 3초간 처리중인 것처럼 하였다. 처리가 끝나면 areStep1.Set()을 사용하여 첫번째 이벤트 areStep1을 신호상태로 변경한다. areStep1이 신호상태로 바뀐 것을 감지하고 작업을 수행하는 쓰레드는 Step2이다. 이제 Step2는 어떻게 Step1의 쓰레드가 이벤트를 신호상태로 바꾼 것을 알고, 작업을 처리하는지 살펴보자.
  public void Step2()  {    areStep1.WaitOne();    Console.WriteLine("Processing Step2");    Thread.Sleep(1000);    areStep2.Set();  }
가장 중요한 부분인데, areStep1.WaitOne()이라고 되어 있다. 즉, 첫번째 이벤트 areStep1이 신호상태가 될 때까지 기다린다는 것을 의미한다. 즉, 쓰레드가 처리하는 어떤 곳에서든지 areStep1이 신호상태가 되면 그것을 감지하고 대기중인 쓰레드를 잠에서 깨우는 역할을 하는 부분이다. Step2에서도 Thread.Sleep(1000) 대신에 DoPerformStep2()와 같이 어떤 함수를 사용할 것이지만 여기서는 작업을 에뮬레이트하기 위해 간단히 Thread.Sleep()을 사용하였다. 마찬가지로 작업이 끝난 다음에 이벤트 areStep2를 신호상태로 변경하여 다른 쓰레드들에게 알린다. Step3은 Step2와 동일하며, 단지 대기중인 이벤트만 다르다. 

이에 대한 전체 소스는 다음과 같다.
이름 : event01.csusing System;using System.Threading;public class AppMain{  public AutoResetEvent areStep1 = new AutoResetEvent(false);  public AutoResetEvent areStep2 = new AutoResetEvent(false);  public AutoResetEvent areStep3 = new AutoResetEvent(false);  public void Step1()  {    // do something    Console.WriteLine("Processing Step1");    Thread.Sleep(3000);    areStep1.Set();  }  public void Step2()  {    areStep1.WaitOne();    Console.WriteLine("Processing Step2");    Thread.Sleep(1000);    areStep2.Set();  }  public void Step3()  {    areStep2.WaitOne();    Console.WriteLine("Processing Step3");    areStep3.Set();  }  public void DoTest()  {    Thread thread1 = new Thread(new ThreadStart(Step1) );    Thread thread2 = new Thread(new ThreadStart(Step2) );    Thread thread3 = new Thread(new ThreadStart(Step3) );    Console.WriteLine("Thread 1, 2, 3 are started");    thread1.Start();    thread2.Start();    thread3.Start();}  public static void Main()  {    AppMain ap = new AppMain();    ap.DoTest();  }}
위에서는 AutoResetEvent를 살펴보았다. AutoResetEvent와 ManualResetEvent의 차이점은 여러분이 짐작하고 있는 것처럼 이벤트의 상태가 자동으로 초기화되느냐 그렇지 않느냐의 차이다. 위의 예제에서 Step2를 처리하는 쓰레드는 이벤트가 신호상태가 되기를 대기한다. 신호상태가 되면 이벤트는 쓰레드를 통과시키고 다시 자동으로 비신호상태가 된다. 따라서 직접 Reset()을 사용할 필요가 없다. 반면에 ManualResetEvent는 한 번 신호상태가 되면 다시 Reset()을 명시적으로 호출하여 비신호상태로 만들때까지 계속 신호상태를 유지한다. 

참고로 Main()에서 두 개의 인스턴스를 생성하여 실행해보도록한다.
  public static void Main()  {    AppMain ap = new AppMain();    ap.DoTest();    AppMain ap2 = new AppMain();    ap2.DoTest();  }
이와 같이 변경한 다음에 실행해보면 두 인스턴스의 각각의 쓰레드들이 병렬적으로 실행된다는 것을 알 수 있을 것이다.(전부 몇 개의 쓰레드가 실행중인지 확인하고 싶다면 Thread.Sleep()의 시간을 충분히 늘려놓은 다음에 7회에서 작성한 WinTop을 이용해서 몇 개의 쓰레드가 실행중인지 확인해보는 것도 좋을 것이다) 또는 다음과 같이 Main()을 수정하고 실행해본다.
  public static void Main()  {    AppMain ap = new AppMain();    ap.DoTest();    ap.DoTest();  }
즉, 특정 작업이 Step1 → Step2 → Step3으로 병렬적으로 실행되는데 좋다는 것을 알 수 있을 것이다. 대용량의 데이터 다섯 개를 동시에 1M씩 메모리로 읽어들이고, 1M씩 계산하고, 처리된 결과를 저장하는 것과 같은 단계별 작업이 필요하다면 최소한 3개의 쓰레드가 이벤트를 통해서 신호를 주고 받으면서 작업할 수 있을 것이다.(이 경우에 공유 데이터가 없다는 사실에 유의한다.) 

수동 이벤트에 대해서 알아보기 위해 위 예제를 ManualResetEvent를 사용하도록 변경해보자.
  public ManualResetEvent mreStep1 = new ManualResetEvent(false);  public ManualResetEvent mreStep2 = new ManualResetEvent(false);  public ManualResetEvent mreStep3 = new ManualResetEvent(false);
ManualResetEvent 역시 AutoEventReset과 동일한 생성자를 갖는다. 여기서도 마찬가지로 세 개의 이벤트를 모두 비신호상태로 둔다. Step1은 다음과 같이 변경한다.
  public void Step1()  {    mreStep1.WaitOne();    mreStep1.Reset();    Console.WriteLine("Processing Step1");    Thread.Sleep(3000);    mreStep1.Set();  }
WaitOne()으로 이벤트가 신호상태가 되기를 기다리는 코드를 추가하였다. AutoResetEvent와 달리 신호상태에서 하나의 대기 쓰레드를 통과시킨 다음에 자동으로 비신호상태가 되지 않으므로 Reset()을 호출하여 명시적으로 비신호상태로 전환한다.
이름: event02.csusing System;using System.Threading;public class AppMain{  public ManualResetEvent mreStep1 = new ManualResetEvent(false);  public ManualResetEvent mreStep2 = new ManualResetEvent(false);  public ManualResetEvent mreStep3 = new ManualResetEvent(false);  public void Step1()  {    mreStep1.WaitOne();    mreStep1.Reset();    Console.WriteLine("Processing Step1");    Thread.Sleep(3000);    mreStep1.Set();  }  public void Step2()  {    mreStep1.WaitOne();    mreStep1.Reset();    Console.WriteLine("Processing Step2");    Thread.Sleep(1000);    mreStep2.Set();  }  public void Step3()  {    mreStep2.WaitOne();    mreStep2.Reset();    Console.WriteLine("Processing Step3");    mreStep3.Set();  }  public void DoTest()  {    Thread thread1 = new Thread(new ThreadStart(Step1) );    Thread thread2 = new Thread(new ThreadStart(Step2) );    Thread thread3 = new Thread(new ThreadStart(Step3) );    thread1.Start();    thread2.Start();    thread3.Start();    Console.WriteLine("Thread 1, 2, 3 are started");  }  public static void Main()  {    AppMain ap = new AppMain();    ap.DoTest();  }}
위 코드를 컴파일하고 실행하면 아무것도 실행되지 않는다는 것을 알 수 있다. Step1에서도 mreStep1.WaitOne()으로 첫번째 이벤트 mreStep1이 신호되기를 기다리고 있기 때문이다. 따라서 DoTest를 다음과 같이 수정한다.
  public void DoTest()  {    Thread thread1 = new Thread(new ThreadStart(Step1) );    Thread thread2 = new Thread(new ThreadStart(Step2) );    Thread thread3 = new Thread(new ThreadStart(Step3) );    thread1.Start();    thread2.Start();    thread3.Start();    mreStep1.Set();    Console.WriteLine("Thread 1, 2, 3 are started");}

이벤트를 신호상태로 만들어준다. 컴파일하여 실행하면 AutoResetEvent를 사용한 것과 차이가 없을 것이다.(내부적으로는 어떻든간에 말이다) 

여기서는 AutoResetEvent와 ManualResetEvent 모두 하나의 이벤트만을 기다리도록 하였다. 그러나 멀티 쓰레드 프로그램에서 어떤 종류의 작업은 동시에 일어나야하는 경우도 있다. AutoResetEvent는 여러 개의 쓰레드가 하나의 작업을 처리하기 위해서 메시지를 주고 받는데 유용하며, ManualResetEvent는 여러 개의 쓰레드가 동시에 여러 개의 작업을 처리하기 위해서 메시지를 주고 받는데 유용하다. 예를 들어서, 사용자가 워드 파일을 읽어들였을 때, 문서에 있는 단어수를 세는 쓰레드가 하나, 문서를 화면에 표시하는 쓰레드가 하나, 맞춤법을 검사하는 쓰레드가 하나, 인쇄를 하는 쓰레드가 하나. 이렇게 4개의 쓰레드가 문서를 읽어들이는 시점에 동시에 발생해야한다면 ManualResetEvent를 사용하도록 한다. 

일반적으로 ManualResetEvent는 Set()을 호출하고 바로 Reset()을 호출하는 것이 대부분이기 때문에 Pulse()와 같은 메소드를 갖고 있어야하겠지만(Win32에서는 그렇다!) 닷넷에서는 이런 종류의 Pulse()는 없다. 따라서 필요하다면 자신이 만들어쓰도록 한다. 이러한 Pulse()는 Monitor 클래스에서 볼 수 있다.(Pulse나 PulseAll에 대해서는 MSDN을 참고한다) 

마치며 

뮤텍스와 이벤트가 무슨 관계가 있을까?라고 생각하는 분들도 있을 것이다. 이벤트가 뮤텍스의 소유권에 대한 상태를 알린다고 하면, 쓰레드들간에 이벤트를 주고 받음으로써 뮤텍스의 소유권을 넘겨 받을 수 있다는 것을 생각할 수 있을 것이다. 다음에는 이벤트와 뮤텍스를 이용하는 것에 대해서 살펴보도록 하자. 끝으로 다음에는 식사하는 철학자 문제를 이벤트(배고픔)와 뮤텍스(포크)를 사용해서 풀어보도록 하자.
살펴본 클래스들에 대해서 정리하는 시간을 갖게 될 것이다. [소스다운] cs_thread09_source.zip

Posted by 동동(이재동)
wpf2009. 9. 23. 15:40

FileInfo fi = new FileInfo(path);

fi.Open(FileMode.Open, FileAccess.ReadWriteFileShare.Read);

 

이렇게 파일을 열었다고 하면,

파일을 읽기/쓰기 모드로 열고 파일 공유를 읽기로만 설정한 경우입니다.

 

이렇게 열린 파일을 다른 프로세스에서 읽을려면,

 

FileInfo fi = new FileInfo(@"D:test.txt");

fi.Open(FileMode.Open, FileAccess.Read ,FileShare.ReadWrite);

로 여시면 됩니다.

 

FileShare의 기본값은 Read 이므로 반드시 명시해 주셔야 합니다. 

 

이미 이전 프로세스에서 읽기/쓰기 모드로 열려 있는데

다음 프로세스에서 읽기 공유만 허용한다는 설정이 안 맞는 것이 됩니다.

 

FileMode.Open, (1)FileAccess.ReadWriteFileShare.Read

FileMode.Open, FileAccess.Read ,(1)FileShare.ReadWrite

*(1)이 대응 되어야 함

 

다음은 간단한 예제 입니다.

 

private void btn_Write_Click(object sender, EventArgs e)
{
            if (txt_path.Text.Trim().Length > 0)
            {
                fs = new System.IO.FileStream(txt_path.Text, FileMode.Append, FileAccess.Write, FileShare.Read);
                                
                byte[] buf = Encoding.Default.GetBytes(txt_Write.Text);
                
                fs.Write(buf, 0, buf.Length);
                
                fs.Flush();
            }
}

 

 private void btn_Read_Click(object sender, EventArgs e)
 {
            if (txt_path.Text.Trim().Length > 0)
            { 
                System.IO.FileStream tfs = File.Open(txt_path.Text, FileMode.Open, FileAccess.Read, FileShare.Write);
                
                sr = new StreamReader(tfs, Encoding.Default);
                
                txt_read.Text = sr.ReadToEnd();
            }
   }

Posted by 동동(이재동)
wpf2009. 9. 22. 17:17
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.DirectoryServices;

namespace IISControler
{
    /// <summary>
    /// IIS Controler 
    /// 2009-09-22 jdlee
    /// </summary>

    class IISControl
    {
        string MetabasePath = "IIS://Localhost/W3SVC";

        public IISControl(string MetabasePath)
        {
            this.MetabasePath = MetabasePath;
        }

        internal void DeleteSite(string SiteName)
        {
            try
            {
                DirectoryEntry directoryEntry = new DirectoryEntry(MetabasePath + "/" + GetSiteId(SiteName));
                directoryEntry.DeleteTree();
            }
            catch (Exception)
            {                
                throw;
            }
        }

        internal void StopSite(string SiteName)
        {
            try
            {
                DirectoryEntry directoryEntry = new DirectoryEntry(MetabasePath + "/" + GetSiteId(SiteName));
                directoryEntry.Invoke("Stop", null);
            }
            catch (Exception)
            {
                
                throw;
            }
        }

        internal void StartSite(string SiteName)
        {
            try
            {
                DirectoryEntry directoryEntry = new DirectoryEntry(MetabasePath + "/" + GetSiteId(SiteName));
                directoryEntry.Invoke("Start", null);
            }
            catch (Exception)
            {
                
                throw;
            }
        }

        internal void CreateSite(string SiteId, string SiteName, string SitePath)
        {
            try
            {
                DirectoryEntry directoryEntry = new DirectoryEntry(MetabasePath);
                string className = directoryEntry.SchemaClassName.ToString();

                if (className.EndsWith("Service"))
                {
                    DirectoryEntries sites = directoryEntry.Children;
                    DirectoryEntry newSite = sites.Add(SiteId, (className.Replace("Service", "Server")));
                    newSite.Properties["ServerComment"][0] = SiteName;
                    newSite.CommitChanges();

                    DirectoryEntry newRoot;
                    newRoot = newSite.Children.Add("Root", "IIsWebVirtualDir");
                    newRoot.Properties["Path"][0] = SitePath;
                    newRoot.Properties["AccessScript"][0] = true;
                    newRoot.CommitChanges();
                }
            }
            catch (Exception)
            {
                
                throw;
            }
        }

        internal void SetSiteBinding(string SiteName, string BindingInfo)
        {
            string propertyName = "ServerBindings";

            try
            {
                DirectoryEntry directoryEntry = new DirectoryEntry(MetabasePath + "/" + GetSiteId(SiteName));

                if (directoryEntry.Properties[propertyName].Value == null)
                    directoryEntry.Properties[propertyName].Add(BindingInfo);
                else
                    directoryEntry.Properties[propertyName][0] = BindingInfo;

                directoryEntry.CommitChanges();
            }
            catch (Exception)
            {
                
                throw;
            }
        }

        internal string GetStatusInfo(string SiteName)
        {
            string Status = "Null";
            
            if (GetSiteId(SiteName) != null)
            {
                DirectoryEntry directoryEntry = new DirectoryEntry(MetabasePath + "/" + GetSiteId(SiteName));

                switch (directoryEntry.Properties["ServerState"].Value.ToString())
                {
                    case "2":
                        return Status = "Start";
                    case "4":
                        return Status = "Stop";
                    case "6":
                        return Status = "Pause";
                }
            }        
            
            return Status;
        }

        private string GetSiteId(string webSiteName)
        {
            try
            {
                DirectoryEntry root = new DirectoryEntry(MetabasePath);
                string siteid = null;

                foreach (DirectoryEntry item in root.Children)
                {
                    if (item.SchemaClassName == "IIsWebServer")
                    {
                        if (webSiteName == item.Properties["ServerComment"].Value.ToString())
                            siteid = item.Name;
                    }
                }

                if (siteid == null)
                    return null;

                return siteid;
            }
            catch (Exception)
            {
                
                throw;
            }
        }
    }
}

Posted by 동동(이재동)
wpf2009. 9. 22. 17:08
Thread를 이용해서 상태 정보를 입력해보자 이걸한 이유는 상태값을 모니터링 해야하는데

마땅히 상태정보를 변경할때 알려주는 이벤트가 없기때문에 쓰레드를 이용해서 2초에 한번씩 계속 상태정보를 뿌려준다.

Thread ServiceStatusUpdateThread;
delegate void DelegateStatusUpdate();
 private bool _active = true;


일단 머 쓰레드랑 ui변경을 위한 delegate와 계속 while문을 돌리기 위한 bool문을 선언한다.


 private void StatusUpdate()
        {
            if (ServiceStatusUpdateThread == null)
            {
                ServiceStatusUpdateThread = new Thread(new ThreadStart (UpdateServiceStatus));
                ServiceStatusUpdateThread.Start();                
            }
        }

이렇게 클래스를 생성하고 쓰레드 시작을 한다.

여기서는 UpdateServiceStatus()메소드를 쓰레드로 돌렸다.


  private void  UpdateServiceStatus()
        {
            while (_active == true)
            {
                Dispatcher.Invoke(DispatcherPriority.Normal, new DelegateStatusUpdate(RefreshStatus));
                Thread.Sleep(2000);
            }
        }

UpdateServiceStatus에는 UI에 업데이트할 Delegate 메소드 RefreshStatus()를 입력한다.

  private void RefreshStatus()
        {
            xDataService.Text = "DataService                : " + iisControl.GetStatusInfo("DataService");
            xDataServiceForCast.Text = "DataServiceForCast       : " + iisControl.GetStatusInfo("DataServiceForCast");
            xDataServiceForManager.Text = "DataServiceForManager : " + iisControl.GetStatusInfo("DataServiceForManager");
        }

자 이제 RefershStatus()에서 본격적으로 상태값을 textBlock에 쓴다 .즉 업데이트한다...

머 이런식으로 하면 되는데 개운하지는 않다.. class라면 notifychanged나 이런걸로 하면 되는데 방법이 없나보다..



Posted by 동동(이재동)
wpf2009. 9. 22. 14:36
IIS를 제어할일이 생겨서 IIS를 제어해보았다... 박과장이 정말 어렵다고 하고 ms에 전화까지 했다던데 못한다고 했는것을

야근시간에 금방했다......ㅡ.ㅡ;;;

일다 참고했던 사이트들이다.

Active Directory 작성방법
http://www.microsoft.com/korea/technet/tcevents/itevents/mec/dev370.mspx
http://stopwebsite

http://forums.asp.net/t/1187304.aspx

.NET 환경하에서의 ADSI 프로그래밍의 개요 
http://www.egocube.pe.kr/adsi_0005.asp

iis 제어 use DirectoryService 
http://msdn.microsoft.com/en-us/library/ms525865.aspx


그리고 DirectoryEntry의 PropertyNames를 보는법이다.

foreach (string item in path.Properties.PropertyNames)            
{
                foreach (object Value in path.Properties[item])
                {
                    xResultBox.Items.Add("key = " +item +" Value = "+Value.ToString());
                }
}

이렇게 listbox에 붙여서만 밨어야했다. 아니면 디버깅사이에서 보기는 힘들었다.

구현한 소스는 첨부로 해놓아야겠다 class로 깔끔하게 묶어서 class만 부르면 언제든지 사용가능하게 만들었다.

IIS 추가,삭제,바인딩세팅,시작,정지 가 되도록 sitename만 넣으면 다 되게 했다.

1차 파일 : [#FILE|IISControler.zip|pds/200909/22/37/|mid|0|0|pds15|0#]

2차 수정은 상태업데이트를 이용하였다 다음 포스트에 쓰겠지만 상태를 계속 모니터링해야하기 때문에

상태가 변하면 알려주는 이벤트가 있으면 좋겠지만그런건 없었다...ㅠ.ㅠ

그래서 쓰레드를 이용해서 2초에 한번씩 상태를 계속 업데이트 하게 만들었다....

2차 파일 : [#FILE|IISControler_2.zip|pds/200909/22/37/|mid|0|0|pds15|0#]


Posted by 동동(이재동)
wpf2009. 9. 21. 17:47
압축을 풀면

logviewer(클라이언트)

udp 서버 폴더가 있는데


아이피를 맞추고 실행하면 된다.

CPortStatusInfo 이클래스를 보면 어떻게 썼는지 나온다.


[#FILE|GetListEvent.zip|pds/200909/21/37/|mid|0|0|pds16|0#]
Posted by 동동(이재동)
wpf2009. 9. 21. 17:32
기존 나는 delegate를 쓸때 메소드 파라미터를 주지 못했었다.


기존은 

 private delegate void DelegateSetStatus();

이런식으로 하고

this.Dispatcher.Invoke(DispatcherPriority.Normal,new DelegateSetStatusForRemove(RemovePortStatus));

이런식으로 해서 메소드에 파라미터를 줄수 가 없었다...

하지만

private delegate void DelegateSetStatus(string message);

string message= "test message";

this.Dispatcher.Invoke(new DelegateSetStatus(SetStatus), new object[] { message });

이런식으로  델리게이트  메소드에 파라미터를 줄수가 있다.. 

물론 메소드는 이렇게 되어있겠지

private void DelegateSetStatus(string message)
{
   ...
}


Posted by 동동(이재동)
wpf2009. 9. 21. 16:34
bservable Collection을 이용한 바인딩 샘플

파일>새로만들기>프로젝트
 순서로 누른다 .

 

 

 

 

 

 

 

 

 

[확인]을 눌러서 프로젝트를 생성한다.

Person 클래스 파일을 추가한다.  System.ComponetModel과 System.Collections.ObjectModel을 추가하고 다음의 코드를 완성한다.

using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using System.Collections.ObjectModel;
 
namespace ObservableCollection
{
    public class Person : INotifyPropertyChanged 
    {
        #region INotifyPropertyChanged 멤버
 
        public event PropertyChangedEventHandler PropertyChanged;
 
        protected void OnPropertyChanged(string info)
        {
            if(PropertyChanged!=null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
 
        #endregion
 
        public Person()
        {
        }
 
        public Person(string first, string last, string town)
        {
            this.firstname = first;
            this.lastname = last;
            this.hometown = town;
        }
        public override string ToString()
        {
            return firstname.ToString();
        }
 
        private string firstname;
 
        public string FirstName
        {
            get { return firstname; }
            set { 
                firstname = value;
                OnPropertyChanged("FirstName");
            }
        }
        private string lastname;
 
        public string LastName
        {
            get { return lastname; }
            set { 
                lastname = value;
                OnPropertyChanged("LastName");
            }
 
        }
        private string hometown;
 
        public string HomeTown
        {
            get { return hometown; }
            set {
                hometown = value;
                OnPropertyChanged("HomeTown");
            }
        }
    }
 
    public class People : ObservableCollection<Person>
    {
        public People() : base()
        {
            Add(new Person("Michael", "Alexander", "Bellevue"));
            Add(new Person("Jeff", "Hay", "Redmond"));
            Add(new Person("Christina", "Lee", "Kirkland"));
            Add(new Person("Samantha", "Smith", "Seattle"));
        }
    };
}

Window1.xaml 파일을 아래와 같이 수정한다.

<Window x:Class="ObservableCollection.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Binding to a Collection"
    xmlns:local="clr-namespace:ObservableColleciton"
    SizeToContent="WidthAndHeight"
    >
 
</Window>

Window1.xaml에 리소스를 추가한다.

  <Window.Resources>
    <ObjectDataProvider x:Key="MyFriends" ObjectType="{x:Type local:People}"/>
    <Style TargetType="ListBoxItem">
      <Setter Property="FontFamily" Value="Verdana"/>
      <Setter Property="FontSize" Value="11"/>
      <Setter Property="Padding" Value="10"/>
    </Style>
    <DataTemplate x:Key="DetailTemplate">
      <Border Width="300" Height="100" Margin="20"
              BorderBrush="LightSeaGreen"  BorderThickness="3" Padding="8"
              CornerRadius="5">
        <Grid>
          <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
          </Grid.RowDefinitions>
          <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
          </Grid.ColumnDefinitions>
          <TextBlock Grid.Row="0" Grid.Column="0" Text="First Name:"/>
          <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Path=FirstName}"/>
          <TextBlock Grid.Row="1" Grid.Column="0" Text="Last Name:"/>
          <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding Path=LastName}"/>
          <TextBlock Grid.Row="2" Grid.Column="0" Text="Home Town:"/>
          <TextBlock Grid.Row="2" Grid.Column="1" Text="{Binding Path=HomeTown}"/>
        </Grid>
      </Border>
    </DataTemplate>
  </Window.Resources>

끝으로 컨트롤을 작성한다.

  <StackPanel>
    <TextBlock FontFamily="Verdana" FontSize="11"
               Margin="5,15,0,10" FontWeight="Bold">My Friends</TextBlock>
    <ListBox Width="200" IsSynchronizedWithCurrentItem="True"
             ItemsSource="{Binding Source={StaticResource MyFriends}}"/>
    <TextBlock FontFamily="Verdana" FontSize="11"
               Margin="5,15,0,5" FontWeight="Bold">Information</TextBlock>
    <ContentControl Content="{Binding Source={StaticResource MyFriends}}"
                    ContentTemplate="{StaticResource DetailTemplate}"/>
  </StackPanel>

끝으로 실행해 본다. ... 아래와 같이 뜨면 잘한겨..


이 글과 관련있는 글을 자동검색한 결과입니다 [?]




















Posted by 동동(이재동)
wpf2009. 9. 16. 16:21
savefilepath = 파일경로와이름


            StreamWriter sw = new StreamWriter(SaveFilePath, false, System.Text.Encoding.UTF8);
            XmlDocument doc = new XmlDocument();            
            XmlWriter xw = XmlWriter.Create(sw);
            xw.WriteStartDocument();
            xw.WriteStartElement("WindowsInfo");
            xw.WriteElementString("Width", this.ActualWidth.ToString());
            xw.WriteElementString("Height", this.ActualHeight.ToString());
            xw.WriteElementString("Left", this.Left.ToString());
            xw.WriteElementString("Top", this.Top.ToString());
            xw.WriteElementString("WindowState", this.WindowState.ToString());            
            xw.WriteEndElement();
            xw.WriteEndDocument();
            xw.Flush();
            xw.Close();
Posted by 동동(이재동)
wpf2009. 9. 10. 20:19
일단 제멀에

  <MenuItem Header="Window" ItemsSource="{Binding}" x:Name="xWindowSelecter" />     

이런식으로 binding을 걸고 xName을 설정하였다.


  xWindowSelecter.Click += new RoutedEventHandler(xWindowSelecter_Click);

이렇게 click event를 연결한뒤

void xWindowSelecter_Click(object sender, RoutedEventArgs e)
        {   
            MenuItem selectMenuItem = e.OriginalSource as MenuItem;
            var selectedItem = selectMenuItem.Header as WindowCollection;
            if (selectedItem != null)
                SetZindexWindow(selectedItem.id);           
        }

이렇게 MenuItem으로 e.OrignalSource를 반환한후 header값을 이용하여 다시한번 원래 WindowCollection을 바꾸었다

이렇게 하니까 잘됨 

Posted by 동동(이재동)
wpf2009. 9. 4. 17:59
string의 substring이나 replace로는 문자 치환이나 검색에 한계가 있다.

그래서 Regex를 이용해서 보다 고급적이게 문자를 control할수 있는것이다...

자세한건 더 검색해보고 공부하자.... 아직 갈길이 멀구나.....


http://blog.naver.com/myheredity/130041647197
Posted by 동동(이재동)
wpf2009. 8. 28. 11:17
<Window x:Class="XPathVisualizer.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:xmlstack="clr-namespace:System.Xml;assembly=System.Xml"
    Title="XPath Visualizer">
  <Window.Resources>
    <SolidColorBrush Color="Blue" x:Key="xmlValueBrush"/>
    <SolidColorBrush Color="Red" x:Key="xmAttributeBrush"/>
    <SolidColorBrush Color="DarkMagenta" x:Key="xmlTagBrush"/>
    <SolidColorBrush Color="Blue" x:Key="xmlMarkBrush"/>
    <DataTemplate x:Key="attributeTemplate">
      <StackPanel Orientation="Horizontal" Margin="3,0,0,0" HorizontalAlignment="Center">
        <TextBlock Text="{Binding Path=Name}" Foreground="{StaticResource xmAttributeBrush}"/>
        <TextBlock Text="=&quot;" Foreground="{StaticResource xmlMarkBrush}"/>
        <TextBlock Text="{Binding Path=Value}" Foreground="{StaticResource xmlValueBrush}"/>
        <TextBlock Text="&quot;" Foreground="{StaticResource xmlMarkBrush}"/>
      </StackPanel>
    </DataTemplate>

    <HierarchicalDataTemplate x:Key="treeViewTemplate" ItemsSource="{Binding XPath=child::node()}">
      <StackPanel Orientation="Horizontal" Margin="3,0,0,0" HorizontalAlignment="Center">
        <TextBlock Text="&lt;" HorizontalAlignment="Center" Foreground="{StaticResource xmlMarkBrush}" x:Name="startTag"/>
        <TextBlock
            Text="{Binding Path=Name}"
            Margin="0"
            HorizontalAlignment="Center"
            x:Name="xmlTag"
            Foreground="{StaticResource xmlTagBrush}"/>
        <ItemsControl
            ItemTemplate="{StaticResource attributeTemplate}"
            ItemsSource="{Binding Path=Attributes}"
            HorizontalAlignment="Center">
          <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
              <StackPanel Orientation="Horizontal"/>
            </ItemsPanelTemplate>
          </ItemsControl.ItemsPanel>
        </ItemsControl>
        <TextBlock Text="&gt;" HorizontalAlignment="Center" Foreground="{StaticResource xmlMarkBrush}" x:Name="endTag"/>
      </StackPanel>
      <HierarchicalDataTemplate.Triggers>
        <DataTrigger Binding="{Binding NodeType}">
          <DataTrigger.Value>
            <xmlstack:XmlNodeType>Text</xmlstack:XmlNodeType>
          </DataTrigger.Value>
          <Setter Property="Text" Value="{Binding InnerText}" TargetName="xmlTag"/>
          <Setter Property="Foreground" Value="Black" TargetName="xmlTag"/>
          <Setter Property="Visibility" Value="Collapsed" TargetName="startTag"/>
          <Setter Property="Visibility" Value="Collapsed" TargetName="endTag"/>
        </DataTrigger>
        <DataTrigger Binding="{Binding HasChildNodes}" Value="False">
          <Setter Property="Text" Value="/&gt;" TargetName="endTag"/>
        </DataTrigger>
      </HierarchicalDataTemplate.Triggers>
    </HierarchicalDataTemplate>
  </Window.Resources>
  <Grid>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="Auto"/>
      <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="*"/>
      <ColumnDefinition Width="Auto"/>
    </Grid.ColumnDefinitions>
    <TextBox Name="xmlPathTextBox" Grid.Row="0" Grid.Column="0" Margin="3"/>
    <Button Margin="3" Content="Browse..." Click="BrowseXmlFile" Grid.Row="0" Grid.Column="1"/>
    <TreeView Grid.Row="2" Grid.ColumnSpan="2" Name="xmlTree" ItemTemplate="{StaticResource treeViewTemplate}">
      <TreeView.ItemContainerStyle>
        <Style TargetType="{x:Type TreeViewItem}">
          <Setter Property="IsExpanded" Value="True"/>
        </Style>
      </TreeView.ItemContainerStyle>
    </TreeView>
  </Grid>
</Window>

private void BrowseXmlFile(Object sender, RoutedEventArgs e)
{
    Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
    dlg.CheckFileExists = true;
    dlg.Filter = "xml files (*.xml)|*.xml|all files(*.*)|*.*";
    dlg.Multiselect = false;
    if (dlg.ShowDialog() == true)
    {
        try
        {
            XmlDocument doc = new XmlDocument();
            doc.Load(dlg.FileName);
            XmlDataProvider provider = new XmlDataProvider();
            provider.Document = doc;
            Binding binding = new Binding();
            binding.Source = provider;
            binding.XPath = "child::node()";
            xmlTree.SetBinding(TreeView.ItemsSourceProperty, binding);
        }
        catch (XmlException)
        {
            MessageBox.Show("Xml is invalid");
        }
    }
}

Posted by 동동(이재동)
wpf2009. 8. 28. 11:16
Loading an XML Document from a File or a String

I just got stuck on something, and it was one of those "in your face" type of solutions that my friend Jeff Julian had to help me with.

I have a file called c:\temp.xml.

In C# I was trying to load this into a XmlDocument. Easy enough.

XmlDocument xml = new XmlDocument();
xml.Load("c:\temp.xml");

Of course the values aren't hard coded, but for the point this works fine.

Here was my problem. I had the contents of c:\temp.xml already loaded in as a string called xmlContents. So I was trying to

xml.Load(xmlContents);

And I was getting an "Invalid URI" error.

The solution.

xml.LoadXml(xmlContents);


출처 : http://weblogs.asp.net/scottcate/archive/2006/11/13/Loading-an-XML-Document-from-a-File-or-a-String.aspx

Posted by 동동(이재동)