Command 에 인자로 두가지 항목을 던지고자 할때

이를 class 로 정의하여 CommandParameter 로 던질수 있다.

 

먼저 프로젝트에 Prism.Wpf Nuget Package 를 설치한다.

 

이제 Class 구조를 잡는다.

(string, object 로 구성)

[StringObject.cs]

namespace MultiValueConverterSample

{

    public class StringObject

    {

        /// <summary>

        /// 문자열 데이터입니다.

        /// </summary>

        public string StringData;

 

        /// <summary>

        /// 오브젝트 데이터입니다.

        /// </summary>

        public object ObjectData;

 

        /// <summary>

        /// 생성자입니다.

        /// </summary>

        /// <param name="stringData">문자열 데이터입니다.</param>

        /// <param name="objectData">오브젝트 데이터입니다.</param>

        public StringObject(string stringData, object objectData)

        {

            this.StringData = stringData;

            this.ObjectData = objectData;

        }

    }

}

 

 

IMultiValueConverter 를 정의한다.

(화면에서 처리된 두개의 인자를 가지고 위에서 만들 Class 구조로 변환해 리턴)

[StringObjectConverter.cs]

using System;

using System.Globalization;

using System.Windows.Data;

 

namespace MultiValueConverterSample

{

    public class StringObjectConverter : IMultiValueConverter

    {

        public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)

        {

            return new StringObject(values[0].ToString(), values[1]);

        }

 

        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)

        {

            throw new NotImplementedException();

        }

    }

}

 

 

화면에 아래 처럼 구성한다.

(조회 버튼을 클릭하면 StringObjectCommand 를 실행해

입력한 값을 string 으로 버튼 객체를 object 로 하여 CommandParameter 로 사용한다.)

[MainWindow.xaml]

<Window x:Class="MultiValueConverterSample.MainWindow"

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

        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

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

        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

        xmlns:local="clr-namespace:MultiValueConverterSample"

        mc:Ignorable="d"

        Title="MainWindow" Height="450" Width="800">

    <Window.Resources>

        <local:StringObjectConverter x:Key="StringObjectConverter" />

    </Window.Resources>

    <Grid>

        <StackPanel>

            <TextBox Name="contentTextBlock" Width="100" Height="30" Margin="10"/>

            <Button Name="searchButton" Content="조회" Margin="10" Height="30" Width="100" Command="{Binding StringObjectCommand}">

                <Button.CommandParameter>

                    <MultiBinding Converter="{StaticResource StringObjectConverter}">

                        <Binding ElementName="contentTextBlock" Path="Text" />

                        <Binding ElementName="searchButton" />

                    </MultiBinding>

                </Button.CommandParameter>

            </Button>

        </StackPanel>

    </Grid>

</Window>

 

 

코드비하인드 단에서는 아래 처럼 처리한다.

(반드시 this.DataContext = this; 을 해야 Command 가 처리된다.)

[MainWindow.xaml.cs]

using Prism.Commands;

 

using System.Windows;

using System.Windows.Input;

 

namespace MultiValueConverterSample

{

    /// <summary>

    /// MainWindow.xaml에 대한 상호 작용 논리

    /// </summary>

    public partial class MainWindow : Window

    {

        #region StringObjectCommand

 

        /// <summary>

        /// StringObjectCommand

        /// </summary>

        public ICommand StringObjectCommand

        {

            get

            {

                if (stringObjectCommand == null)

                {

                    stringObjectCommand = new DelegateCommand<StringObject>((stringObject) =>

                    {

                        string stringData = stringObject.StringData;

                        object objectData = stringObject.ObjectData;

                    });

                }

                return stringObjectCommand;

            }

        }

        private DelegateCommand<StringObject> stringObjectCommand;

 

        #endregion

 

        public MainWindow()

        {

            InitializeComponent();

 

            this.DataContext = this;

        }

    }

}

 

 

 

조회 버튼을 누르게 되면 Command 에서 아래 처럼 해당 항목이 넘어온것을 확인할 수 있다.

 

 

 

아래 예시는 각기 다른 4가지 이미지를 4분면에 그리는 코드입니다.

 

4분면에 이미지를 하나씩 그리는 예시입니다.

처음 이미지의 크기에 따라 나머지 이미지 들도 크기가 지정되며

바둑판모양으로 이미지가 달라 붙습니다.

 

        private void MergeImage(string path1, string path2, string path3, string path4, string outputFilePath)

        {

            // 이미지를 로드합니다.

            BitmapFrame frame1 = BitmapDecoder.Create(new Uri(path1), BitmapCreateOptions.None, BitmapCacheOption.OnLoad).Frames.First();

            BitmapFrame frame2 = BitmapDecoder.Create(new Uri(path2), BitmapCreateOptions.None, BitmapCacheOption.OnLoad).Frames.First();

            BitmapFrame frame3 = BitmapDecoder.Create(new Uri(path3), BitmapCreateOptions.None, BitmapCacheOption.OnLoad).Frames.First();

            BitmapFrame frame4 = BitmapDecoder.Create(new Uri(path4), BitmapCreateOptions.None, BitmapCacheOption.OnLoad).Frames.First();

 

            // 첫번째이미지 기준으로 그려질 이미지의 크기를 정의합니다.

            int imageWidth = frame1.PixelWidth;

            int imageHeight = frame1.PixelHeight;

 

            // DrawingVisual 에 전체 4분면에서 각 분면 이미지를 그립니다.

            DrawingVisual drawingVisual = new DrawingVisual();

            using (DrawingContext drawingContext = drawingVisual.RenderOpen())

            {

                drawingContext.DrawImage(frame1, new Rect(0, 0, imageWidth, imageHeight));

                drawingContext.DrawImage(frame2, new Rect(imageWidth, 0, imageWidth, imageHeight));

                drawingContext.DrawImage(frame3, new Rect(0, imageHeight, imageWidth, imageHeight));

                drawingContext.DrawImage(frame4, new Rect(imageWidth, imageHeight, imageWidth, imageHeight));

            }

 

            // RenderTargetBitmap 을 사용해 DrawingVisual 을 BitmapSource 객체로 변환합니다.

            RenderTargetBitmap renderTargetBitmap = new RenderTargetBitmap(imageWidth * 2, imageHeight * 2, 96, 96, PixelFormats.Pbgra32);

            renderTargetBitmap.Render(drawingVisual);

 

            // BitmapSource 를 PngBitmapEncoder 를 사용해 frame 에 추가합니다.

            PngBitmapEncoder encoder = new PngBitmapEncoder();

            encoder.Frames.Add(BitmapFrame.Create(renderTargetBitmap));

 

            // 이미지를 저장합니다.

            using (Stream stream = File.Create(outputFilePath))

            {

                encoder.Save(stream);

            }

        }

 

* ico 파일 지정도 가능

 

결과

Image 를 사용할때 코드 상에서 이미지 경로를 지정 할때

아래 처럼 사용합니다.

 

 

BitmapImage result = new BitmapImage();

result.BeginInit();

result.UriSource = new Uri(@"C:\Users\my\Desktop\100.PNG");

result.EndInit();

 

// Image control

this.resultImage.Source = result;

 

 

1. Self

{Binding RelativeSource={RelativeSource Self}, Path=Height}

: 자기 자신을 참조하는 것으로 위 내용은 자기 자신의 Height 값을 바인딩합니다.

 

2. FindAncestor

{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Border}, AncestorLevel=2},Path=Name}

: 부모를 찾아 바인딩하는 것으로 위 내용은 부모 중 Border 타입을 찾는데 2번째 부모를 찾아 Name 값을 바인딩합니다. (1이면 Border 타입중 바로 자기 상위 부모를 찾습니다.)

 

3. TemplatedParent

<Ellipse Height="100" Width="150" Fill="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Background}"/>

: 위 항목이 Style 에 정의 되어있는 경우 해당 Style 을 사용하는 항목의 요소를 변경한다.

  위내용은 원을 그릴때 해당 Style 를 사용하는 컨트롤의 BackGround 색으로 원을 체웁니다.

 

4. PreviousData

{Binding RelativeSource={RelativeSource PreviousData},Path=Value}

: 이전값을 가지는 것으로 위 내용은 이전 값의 Value 값을 바인딩 합니다.

 

 

참고 : https://www.c-sharpcorner.com/UploadFile/yougerthen/relativesources-in-wpf/

 

아래는 백업 용도 ---

 

A lot of articles those are talking about binding and sources, and how to bind properties each other using StaticResources, DynamicResources, although you can find information about the RelativeSource and its use cases but not with more details even in Microsoft documentations. In this article, I will expose the use cases of the RelativeSources in WPF.

The RelativeSource is a markup extension that is used in particular binding cases when we try to bind a property of a given object to another property of the object itself, when we try to bind a property of a object to another one of its relative parents, when binding a dependency property value to a piece of XAML in case of custom control development and finally in case of using a differential of a series of a bound data. All of those situations are expressed as relative source modes. I will expose all of those cases one by one.

1. Mode Self:

Imagine this case, a rectangle that we want that its height is always equal to its width, a square let's say. We can do this using the element name

<Rectangle Fill="Red" Name="rectangle"
                   
 Height="100" Stroke="Black"
                   
 Canvas.Top="100" Canvas.Left="100"
                    Width="{Binding ElementName=rectangle,
                    Path=Height}"/>

But in this above case we are obliged to indicate the name of the binding object, namely the rectangle. We can reach the same purpose differently using the RelativeSource

<Rectangle Fill="Red" Height="100"
                  
 Stroke="Black"
                  
 Width="{Binding RelativeSource={RelativeSource Self},
                   Path=Height}"/>

For that case we are not obliged to mention the name of the binding object and the Width will be always equal to the Height whenever the height is changed.

If you want to parameter the Width to be the half of the height then you can do this by adding a converter to the Binding markup extension.
Let's imagine another case now:

 <TextBlock Width="{Binding RelativeSource={RelativeSource Self},
                   Path=Parent.ActualWidth}"/>

The above case is used to tie a given property of a given element to one of its direct parent ones as this element holds a property that is called Parent. This leads us to another relative source mode which is the FindAncestor one.

2. Mode FindAncestor

In this case, a property of a given element will be tied to one of its parents, Of Corse. The main difference with the above case is the fact that, it's up to you to determine the ancestor type and the ancestor rank in the hierarchy to tie the property. By the way try to play with this piece of XAML

<Canvas Name="Parent0">
        <Border Name="Parent1"
                 Width="{Binding RelativeSource={RelativeSource Self},
                 Path=Parent.ActualWidth}"
                 Height="{Binding RelativeSource={RelativeSource Self},
                 Path=Parent.ActualHeight}">
            <Canvas Name="Parent2">
                <Border Name="Parent3"
                Width="{Binding RelativeSource={RelativeSource Self},
               Path=Parent.ActualWidth}"
               Height="{Binding RelativeSource={RelativeSource Self},
                  Path=Parent.ActualHeight}">
                   <Canvas Name="Parent4">
                   <
TextBlock FontSize="16"
                   Margin="5" Text="Display the name of the ancestor"/>
                   <TextBlock FontSize="16"
                     
Margin="50"
                
Text="{Binding RelativeSource={RelativeSource 
                           FindAncestor
,
                           
AncestorType={x:Type Border},
                           AncestorLevel=2},Path=Name}"
                           
Width="200"/>
                    </Canvas>
                </Border>
            </Canvas>
        </Border>
    </Canvas>

The above situation is of two TextBlock elements those are embedded within a series of borders and canvas elements those represent their hierarchical parents. The second TextBlock will display the name of the given parent at the relative source level.

So try to change AncestorLevel=2 to AncestorLevel=1 and see what happens. Then try to change the type of the ancestor from AncestorType=Border to AncestorType=Canvas and see what's happens.

The displayed text will change according to the Ancestor type and level. Then what's happen if the ancestor level is not suitable to the ancestor type? This is a good question, I know that you're about to ask it. The response is no exceptions will be thrown and nothings will be displayed at the TextBlock level.

3. TemplatedParent

This mode enables tie a given ControlTemplate property to a property of the control that the ControlTemplate is applied to. To well understand the issue here is an example bellow

<Window.Resources>
    <ControlTemplate x:Key="template">
            <Canvas>
                <Canvas.RenderTransform>
                    <RotateTransform Angle="20"/>
                    </Canvas.RenderTransform>
                <Ellipse Height="100" Width="150"
                    
 Fill="{Binding
               
 RelativeSource={RelativeSource TemplatedParent},
                Path=Background}">

                  </Ellipse>
                <ContentPresenter Margin="35"
                      Content
="{Binding RelativeSource={RelativeSource 
                      TemplatedParent
},Path=Content}"/>
            </Canvas>
        </ControlTemplate>
    </Window.Resources>
        <Canvas Name="Parent0">
        <Button   Margin="50"
                  Template
="{StaticResource template}" Height="0"
                  Canvas.Left
="0" Canvas.Top="0" Width="0">
            <TextBlock FontSize="22">Click me</TextBlock>
        </Button>
    </Canvas>

If I want to apply the properties of a given control to its control template then I can use the TemplatedParent mode. There is also a similar one to this markup extension which is the TemplateBinding which is a kind of short hand of the first one, but the TemplateBinding is evaluated at compile time at the contrast of the TemplatedParent which is evaluated just after the first run time. As you can remark in the bellow figure, the background and the content are applied from within the button to the control template.



4. PreviousData

This is the most ambiguous and the less used mode of the RelativeSource, I mean the PreviousData one. The PreviousData is used for particular cases. Its purpose is to tie the given property to another property with a particular assignment; I mean it assigns the previous value of the property to the bound one. In other word, if you have a TextBox that has a text property and another control that has a value property that holds data. Say that value is actually 5 and it was 3 just before. The 3 is assigned to the text property of the TextBox and not 5. This leads to the idea that this kind of RelativeSource is frequently used with the items controls.

To understand the phenomenon of the RelativeSource let's expose this sample. I will add an ItemsControl into the scene and I will aliment it from a custom collection

<Grid>
    <ItemsControl></ItemsControl>
</
Grid>

This ItemsControl is alimented using this collection:

public class Items : ObservableCollection<Item>
    {
        
public Items()
        {
            Add(
new Item { Value = 80.23 });
            Add(
new Item { Value = 126.17 });
            Add(
new Item { Value = 130.21 });
            Add(
new Item { Value = 115.28 });
            Add(
new Item { Value = 131.21 });
            Add(
new Item { Value = 135.22 });
            Add(
new Item { Value = 120.27 });
            Add(
new Item { Value = 110.25 });
            Add(
new Item { Value = 90.20 });
        }
    }


It's an ObservableCollection of type Item that I had developed and that holds a simple property which is called Value, it is of type double.

 
public class Item :INotifyPropertyChanged
    {
        
private double _value;

        public double Value
        {
            
get { return _value; }
            
set { _value = value; OnPropertyChanged("Value"); }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion

     protected void OnPropertyChanged(string PropertyName)
     {
       
if (null != PropertyChanged)
       {
         PropertyChanged(
this,
              
new PropertyChangedEventArgs(PropertyName));
       }
    }
 }

Now, to bind the 
ItemsControl to the collection data, I will set the DataContext property of the whole window to the collection at the Window constructor level.

 public Window1()

        {

            InitializeComponent();

            this.DataContext = new Items();

        }

And then I'll specify the binding of the ItemsControl

ItemsControl ItemsSource="{Binding}" Margin="10"

Then the result will be like this. I mean not presentable.



Therefore, we have to apply some features to enhance the visual of that representation.

<ItemsControl ItemsSource="{Binding}" Margin="10">

        <ItemsControl.ItemsPanel>
           <ItemsPanelTemplate>
              <StackPanel Orientation="Horizontal"/>
           </ItemsPanelTemplate>
           </ItemsControl.ItemsPanel>
       <ItemsControl.ItemTemplate>
          <DataTemplate>
              <StackPanel>
             <Border CornerRadius="3" BorderThickness="3"
                Width
="80" Height="{Binding Value}"
                            Margin
="0,0,35,0" 
                           
 BorderBrush="Violet" 
                            Background
="BlueViolet">
                            <TextBlock Text="{Binding Value}"
                               FontWeight
="bold"
                               VerticalAlignment
="Center"
                               
HorizontalAlignment="Center" 
                               Foreground
="Wheat">
                        <TextBlock.RenderTransform>
                  <TransformGroup>
                            <ScaleTransform ScaleY="-1"/>
                 
</TransformGroup>
                  </TextBlock.RenderTransform>
                  </TextBlock>
             </Border>
         </StackPanel>
      </DataTemplate>
   </ItemsControl.ItemTemplate>
   <
ItemsControl.RenderTransform>
       <TransformGroup>
         <ScaleTransform ScaleY="-1"/>
         <TranslateTransform Y="250"/>
       </TransformGroup>
   </ItemsControl.RenderTransform>
 </ItemsControl>


Shortly, I will describe the above XAML. First, the ItemsPanel will arrange the items within an horizontal StackPanel for more information about the ItemsPanel please refer to this link

http://msdn.microsoft.com/en-us/library/system.windows.controls.itemscontrol.itemspanel.aspx


Second, the DataTemplate is used to present the data as a border; the border height is bound to the Value of the item class to reflect the Values that the collection holds. The same border includes a TextBlock that displays the Value of the Item object.

For more information about te DateTemplate please refer to this MSDN link

http://msdn.microsoft.com/en-us/library/system.windows.datatemplate.aspx



The RenderTransform is used to emphasize the position of the items in the scene. For more information about transformation please refer to this MSDN link

http://msdn.microsoft.com/en-us/library/system.windows.media.transform.aspx


The result of the presentation will be as follow



Now, the main purpose of this demo is to show the characteristic of the RelativeSource.PreviousData mode.

The idea consists of adding a TextBox and tie the Text property to the Value of the previous border in the items' list. Something that seems to be as the bellow representation



As you can note, each TextBlock represents the previous value that the previous item holds. This is in fact the magic of the PreviousData of the RelativeSource mode.

The idea is to add the TextBlock to the DataTemplate as Follow

<TextBlock FontSize="14" FontWeight="bold" Margin="20"
            
Text="{Binding RelativeSource={RelativeSource PreviousData},
                   Path
=Value}">
                          <TextBlock.RenderTransform>
                              <ScaleTransform ScaleY="-1"/>
                          </TextBlock.RenderTransform>   
</TextBlock>

Then the whole picture will be

<Grid>
    <ItemsControl ItemsSource="{Binding}" Margin="10">
        <ItemsControl.RenderTransform>
            <TransformGroup>
                <ScaleTransform ScaleY="-1"/>
                <TranslateTransform Y="250"/>
            </TransformGroup>
        </ItemsControl.RenderTransform>
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <StackPanel Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <StackPanel>
                    <TextBlock FontSize="14" FontWeight="bold"
                           Margin
="20"
                           
Text="{Binding
                           RelativeSource
={RelativeSource PreviousData},
                                               Path
=Value}">
                      <TextBlock.RenderTransform>
                          <ScaleTransform ScaleY="-1"/>
                      </TextBlock.RenderTransform>   
                    
</TextBlock>
                    <Border CornerRadius="3" BorderThickness="3"
                           Width
="80" Height="{Binding Value}"
                           Margin
="0,0,35,0" 
                       
 BorderBrush="Violet" Background="BlueViolet">
                        <TextBlock Text="{Binding Value}"
                           FontWeight
="bold" VerticalAlignment="Center"
                                  
 HorizontalAlignment="Center"
                                   Foreground
="Wheat">
                        <TextBlock.RenderTransform>
                            <TransformGroup>
                        <ScaleTransform ScaleY="-1"/>
                        
</TransformGroup>
                        </TextBlock.RenderTransform>
                        </TextBlock>
                    </Border>
                </StackPanel>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>
</
Grid>

Of Course, we could do more that this using RelativeSource.PreviousData but this will be enough to have a general consistent idea about that mode. That's all

DrawingBrush 를 이용하여 체스판을 만들어 봅니다.

체스판의 한 조각을 만들어 이어 붙이는 예제입니다.

 

한조각은 아래 그림과 같으며

 

 

DrawingBrush 의 Viewport 와 TileMode 를 이용해 이어붙여 체스판을 완성합니다.

 

MainWindow.xaml.cs

        private void CreateChessBoardWithDrawingBrush()

        {

            // 반복하여 그려질 사각형의 바탕이 되는 흰색배경의 사각형을 만듭니다.

            GeometryDrawing whiteRectDrawing = new GeometryDrawing(Brushes.White, null, new RectangleGeometry(new Rect(0, 0, 400, 400)));

 

            // 검은색으로 채울 사각형을 정의합니다.

            GeometryGroup blackGeometryGroup = new GeometryGroup();

            blackGeometryGroup.Children.Add(new RectangleGeometry(new Rect(0, 0, 200, 200)));

            blackGeometryGroup.Children.Add(new RectangleGeometry(new Rect(200, 200, 200, 200)));

 

            // 정의한 사격형을 검은색으로 체웁니다.

            GeometryDrawing blackRectDrawing = new GeometryDrawing(new SolidColorBrush(Colors.Black), null, blackGeometryGroup);

 

            // 배경에 해당하는 흰색 사각형과 체크표시할 검은색 사각형을 그룹으로 묶습니다.

            DrawingGroup checkersDrawingGroup = new DrawingGroup();

            checkersDrawingGroup.Children.Add(whiteRectDrawing);

            checkersDrawingGroup.Children.Add(blackRectDrawing);

 

            // DrawingGroup 를 요소로 가지는 DrawingBrush 를 정의합니다.

            DrawingBrush chessBrush = new DrawingBrush() { Drawing = checkersDrawingGroup };

 

            // DrawingGroup 의 Viewport 와  TileMode 를 정의합니다.

            chessBrush.Viewport = new Rect(0, 0, 0.25, 0.25); // 1 기준으로 0.25 인경우 4칸.4줄로 나눠집니다.

            chessBrush.TileMode = TileMode.Tile;

 

            // 큰 사각형을 정의하여 DrawingBrush 로 내부를 그리도록 합니다.

            Rectangle chessRectangle = new Rectangle();

            chessRectangle.Width = 300;

            chessRectangle.Height = 300;

            chessRectangle.Fill = chessBrush;

 

            Content = chessRectangle;

        }

 

결과

 

 

 

 

마우스 드래그시 사각형을 그리고 사각형 주변으로 길이를 표시합니다.

 

MainWindow.xaml

<Window x:Class="WpfApp.MainWindow"

        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:local="clr-namespace:WpfApp"

        mc:Ignorable="d"

        Title="MainWindow" Height="450" Width="800" Background="Gray">

    <Grid Background="#01000000">

        <Canvas IsHitTestVisible="False" IsEnabled="False">

            <!-- Rectangle -->

            <ContentControl x:Name="RectAdder" Visibility="Collapsed">

                <ContentControl.ContentTemplate>

                    <DataTemplate>

                        <Grid >

                            <Rectangle

                                x:Name="Rect" SnapsToDevicePixels="True"

                                Fill="#770000FF" Stroke="White" StrokeThickness="1" StrokeDashArray="1 1 1 3" />

                            <!--위쪽-->

                            <Grid x:Name="DrawingHSizeAdorner">

                                <Rectangle

                                    Width="1" Height="15" Fill="White" SnapsToDevicePixels="True"

                                    IsHitTestVisible="False" IsEnabled="False"

                                    HorizontalAlignment="Left" VerticalAlignment="Top" Margin="0,-18,0,0"

                                    Tag="TopLeft"/>

                                <Rectangle

                                    Height="1" Width="{Binding ActualWidth, ElementName=Rect}"

                                    Fill="White" SnapsToDevicePixels="True"

                                    IsHitTestVisible="False" IsEnabled="False"

                                    HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,-10,0,0"

                                    Tag="TopCenter"/>

                                <Rectangle

                                    Width="1" Height="15" Fill="White" SnapsToDevicePixels="True"

                                    IsHitTestVisible="False" IsEnabled="False"

                                    HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,-18,0,0"

                                    Tag="TopRight"/>

                                <TextBlock

                                    Text="{Binding ActualWidth, ElementName=Rect, StringFormat='0.00 mm'}" Height="15" Width="{Binding ActualWidth, ElementName=Rect}"

                                    SnapsToDevicePixels="True" IsHitTestVisible="False" IsEnabled="False"

                                    HorizontalAlignment="Center" VerticalAlignment="Top"

                                    TextAlignment="Center" Foreground="White" FontSize="9"

                                    Margin="0,-25,0,0"

                                    ClipToBounds ="True"/>

                            </Grid>

                            <!--아래쪽-->

                            <Grid x:Name="DrawingBHSizeAdorner" >

                                <Rectangle

                                    Width="1" Height="15" Fill="White" SnapsToDevicePixels="True"

                                    IsHitTestVisible="False" IsEnabled="False"

                                    HorizontalAlignment="Left" VerticalAlignment="Bottom"

                                    Margin="0,0,0,-18"

                                    Tag="BottomLeft"/>

                                <Rectangle

                                    Height="1" Width="{Binding ActualWidth, ElementName=Rect}"

                                    Fill="White" SnapsToDevicePixels="True"

                                    IsHitTestVisible="False" IsEnabled="False"

                                    HorizontalAlignment="Center" VerticalAlignment="Bottom" Margin="0,0,0,-10"

                                    Tag="BottomCenter"/>

                                <Rectangle

                                    Width="1" Height="15" Fill="White" SnapsToDevicePixels="True"

                                    IsHitTestVisible="False" IsEnabled="False"

                                    HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,0,-18"

                                    Tag="BottomRight"/>

                                <TextBlock

                                    Text="{Binding ActualWidth, ElementName=Rect, StringFormat='0.00 mm'}" Height="15" Width="{Binding ActualWidth, ElementName=Rect}"

                                    SnapsToDevicePixels="True" IsHitTestVisible="False" IsEnabled="False"

                                    HorizontalAlignment="Center" VerticalAlignment="Bottom"

                                    TextAlignment="Center" Foreground="White" FontSize="9"

                                    Margin="0,0,0,-25"

                                    ClipToBounds ="True"/>

                            </Grid>

                            <!--왼쪽-->

                            <Grid x:Name="DrawingLVSizeAdorner" Margin="10,0,0,0">

                                <Rectangle Width="15" Height="1" Fill="White" SnapsToDevicePixels="True"

                                           IsHitTestVisible="False" IsEnabled="False"

                                           HorizontalAlignment="Left"

                                           VerticalAlignment="Top"

                                           Margin="-27,0,0,0"

                                           Tag="LeftTop"/>

                                <Rectangle Height="{Binding ActualHeight, ElementName=Rect}" Width="1" Fill="White" SnapsToDevicePixels="True"

                                           IsHitTestVisible="False" IsEnabled="False"

                                           HorizontalAlignment="Left" VerticalAlignment="Center" Margin="-20,0,0,0"

                                           Tag="LeftCenter"/>

                                <Rectangle Width="15" Height="1" Fill="White" SnapsToDevicePixels="True"

                                           IsHitTestVisible="False" IsEnabled="False"

                                           HorizontalAlignment="Left" VerticalAlignment="Bottom" Margin="-27,0,0,0"

                                           Tag="LeftBottom"/>

                                <StackPanel Margin="-35,70,0,0" HorizontalAlignment="Left" Height="25"

                                            SnapsToDevicePixels="True" IsHitTestVisible="False" IsEnabled="False"

                                            VerticalAlignment="Center">

                                    <StackPanel.RenderTransform>

                                        <RotateTransform Angle="270"/>

                                    </StackPanel.RenderTransform>

                                    <TextBlock Text="{Binding ActualHeight, ElementName=Rect, StringFormat='0.00 mm'}" Height="25" Foreground="White" FontSize="9"/>

                                </StackPanel>

                            </Grid>

                            <!--오른쪽-->

                            <Grid x:Name="DrawingVSizeAdorner" Margin="10,0,0,0">

                                <Rectangle

                                    Width="15" Height="1" Fill="White" SnapsToDevicePixels="True"

                                    IsHitTestVisible="False" IsEnabled="False"

                                    HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,0,-18,0"

                                    Tag="RightTop"/>

                                <Rectangle Height="{Binding ActualHeight, ElementName=Rect}" Width="1" Fill="White" SnapsToDevicePixels="True"

                                           IsHitTestVisible="False" IsEnabled="False"

                                           HorizontalAlignment="Right" VerticalAlignment="Center" Margin="0,0,-11,0"

                                           Tag="RightCenter"/>

                                <Rectangle Width="15" Height="1" Fill="White" SnapsToDevicePixels="True"

                                           IsHitTestVisible="False" IsEnabled="False"

                                           HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,-18,0"

                                           Tag="RightBottom"/>

                                <StackPanel Margin="0,0,-70,0" HorizontalAlignment="Right" Height="25"

                                            SnapsToDevicePixels="True" IsHitTestVisible="False" IsEnabled="False"

                                            VerticalAlignment="Center">

                                    <StackPanel.RenderTransform>

                                        <RotateTransform Angle="90"/>

                                    </StackPanel.RenderTransform>

                                    <TextBlock Text="{Binding ActualHeight, ElementName=Rect, StringFormat='0.00 mm'}" Height="25" Foreground="White" FontSize="9"/>

                                </StackPanel>

                            </Grid>

                            <TextBlock Text="+" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" />

                        </Grid>

                    </DataTemplate>

                </ContentControl.ContentTemplate>

            </ContentControl>

        </Canvas>

    </Grid>

</Window>

 

 

MainWindow.xaml.cs

using System;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Input;

 

namespace WpfApp

{

    /// <summary>

    /// MainWindow.xaml에 대한 상호 작용 논리

    /// </summary>

    public partial class MainWindow : Window

    {

        /// <summary>

        /// 좌측 마우스 클릭 시 포인트 위치입니다.

        /// </summary>

        Point? downPoint;

 

        /// <summary>

        /// 그려질 사각형입니다.

        /// </summary>

        Rect addRect;

 

        public MainWindow()

        {

            InitializeComponent();

        }

 

        /// <summary>

        /// 마우스 좌측 버튼 클릭 시 이벤트입니다.

        /// </summary>

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

        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)

        {

            var pos = e.GetPosition(this);

            downPoint = pos;

 

            this.CaptureMouse();

        }

 

        /// <summary>

        /// 마우스 이동 시 이벤트입니다.

        /// </summary>

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

        protected override void OnPreviewMouseMove(MouseEventArgs e)

        {

            base.OnPreviewMouseMove(e);

 

            if (Mouse.LeftButton == MouseButtonState.Pressed)

            {

                var pos = e.GetPosition(this);

 

                this.RectAdder.Visibility = Visibility.Visible;

 

                this.addRect = new Rect(new Point(downPoint.Value.X, downPoint.Value.Y), new Point(pos.X, pos.Y));

 

                Canvas.SetLeft(this.RectAdder, this.addRect.X);

                Canvas.SetTop(this.RectAdder, this.addRect.Y);

                this.RectAdder.Width = Math.Abs(this.addRect.Width);

                this.RectAdder.Height = Math.Abs(this.addRect.Height);

            }

        }

 

        /// <summary>

        /// 마우스를 놓을때 발생되는 이벤트입니다.

        /// </summary>

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

        protected override void OnMouseUp(MouseButtonEventArgs e)

        {

            this.ReleaseMouseCapture();

        }

    }

}

 

 

 


EventAggregator 는 이벤트들을 구독하거나 구독기능을 제공하며

컴포넌트 간의 이벤트 처리를 직접적인 참조 없이 가능하게 해줍니다.

 

MainWindow.xaml

<Window x:Class="WpfAppPrism.MainWindow"

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

        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

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

        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

        mc:Ignorable="d"

        Title="MainWindow" Height="450" Width="800">

    <StackPanel VerticalAlignment="Center">

        <Button Name="testButton" Content="Click!" Height="100" Width="200" HorizontalAlignment="Center" Click="testButton_Click"/>

        <Label Content=""/>

        <Button Name="unsubscribeButton" Content="Unsubscribe" Height="100" Width="200" HorizontalAlignment="Center" Click="unsubscribeButton_Click"/>

    </StackPanel>

</Window>

 

 

MainWindow.xaml.cs

using Microsoft.Practices.Composite.Events;

using Microsoft.Practices.Composite.Presentation.Events;

 

using System.Windows;

 

namespace WpfAppPrism

{

    /// <summary>

    /// string 을 인자를 가지는 이벤트 입니다.

    /// CompositePresentationEvent 는 이벤트 게시 및 구독을 관리하는 클래스입니다.

    /// </summary>

    public class SampleStringEvent : CompositePresentationEvent<string> { }

 

    public partial class MainWindow : Window

    {

        /// <summary>

        /// EventAggregator 입니다.

        /// </summary>

        EventAggregator aggregator = new EventAggregator();

 

        /// <summary>

        /// 구독 토큰입니다.

        /// </summary>

        private SubscriptionToken subscriptionToken;

 

        public MainWindow()

        {

            InitializeComponent();

 

            // 이벤트를 구독합니다.

            this.aggregator.GetEvent<SampleStringEvent>().Subscribe(StringAction1);

 

            //이벤트를 구독하고 구독취소를 위해 SubscriptionToken 을 받습니다.

            this.subscriptionToken = this.aggregator.GetEvent<SampleStringEvent>().Subscribe(StringAction2);

        }

 

        /// <summary>

        /// 이벤트 1 입니다.

        /// </summary>

        /// <param name="s"></param>

        private void StringAction1(string s)

        {

            MessageBox.Show($"{s}.StringAction1");

        }

 

        /// <summary>

        /// 이벤트 2 입니다.

        /// </summary>

        /// <param name="s"></param>

        private void StringAction2(string s)

        {

            MessageBox.Show($"{s}.StringAction2");

        }

 

        /// <summary>

        /// test 버튼 클릭 이벤트입니다.

        /// </summary>

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

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

        private void testButton_Click(object sender, RoutedEventArgs e)

        {

            // Subscribe 된 이벤트를 실행합니다.

            this.aggregator.GetEvent<SampleStringEvent>().Publish("testButton");

        }

 

        /// <summary>

        /// Unsubscribe 버튼 클릭 이벤트입니다.

        /// </summary>

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

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

        private void unsubscribeButton_Click(object sender, RoutedEventArgs e)

        {

            // 이벤트 하나를 Unsubscribe 합니다.

            this.aggregator.GetEvent<SampleStringEvent>().Unsubscribe(this.subscriptionToken);

        }

    }

}

 

 

 

우선 Click! 버튼을 클릭하면 "testButton.StringAction2" 메세지 박스가 나온후 "testButton.StringAction1" 메세지 박스가 나타난다.

구독한 순서 역순으로 이벤트는 발생된다.

 

여기서 Unsubscribe 버튼을 클릭하면 "testButton.StringAction1" 메세지 박스만 나타난다.




 

 

이미지를 특정 모양 부분만 보이도록 하는 방법입니다.

Clip 에 패스미니언어를 이용하면됩니다.

 

<Window x:Class="WpfApp3.MainWindow"

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

        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

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

        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

        mc:Ignorable="d"

        Title="MainWindow" Height="450" Width="600">

    <Image

         Clip="M 220 170 L 170 390 L 370 390 L 320 170 A 70 70 0 1 0 220 170"

         Stretch="None"

         Source="http://img2.yna.co.kr/etc/inner/KR/2019/09/09/AKR20190909107200009_04_i_P4.jpg" />

</Window>

 

 

CollectionView 는 필터를 적용할수 있습니다.

bool 값을 반환하는 메서드를 만들어 Filter 에 적용시키면됩니다.

 

bool 값을 반환하는 메서드를 아래 처럼 정의하고

* "K" 로 시작하는 값만 필터링하게 합니다.

 

        bool NameFilter(object obj)

        {

            return (obj as Person).Name.StartsWith

            (

                "K",

                StringComparison.CurrentCultureIgnoreCase

            );

        }

 

CollectionView 의 Filter = NameFilter; 로 적용하면 됩니다.

 

아래는 예시 코드입니다.

 

Person.cs

using System.ComponentModel;

 

namespace WpfApp

{

    public class Person : INotifyPropertyChanged

    {

        /// <summary>

        /// 속성변경 이벤트입니다.

        /// </summary>

        public event PropertyChangedEventHandler PropertyChanged;

 

        /// <summary>

        /// 이름입니다.

        /// </summary>

        string name = "";

 

        /// <summary>

        /// 별명입니다.

        /// </summary>

        string nickName = "";

 

        /// <summary>

        /// 이름입니다.

        /// </summary>

        public string Name

        {

            set

            {

                this.name = value;

                OnPropertyChanged(nameof(Name));

            }

            get { return name; }

        }

 

        /// <summary>

        /// 별명입니다.

        /// </summary>

        public string NickName

        {

            set

            {

                nickName = value;

                OnPropertyChanged(nameof(NickName));

            }

            get { return nickName; }

        }

 

        /// <summary>

        /// 속성 값이 변경될 때 발생합니다.

        /// </summary>

        /// <param name="propertyName"></param>

        protected virtual void OnPropertyChanged(string propertyName)

        {

            if (PropertyChanged != null)

            {

                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

            }

        }

    }

}

 

 

MainWindow.xaml

<Window x:Class="WpfApp3.MainWindow"

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

        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

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

        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

        mc:Ignorable="d"

        Title="MainWindow" Height="450" Width="400">

    <StackPanel>

        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Margin="10">

            <TextBox Name="searchTextBox" Text="" Width="200" VerticalContentAlignment="Center"/>

            <Button Name="searchButton" Content="Search" Padding="5" Margin="5" Click="searchButton_Click"/>

        </StackPanel>

        <TextBox

            Margin="12" Height="50" VerticalContentAlignment="Center"

            Text="{Binding Name,

                   Mode=TwoWay,

                   UpdateSourceTrigger=PropertyChanged}" />

        <TextBox

            Margin="12" Height="50" VerticalContentAlignment="Center"

            Text="{Binding NickName,

                   Mode=TwoWay,

                   UpdateSourceTrigger=PropertyChanged}" />

        

        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">

            <Button Name="prevButton" Content="Prev" Padding="10" Margin="10" Click="prevButton_Click"/>

            <Button Name="nextButton" Content="Next" Padding="10" Margin="10" Click="nextButton_Click"/>

        </StackPanel>

    </StackPanel>

</Window>

 

 

MainWindow.xaml.cs

using System;

using System.Collections.ObjectModel;

using System.ComponentModel;

using System.Linq;

using System.Windows;

using System.Windows.Data;

 

namespace WpfApp3

{

    public partial class MainWindow : Window

    {

        /// <summary>

        /// 데이터 collectionView 입니다.

        /// </summary>

        ICollectionView collectionView;

 

        public MainWindow()

        {

            InitializeComponent();

 

            ObservableCollection<Person> datas = new ObservableCollection<Person>();

 

            datas.Add(new Person() { Name = "Kang",  NickName = "Super" });

            datas.Add(new Person() { Name = "An",    NickName = "Father" });

            datas.Add(new Person() { Name = "Jang",  NickName = "Marvel" });

            datas.Add(new Person() { Name = "Joo",   NickName = "Mother" });

            datas.Add(new Person() { Name = "Kim",   NickName = "DC" });

 

            this.collectionView = CollectionViewSource.GetDefaultView(datas);

            this.collectionView.Filter = NameFilter;

            this.collectionView.CurrentChanged += CollectionView_CurrentChanged; ;

 

            this.collectionView.SortDescriptions.Add(new SortDescription(nameof(Person.Name), ListSortDirection.Ascending));

 

            this.DataContext = this.collectionView;

 

            // 처음값으로 이동시킵니다.

            this.collectionView.MoveCurrentToFirst();

        }

 

        /// <summary>

        /// 이전 값으로 이동하는 버튼 클릭이벤트입니다.

        /// </summary>

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

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

        private void prevButton_Click(object sender, RoutedEventArgs e)

        {

            this.collectionView.MoveCurrentToPrevious();

        }

 

        /// <summary>

        /// 다음 값으로 이동하는 버튼 클릭이벤트입니다.

        /// </summary>

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

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

        private void nextButton_Click(object sender, RoutedEventArgs e)

        {

            this.collectionView.MoveCurrentToNext();

        }

 

        /// <summary>

        /// CollectionView 현재값이 변경되었을때 발생되는 이벤트입니다.

        /// </summary>

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

        /// <param name="args"></param>

        private void CollectionView_CurrentChanged(object sender, EventArgs e)

        {

            // 이전/다음 항목 존재여부에 따라 이전/다음 버튼을 활성화하거나 비활성화 합니다.

            this.prevButton.IsEnabled = this.collectionView.CurrentPosition > 0;

            this.nextButton.IsEnabled = this.collectionView.CurrentPosition < this.collectionView.Cast<object>().Count() - 1;

        }

 

        /// <summary>

        /// Search 버튼 클릭이벤트입니다.

        /// </summary>

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

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

        private void searchButton_Click(object sender, RoutedEventArgs e)

        {

            // Refresh 하여 필터 적용된 내용이 보이도록합니다.

            this.collectionView.Refresh();

        }

 

        /// <summary>

        /// 이름 기준 필터입니다.

        /// </summary>

        /// <param name="obj"></param>

        /// <returns></returns>

        bool NameFilter(object obj)

        {

            return (obj as Person).Name.StartsWith

            (

                this.searchTextBox.Text,

                StringComparison.CurrentCultureIgnoreCase

            );

        }

    }

}

 

 

결과


결과를 보면 내용중에 필터링 된 내용만 볼수있도록 처리가 됩니다.


CollectionView 에 담겨진 데이터를 화면에 하나씩 나타내도록 하여

탐색하는 코드를 알아봅니다.

 

Person.cs

using System.ComponentModel;

 

namespace WpfApp

{

    public class Person : INotifyPropertyChanged

    {

        /// <summary>

        /// 속성변경 이벤트입니다.

        /// </summary>

        public event PropertyChangedEventHandler PropertyChanged;

 

        /// <summary>

        /// 이름입니다.

        /// </summary>

        string name = "";

 

        /// <summary>

        /// 별명입니다.

        /// </summary>

        string nickName = "";

 

        /// <summary>

        /// 이름입니다.

        /// </summary>

        public string Name

        {

            set

            {

                this.name = value;

                OnPropertyChanged(nameof(Name));

            }

            get { return name; }

        }

 

        /// <summary>

        /// 별명입니다.

        /// </summary>

        public string NickName

        {

            set

            {

                nickName = value;

                OnPropertyChanged(nameof(NickName));

            }

            get { return nickName; }

        }

 

        /// <summary>

        /// 속성 값이 변경될 때 발생합니다.

        /// </summary>

        /// <param name="propertyName"></param>

        protected virtual void OnPropertyChanged(string propertyName)

        {

            if (PropertyChanged != null)

            {

                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));

            }

        }

    }

}

 

 

MainWindow.xaml

<Window x:Class="WpfApp.MainWindow"

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

        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

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

        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

        mc:Ignorable="d"

        Title="MainWindow" Height="450" Width="400">

    <StackPanel>

        <TextBox

            Margin="12" Height="50" VerticalContentAlignment="Center"

            Text="{Binding Name,

                   Mode=TwoWay,

                   UpdateSourceTrigger=PropertyChanged}" />

        <TextBox

            Margin="12" Height="50" VerticalContentAlignment="Center"

            Text="{Binding NickName,

                   Mode=TwoWay,

                   UpdateSourceTrigger=PropertyChanged}" />

        

        <StackPanel Orientation="Horizontal" HorizontalAlignment="Center">

            <Button Name="prevButton" Content="Prev" Padding="10" Margin="10" Click="prevButton_Click"/>

            <Button Name="nextButton" Content="Next" Padding="10" Margin="10" Click="nextButton_Click"/>

        </StackPanel>

    </StackPanel>

</Window>

 

 

MainWindow.xaml.cs

using System;

using System.Collections.ObjectModel;

using System.ComponentModel;

using System.Linq;

using System.Windows;

using System.Windows.Data;

 

namespace WpfApp

{

    public partial class MainWindow : Window

    {

        /// <summary>

        /// 데이터 collectionView 입니다.

        /// </summary>

        ICollectionView collectionView;

 

        public MainWindow()

        {

            InitializeComponent();

 

            ObservableCollection<Person> datas = new ObservableCollection<Person>();

 

            datas.Add(new Person(){ Name = "Kang",  NickName = "Super" });

            datas.Add(new Person(){ Name = "An",    NickName = "Father" });

            datas.Add(new Person(){ Name = "Jang",  NickName = "Marvel" });

 

            this.collectionView = CollectionViewSource.GetDefaultView(datas);

            this.collectionView.CurrentChanged += CollectionView_CurrentChanged; ;

 

            this.collectionView.SortDescriptions.Add(new SortDescription(nameof(Person.Name), ListSortDirection.Ascending));

 

            this.DataContext = this.collectionView;

 

            // 처음값으로 이동시킵니다.

            this.collectionView.MoveCurrentToFirst();

        }

 

        /// <summary>

        /// 이전 값으로 이동하는 버튼 클릭이벤트입니다.

        /// </summary>

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

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

        private void prevButton_Click(object sender, RoutedEventArgs e)

        {

            this.collectionView.MoveCurrentToPrevious();

        }

 

        /// <summary>

        /// 다음 값으로 이동하는 버튼 클릭이벤트입니다.

        /// </summary>

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

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

        private void nextButton_Click(object sender, RoutedEventArgs e)

        {

            this.collectionView.MoveCurrentToNext();

        }

 

        /// <summary>

        /// CollectionView 현재값이 변경되었을때 발생되는 이벤트입니다.

        /// </summary>

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

        /// <param name="args"></param>

        private void CollectionView_CurrentChanged(object sender, EventArgs e)

        {

            // 이전/다음 항목 존재여부에 따라 이전/다음 버튼을 활성화하거나 비활성화 합니다.

            this.prevButton.IsEnabled = this.collectionView.CurrentPosition > 0;

            this.nextButton.IsEnabled = this.collectionView.CurrentPosition < this.collectionView.Cast<object>().Count() - 1;

        }

    }

}

 

 


 

+ Recent posts