WPF를 처음 사용하시는 분들~ ^^
쉽지 않으셨으리라 생각됩니다. 관련된 테마별로 소스와 문서가 잘 정리되어 있는 링크를 공유합니다.
Visual Studio 2010과 함께 WPF도 4.0으로 업그레이드 될 예정입니다.
http://code.msdn.microsoft.com/wpfsamples
This is the way how I implemented paging among a wrap panel in WPF. For this example I used simple buttons inside the wrap panel. My example dynamically takes the height of the control inside the wrap panel to determine the quantity of controls that the wrap panel must show.
In this example the columns always stay static, 1 column Min and 2 columns Max. You can also create a column function to show dynamically the quantity of columns you want.
The result is this:
This is the window when is loaded:
This is the window when is resized:
XAML:
<Window x:Class=”PagingWrapPanel.PagingWithDuff”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
Title=”PagingWithDuff” Height=”250″ Loaded=”Window_Loaded”
MaxHeight=”500″ MinHeight=”120″ MaxWidth=”500″ MinWidth=”500″>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height=”24″ />
<RowDefinition Height=”26″ />
</Grid.RowDefinitions>
<ItemsControl Grid.Row=”0″ Name=”RowsContainer” Background=”Transparent” ItemsSource=”{Binding}” SizeChanged=”RowsContainer_SizeChanged” >
<ItemsControl.ItemTemplate>
<DataTemplate>
<Label />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation=”Vertical” IsItemsHost=”True”
Width=”Auto” HorizontalAlignment=”Center” />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<Grid Grid.Row=”1″ HorizontalAlignment=”Center” >
<Label Name=”label1″ VerticalAlignment=”Top” Height=”Auto” >Page 1 out of 1</Label>
</Grid>
<Grid Grid.Row=”2″ >
<Grid.ColumnDefinitions>
<ColumnDefinition Width=”*”/>
<ColumnDefinition Width=”*” />
</Grid.ColumnDefinitions>
<Button Grid.Column=”0″ x:Name=”btnBack” Click=”btnBack_Click” MaxHeight=”25″ >Back</Button>
<Button Grid.Column=”1″ x:Name=”btnNext” Click=”btnNext_Click” MaxHeight=”25″ >Next</Button>
</Grid>
</Grid>
</Window>
By the way, you must change in the App.xaml the StartupUri string to - StartupUri=”PagingWrapPanel.xaml” -
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
namespace PagingWrapPanel
{
///
/// Interaction logic for Window1.xaml
///
public partial class PagingWithDuff : Window
{
ObservableCollection<Label> collection;
WrapPanel currentPanel;
int rowsInPanel, pagingPressed;
double height;
//RowsContainer SizeChanged event is raised before the Loaded event for this class
//We need to raise Loaded event first, using this variable
bool isLoaded;
public PagingWithDuff()
{
InitializeComponent();
collection = new ObservableCollection<Label>();
isLoaded = false;
//Generate 100 Labels to Page
for (int generator = 1; generator <= 100; generator++)
{
Label newLbl = new Label();
newLbl.Name = “Label” + generator;
newLbl.Content = “Label “ + generator;
newLbl.Width = 160;
newLbl.Background = Brushes.Black;
newLbl.Foreground = Brushes.White;
newLbl.HorizontalContentAlignment = HorizontalAlignment.Center;
collection.Add(newLbl);
}
btnBack.IsEnabled = false;
btnNext.IsEnabled = false;
pagingPressed = 1;
RowsContainer.DataContext = collection;
}
//Calculate how many rows will be showed in the container
public int rowsToShow()
{
currentPanel = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(RowsContainer, 0), 0), 0) as WrapPanel;
int rows = 0;
height = this.currentPanel.ActualHeight;
double RowControlHeight = 0.0;
//Get the first Visible Label in the Panel and get its Height
for (int iterator = 0; iterator <= collection.Count - 1; iterator++)
{
Visibility isVisible = (Visibility)(currentPanel.Children[iterator] as Label).Visibility;
//If is visible get height and break iteration
if (isVisible == Visibility.Visible)
{
RowControlHeight = (double)(currentPanel.Children[iterator] as Label).ActualHeight;
break;
}
}
rows = (int)Math.Floor((decimal)height / (decimal)RowControlHeight);
return rows;
}
//Shows the controls inside the wrap panel
public void setRowsControlsToShow()
{
bool endOfList = false;
bool startOfList = false;
bool medOfList = false;
bool overflow = false;
int collectionCount = 0;
collectionCount = collection.Count;
//Once the datacontext is displayed, we need to show up only the labels in this case we need
if (RowsContainer.Items.Count > 0)
{
int start = PagingWithDuff.startFromIndex(rowsToShow(), pagingPressed);
int end = PagingWithDuff.endInIndex(start, rowsToShow() * 2);
if (start > collectionCount - 1)
//If this happens, means that the panel have no content to show
{
end = collectionCount - 1;
start = (collectionCount - 1) - rowsToShow();
overflow = true;
}
if (start == 0) startOfList = true;
if (end > collectionCount)
{
end = collectionCount - 1;
endOfList = true;
}
else
{
if (!startOfList && !endOfList) { medOfList = true; }
}
for (int iter = 0; iter <= collectionCount - 1; iter++)
{
Label currentRow = currentPanel.Children[iter] as Label;
if ((iter < start) || (iter > end))
{
currentRow.Visibility = Visibility.Collapsed;
}
else
{
currentRow.Visibility = Visibility.Visible;
}
}
if (endOfList)
{
btnNext.IsEnabled = false;
if (pagingPressed > 1) btnBack.IsEnabled = true;
}
else
btnNext.IsEnabled = true;
if (medOfList)
{
if (end <= collectionCount - 1)
{
btnBack.IsEnabled = true;
}
}
if (startOfList)
{
btnBack.IsEnabled = false;
if ((rowsInPanel * 2) <= collectionCount)
btnNext.IsEnabled = true;
}
if (overflow)
{
btnNext.IsEnabled = false;
pagingPressed -= 1;
}
}
else
{
btnNext.IsEnabled = false;
btnBack.IsEnabled = false;
}
}
public void setPagingInfo()
{
int totalQty = collection.Count;
rowsInPanel = rowsToShow();
int totalPages = (int)Math.Floor((double)totalQty / rowsInPanel);
if (totalPages == 0)
{
totalPages = 1;
btnNext.IsEnabled = false;
btnBack.IsEnabled = false;
}
if (pagingPressed > totalPages)
this.label1.Content = “Page “ + totalPages + ” out of “ + totalPages;
else
this.label1.Content = “Page “ + pagingPressed + ” out of “ + totalPages;
}
private static int startFromIndex(int qtyToShow, int page)
{
if (page == 1) return 0;
return ((page - 1) * qtyToShow);
}
private static int endInIndex(int start, int qtyToShow)
{
return (start + qtyToShow) - 1;
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
setRowsControlsToShow();
setPagingInfo();
isLoaded = true;
}
private void RowsContainer_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (isLoaded)
{
setRowsControlsToShow();
setPagingInfo();
}
}
private void btnNext_Click(object sender, RoutedEventArgs e)
{
pagingPressed++;
setRowsControlsToShow();
setPagingInfo();
}
private void btnBack_Click(object sender, RoutedEventArgs e)
{
pagingPressed -= 1;
setRowsControlsToShow();
setPagingInfo();
}
}
}
01.
// This example shows how a named mutex is used to signal between
02.
// processes or threads.
03.
// Run this program from two (or more) command windows. Each process
04.
// creates a Mutex object that represents the named mutex "MyMutex".
05.
// The named mutex is a system object whose lifetime is bounded by the
06.
// lifetimes of the Mutex objects that represent it. The named mutex
07.
// is created when the first process creates its local Mutex; in this
08.
// example, the named mutex is owned by the first process. The named
09.
// mutex is destroyed when all the Mutex objects that represent it
10.
// have been released.
11.
// The second process (and any subsequent process) waits for earlier
12.
// processes to release the named mutex.
13.
using
System;
14.
using
System.Threading;
15.
public
class
Test
16.
{
17.
public
static
void
Main()
18.
{
19.
// Set this variable to false if you do not want to request
20.
// initial ownership of the named mutex.
21.
bool
requestInitialOwnership =
true
;
22.
bool
mutexWasCreated;
23.
// Request initial ownership of the named mutex by passing
24.
// true for the first parameter. Only one system object named
25.
// "MyMutex" can exist; the local Mutex object represents
26.
// this system object. If "MyMutex" is created by this call,
27.
// then mutexWasCreated contains true; otherwise, it contains
28.
// false.
29.
Mutex m =
new
Mutex(requestInitialOwnership,
30.
"MyMutex"
,
31.
out
mutexWasCreated);
32.
// This thread owns the mutex only if it both requested
33.
// initial ownership and created the named mutex. Otherwise,
34.
// it can request the named mutex by calling WaitOne.
35.
if
(!(requestInitialOwnership && mutexWasCreated))
36.
{
37.
Console.WriteLine(
"Waiting for the named mutex."
);
38.
m.WaitOne();
39.
}
40.
// Once the process has gained control of the named mutex,
41.
// hold onto it until the user presses ENTER.
42.
Console.WriteLine(
"This process owns the named mutex. "
+
43.
"Press ENTER to release the mutex and exit."
);
44.
Console.ReadLine();
45.
// Call ReleaseMutex to allow other threads to gain control
46.
// of the named mutex. If you keep a reference to the local
47.
// Mutex, you can call WaitOne to request control of the
48.
// named mutex.
49.
m.ReleaseMutex();
50.
}
51.
}
하지만 WPF에서 위에 방법을 App.xaml.cs 에 적용시켜도 원하는 데로 동작하지 않습니다.
그래서 오늘은 WPF에서 사용할 수 있는 방법을 올려드리겠습니다.
아래 예제는 뮤텍스를 사용한 중복 방지 소스를 구글링해서 힘들게 찾은 소스입니다.
제가 잘 동작하는 것은 확인 하였습니다. 이 밖에 다른 예제 소스도 보여드리겠습니다만 아래 예제를 쓰시는 것이 가장 좋습니다.
위에 예제는 OnStartUp이벤트와 Mutex의 WaitOne을 이용하여 중복방지를 구현하였습니다.
그럼 이번에는 약간 다른 방법으로 구현해 보겠습니다.
이 방법은 WPF 초기화를 C# 코드로 직접 구현하는 방식입니다.
이 방법을 사용하면 App.xaml의 Build Action 속성을 ApplicationDefinition 에서 Page로 변경해 주어야 합니다.
WPF가 구성해주는 진입점을 직접 작성하셔야 하므로 여러가지로 귀찮은 일이 생길 수 있을 거 같습니다.
마지막으로 소개해 드릴 방법은 프로세서의 네임 중복을 체크하는 방법입니다.
이 방법은 잘 동작하지만 실행파일의 이름을 바꾸기만 해도 쉽게 중복 실행이 가능해 집니다.
이런 방법들을 사용하시면 WPF에서 중복방지를 구현하실수 있습니다.
길게 읽지 않으시고 첫번째 소개해 드린 방법을 사용하시면 됩니다.
나머지는 참고만 하셔도 될 것 같습니다.
출처 : http://wpfkorea.com/?document_srl=3047#0