Generate a complex grid in WPF programmatically - wpf

I have working complex grid in wpf written in XAML.
My purpose is to convert the XAML code to c#, so I am going to be able to generate this grid programmatically.
This is the working XAML code
<Grid>
<!--<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="2*"/>
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding ElementName=datagrid1, Path=RowHeaderWidth}" />
<ColumnDefinition Width="{Binding ElementName=Column1, Path=ActualWidth}" />
<ColumnDefinition Width="{Binding ElementName=Column2, Path=ActualWidth}" />
<ColumnDefinition Width="{Binding ElementName=Column3, Path=ActualWidth}" />
<ColumnDefinition Width="{Binding ElementName=Column4, Path=ActualWidth}" />
</Grid.ColumnDefinitions>
<Border Grid.Column="1" Grid.ColumnSpan="4" HorizontalAlignment="Stretch" BorderThickness="2">
<Label HorizontalAlignment="Center">Super Header!!</Label>
</Border>
</Grid>
<DataGrid AutoGenerateColumns="False"
Name="datagrid1" Grid.Row="1" Width="10">
<DataGrid.Columns>
<DataGridTextColumn Header="column 1" Width="100" x:Name="Column1" />
<DataGridTextColumn Header="column 2" Width="80" x:Name="Column2"/>
<DataGridTextColumn Header="column 3" Width="80" x:Name="Column3"/>
<DataGridTextColumn Header="column 4" Width="*" x:Name="Column4"/>
</DataGrid.Columns>
</DataGrid>-->
When I compile it shows the desire result which looks like
this: http://i.imgur.com/BK8GDtd.png?1
What I have tried is this :
Grid outernGrid = new Grid();
outernGrid.ShowGridLines = true;
RowDefinition row1 = new RowDefinition();
row1.Height = GridLength.Auto;
RowDefinition row2 = new RowDefinition();
row2.Height = new GridLength(2, GridUnitType.Star);
outernGrid.RowDefinitions.Add(row1);
outernGrid.RowDefinitions.Add(row2);
//Second grid
Grid innerGrid = new Grid();
ColumnDefinition col0 = new ColumnDefinition();
ColumnDefinition col1 = new ColumnDefinition();
ColumnDefinition col2 = new ColumnDefinition();
ColumnDefinition col3 = new ColumnDefinition();
ColumnDefinition col4 = new ColumnDefinition();
//Binding Initialize
Binding b0 = new Binding { ElementName = "dataGrid2", Path = new PropertyPath("RowHeaderWidth") };
Binding b1 = new Binding { ElementName = "Column1", Path = new PropertyPath("ActualWidth") };
Binding b2 = new Binding { ElementName = "Column2", Path = new PropertyPath("ActualWidth") };
Binding b3 = new Binding { ElementName = "Column3", Path = new PropertyPath("ActualWidth") };
Binding b4 = new Binding { ElementName = "Column4", Path = new PropertyPath("ActualWidth") };
//Set binding
BindingOperations.SetBinding(col1, DataGrid.ColumnWidthProperty, b1);
BindingOperations.SetBinding(col2, DataGrid.ColumnWidthProperty, b2);
BindingOperations.SetBinding(col3, DataGrid.ColumnWidthProperty, b3);
BindingOperations.SetBinding(col4, DataGrid.ColumnWidthProperty, b4);
BindingOperations.SetBinding(col0, DataGrid.ColumnWidthProperty, b0);
//Adding columns
innerGrid.ColumnDefinitions.Add(col0);
innerGrid.ColumnDefinitions.Add(col1);
innerGrid.ColumnDefinitions.Add(col2);
innerGrid.ColumnDefinitions.Add(col3);
innerGrid.ColumnDefinitions.Add(col4);
Label header = new Label();
header.Content = "This is the superheader";
//Datagrid
DataGrid dataGrid = new DataGrid { Name = "dataGrid2" };
dataGrid.AutoGenerateColumns = false;
dataGrid.RowHeaderWidth = 10;
//datagrid columns
DataGridTextColumn datacol1 = new DataGridTextColumn();
DataGridTextColumn datacol2 = new DataGridTextColumn();
DataGridTextColumn datacol3 = new DataGridTextColumn();
DataGridTextColumn datacol4 = new DataGridTextColumn();
datacol1.Header = "Column1";
datacol2.Header = "Column2";
datacol3.Header = "Column3";
datacol4.Header = "Column4";
datacol1.Width = 100;
datacol1.Width = 80;
datacol1.Width = 80;
datacol1.Width = DataGridLength.Auto;
Grid.SetColumn(header,1);
header.SetValue(Grid.ColumnSpanProperty,4);
Grid.SetRow(innerGrid, 0);
Grid.SetColumn(dataGrid, 1);
outernGrid.Children.Add(innerGrid);
outernGrid.Children.Add(dataGrid);
RootWindow.Content = outernGrid;
}
And the output is this
Can someone help me solving this issue.

I have two observations for you while I attempt to work on this.
First in your code sample you have this:
datacol1.Width = 100;
datacol1.Width = 80;
datacol1.Width = 80;
datacol1.Width = DataGridLength.Auto;
I imagine you wanted this:
datacol1.Width = 100;
datacol2.Width = 80;
datacol3.Width = 80;
datacol4.Width = DataGridLength.Auto;
Second, you do not name your columns in the Datagrid programmatically.
You'll have to do something like this for each column:
datacol1.ColumnName = "Column1";
datacol2.ColumnName = "Column2";
datacol3.ColumnName = "Column3";
datacol4.ColumnName = "Column4";

Related

FlowDocument to XPS - lost databinding in DataGrid

I've created a FlowDocument with databinded Datagrid.
Output in FlowDocumentScrollviewer is like this:
Next, I need to convert this FlowDocument into Fixed document (XPS), in order to print It in A4 paper size. But once I do that, my DataGrid data is lost.
Here is my XAML:
<Window x:Class="Test_Flow.Izpisi.DataGrid_DataTable"
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:Test_Flow.Izpisi"
mc:Ignorable="d"
Title="DataGrid_DataTable" Height="850" Width="850"
WindowStartupLocation="CenterScreen">
<Grid>
<FlowDocumentScrollViewer Name="Flow_reader" >
<FlowDocument Name="Flow_dokument" PageHeight="29.7cm" PageWidth="21cm" >
<Paragraph>Datagrid with DataTable example</Paragraph>
<BlockUIContainer>
<DataGrid BorderThickness="1" ItemsSource="{Binding Dt_Flow}" IsEnabled="False"
AutoGenerateColumns="False" HeadersVisibility="Column" BorderBrush="Black">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Number}"/>
<DataGridTextColumn Header="Surname" Binding="{Binding Surname}"/>
<DataGridTextColumn Header="Address" Binding="{Binding Address}"/>
<DataGridTextColumn Header="City" Binding="{Binding City}"/>
</DataGrid.Columns>
</DataGrid>
</BlockUIContainer>
</FlowDocument>
</FlowDocumentScrollViewer>
<DocumentViewer Name="doc_viewer" Visibility="Collapsed" />
</Grid>
</Window>
And my View_Model:
class My_ViewModel : INotifyPropertyChanged
{
public My_ViewModel(DocumentViewer doc_viewer, FlowDocument dokument, FlowDocumentScrollViewer page_viewer)
{
Fill_Table(); //Fill some test data
//Convert to XPS and display It in DocumentViewer
Convert_to_XPS(doc_viewer, dokument, page_viewer);
}
public DataTable Dt_Flow { get; set; } //DataTable for FlowDocument
private void Convert_to_XPS(DocumentViewer doc_viewer, FlowDocument dokument, FlowDocumentScrollViewer page_viewer)
{
//Convert FlowDocument to XPS
MemoryStream ms = new MemoryStream();
Package pkg = Package.Open(ms, FileMode.Create, FileAccess.ReadWrite);
string pack = "pack://report.xps";
PackageStore.RemovePackage(new Uri(pack));
PackageStore.AddPackage(new Uri(pack), pkg);
XpsDocument doc = new XpsDocument(pkg, CompressionOption.Maximum, pack);
XpsSerializationManager rsm = new XpsSerializationManager(new XpsPackagingPolicy(doc), false);
DocumentPaginator paginator = ((IDocumentPaginatorSource)dokument).DocumentPaginator;
rsm.SaveAsXaml(paginator);
//Hide FlowDocumentScrollViwer and show DocumentViewer
page_viewer.Visibility = Visibility.Collapsed;
doc_viewer.Document = doc.GetFixedDocumentSequence();
doc_viewer.Visibility = Visibility.Visible;
}
private void Fill_table()
{
//Fill test table
Dt_Flow = new DataTable();
Dt_Flow.Columns.Add("Number");
Dt_Flow.Columns.Add("Surname");
Dt_Flow.Columns.Add("Address");
Dt_Flow.Columns.Add("City");
int my_number = 1;
for (int i = 0; i < 10; i++)
{
DataRow _newRow = Dt_Flow.NewRow();
_newRow["Number"] = my_number + ".";
_newRow["Surname"] = "Johnson";
_newRow["Address"] = "Beverly Hills";
_newRow["City"] = "Los Angeles";
Dt_Flow.Rows.Add(_newRow);
my_number++;
}
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
Why is that happening, how can I fix this ?
EDIT: I added "Fill_table" method in "My_ViewMmodel", so you can test whole thing by yourself.

Wpf animation with mvvm pattern

The small example of work on animation, does not work variation of property of model, at animation
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="440" Width="732" Loaded="Window_Loaded"
MouseLeftButtonDown="Grid_MouseLeftButtonDown" MouseLeftButtonUp="Grid_MouseLeftButtonUp" MouseMove="Grid_MouseMove">
<Window.Resources>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="20"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="20"></RowDefinition>
</Grid.RowDefinitions>
<Canvas Name="canvMain" Height="360" Width="710" Grid.Row="1">
<Rectangle Name="Zombi" Width="20" Height="40" Stroke="Black" StrokeThickness="2" Canvas.Bottom="0" Canvas.Right="{Binding Path=X, UpdateSourceTrigger=PropertyChanged, NotifyOnTargetUpdated=True}"></Rectangle>
</Canvas>
</Grid>
public double X
{
get { return _x; }
set
{
if (!double.IsNaN(value))
{
_x = value;
OnPropertyChanged("X");
}
}
}
double pixelMetr = 0.715;
private int GetPixel(double metr)
{
return Convert.ToInt32(metr / pixelMetr);
}
private void StartZombi()
{
double distance = 250;
double maxTime = (new Random()).Next(5, 10);
PathGeometry animationPath = new PathGeometry();
LineGeometry lineGeometry = new LineGeometry();
lineGeometry.StartPoint = new Point(0, 0);
lineGeometry.EndPoint = new Point(GetPixel(distance), 0);
animationPath.AddGeometry(lineGeometry);
DoubleAnimationUsingPath animationX = new DoubleAnimationUsingPath();
animationX.PathGeometry = animationPath;
animationX.Duration = TimeSpan.FromSeconds(maxTime);
animationX.Source = PathAnimationSource.X;
Storyboard.SetTarget(animationX, Zombi);
Storyboard.SetTargetProperty(animationX, new PropertyPath(Canvas.RightProperty));
Storyboard pathAnimationStoryboard = new Storyboard();
pathAnimationStoryboard.RepeatBehavior = RepeatBehavior.Forever;
pathAnimationStoryboard.Children.Add(animationX);
pathAnimationStoryboard.Begin(this);
}
Hello why does not work property change, I need to get the current value of the property ' canvas.right ', when the animation runs.
This is xaml code.
OK, I found you a solution... all you need to do is to create a DependencyProperty for X and animate that instead:
public static readonly DependencyProperty XProperty = DependencyProperty.
Register("X", typeof(double), typeof(MainWindow));
public double X
{
get { return (double)GetValue(XProperty); }
set { SetValue(XProperty, value); }
}
Then your animation method:
private void StartZombi()
{
double distance = 250;
double maxTime = (new Random()).Next(5, 10);
PathGeometry animationPath = new PathGeometry();
LineGeometry lineGeometry = new LineGeometry();
lineGeometry.StartPoint = new Point(0, 0);
lineGeometry.EndPoint = new Point(GetPixel(distance), 0);
animationPath.AddGeometry(lineGeometry);
DoubleAnimationUsingPath animationX = new DoubleAnimationUsingPath();
animationX.PathGeometry = animationPath;
animationX.Duration = TimeSpan.FromSeconds(maxTime);
animationX.Source = PathAnimationSource.X;
Storyboard.SetTarget(animationX, This); // <<< 'This' is the Window.Name
Storyboard.SetTargetProperty(animationX,
new PropertyPath(MainWindow.XProperty));
Storyboard pathAnimationStoryboard = new Storyboard();
pathAnimationStoryboard.RepeatBehavior = RepeatBehavior.Forever;
pathAnimationStoryboard.Children.Add(animationX);
pathAnimationStoryboard.Begin(this);
}
And the XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<Canvas Name="canvMain" Height="360" Width="710" Grid.Row="1">
<Rectangle Name="Zombi" Width="20" Height="40" Stroke="Black"
StrokeThickness="2" Canvas.Bottom="60" Canvas.Right="{Binding Path=X,
UpdateSourceTrigger=PropertyChanged}" />
</Canvas>
<TextBlock Grid.Row="0" FontSize="26" Text="{Binding X}" />
</Grid>
The last thing that you need to do to make this work is to set the Window.Name property to "This".
To answer your original question as to why your code was not working, I'm not 100% sure, but I believe that it was because the Canvas.X property is not plugged into the INotifyPropertyChanged interface and so your original CLR X property setter was never being called. There is a similar situation when Binding to the Count property of a collection... it won't update the UI when you add items to the collection, as it is not notifying the changes.

Wpf dynamic design with Grid and data binding

I am creating dynamic UI using from code.I am adding grid to the Page using for loop,in multiple rows and 4 columns,so it takes time to load UI on Page.I want to load each child Grid lazily, I don't know how to do it, please help.
here is XAML file
<ScrollViewer Grid.Row="1" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" CanContentScroll="True" HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Auto" Margin="0 0 0 0">
<Grid x:Name="mainGrid" HorizontalAlignment="Stretch" VerticalAlignment="Top" Margin="10" Width="Auto">
</Grid>
</ScrollViewer>
and this is pseudo code from .cs file
for (int column = 0; column < dsMacDtls.Tables[0].Rows.Count; column++)
{
grid = new Grid();
grid.Width = 108;
grid.Height = 108;
if (column % 4 == 0)
{
mainGrid.RowDefinitions.Add(new RowDefinition());
}
else
{
mainGrid.RowDefinitions.Add(new RowDefinition());
}
box = new CheckBox();
TextBlock tb = new TextBlock();
tb.Text = "abcd";
box.Content = tb;
grid.Children.Add(box);
ImageBrush myBrush = new ImageBrush();
image = new Image();
image.Source = new BitmapImage(new Uri("abcd.png"));
grid.Background = myBrush;
Grid.SetColumn(grid, 0);
Grid.SetRow(grid, i);
Border br = new Border();
br.BorderThickness = new Thickness(1);
br.BorderBrush = new SolidColorBrush(Colors.Gray);
Grid.SetColumn(br, 0);
Grid.SetRow(br, rowNumberofMachine);
br.Width = 108;
br.Height = 108;
grid.Children.Add(br);
mainGrid.Children.Add(grid);
}

How to bind a Treeview's SelectedItem via DataSource to a Datagrid

I would need to bind selected treeview item (actually datasource of that) to datagrid.
F.ex. I have list of receipts, and under one receipt there is list of receipt-items. I would like to bind those items to datagrid when receipt is selected from treeview.
In datagrid receipt-items would be edited, so values should be bind to actual datasource.
How to do this?
Bind your DataGrids' ItemsSource to the selected Receipt in the Tree:
<TreeView x:Name="ReceiptsTree" ItemsSource="{Binding Receipts}"/>
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding ElementName=ReceiptsTree, Path=SelectedItem.ReceiptItems}">
<DataGrid.Columns>
<DataGridTextColumn Header="Receipt Name" Binding="{Binding Name}" />
....
</DataGrid.Columns>
</DataGrid>
This example does it all in the bare essence (With a little help of SvenG according the DataGrid).
Window.xaml:
<Window x:Class="WpfSOTreeviewSelectedItem.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TreeView Name="tvReceipts" Grid.Column="0">
<TreeView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Name}"></TextBlock>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<DataGrid AutoGenerateColumns="False" Grid.Column="1" ItemsSource="{Binding ElementName=tvReceipts, Path=SelectedItem.Items}">
<DataGrid.Columns>
<DataGridTextColumn Header="Ingredient" Binding="{Binding Name}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Code Behind MainWindow:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
List<Receipt> list = new List<Receipt>();
Receipt r;
r = new Receipt() { Name = "Pizza" };
list.Add(r);
ReceiptItem ri;
ri = new ReceiptItem() { Name = "Tomatoes" };
r.Items.Add(ri);
ri = new ReceiptItem() { Name = "Herbs" };
r.Items.Add(ri);
r = new Receipt() { Name = "Tortellini" };
list.Add(r);
ri = new ReceiptItem() { Name = "Flower" };
r.Items.Add(ri);
ri = new ReceiptItem() { Name = "Meat" };
r.Items.Add(ri);
r = new Receipt() { Name = "Tarte Tatin" };
list.Add(r);
ri = new ReceiptItem() { Name = "Apples" };
r.Items.Add(ri);
ri = new ReceiptItem() { Name = "Raisins" };
r.Items.Add(ri);
tvReceipts.ItemsSource = list;
}
}
public class Receipt
{
private String _name;
public String Name
{
get { return _name; }
set { _name = value; }
}
private List<ReceiptItem> _items = new List<ReceiptItem>();
public List<ReceiptItem> Items
{
get { return _items; }
set { _items = value; }
}
}
public class ReceiptItem
{
private String _name;
public String Name
{
get { return _name; }
set { _name = value; }
}
}

Area Calculation of CombinedGeometry parts

I need to calculate some areas based on Geometry intersection.
In my example I have the following Geometries:
Left RectangleGeometry
Right RectangleGeometry
EllipseGeometry
The Ellipse is in the middle of the Rectangles and I want two get the following data:
Area of the intersection between the Ellipse and left rectangle
Area of the intersection between the Ellipse and the right rectangle
Total Area of the Ellipse.
The issue is that the total area of the ellipse, EllipseGeometry.GetArea(), and the "LeftEllipseGeometry".GetArea() + "RightEllipseGeometry".GetArea() are different.
The sum of intersections areas have to be the same as the ellipe Area.
I made an example where you can test and see the problem.
MainWindow.xaml.cs
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
//LEFT
rectLeft = new RectangleGeometry();
rectLeft.Rect = new Rect(new Point(75, 100), new Point(700, 600));
Path pathRectLeft = new Path();
pathRectLeft.Stroke = Brushes.Red;
pathRectLeft.Data = rectLeft;
grdMain.Children.Add(pathRectLeft);
//RIGHT
rectRight = new RectangleGeometry();
rectRight.Rect = new Rect(new Point(700, 100), new Point(1300, 600));
Path pathRectRight = new Path();
pathRectRight.Stroke = Brushes.Green;
pathRectRight.Data = rectRight;
grdMain.Children.Add(pathRectRight);
//ELLIPSE
ellipseGeo = new EllipseGeometry();
ellipseGeo.RadiusX = 200;
ellipseGeo.RadiusY = 200;
ellipseGeo.Center = new Point(700, 350);
Path ellipsePath = new Path();
ellipsePath.Stroke = Brushes.Blue;
ellipsePath.Data = ellipseGeo;
grdMain.Children.Add(ellipsePath);
lblEllipseArea.Content = String.Concat("Area Ellipse = ", ellipseGeo.GetArea());
}
private void Button_Click(object sender, RoutedEventArgs e)
{
CombinedGeometry cgLeft = new CombinedGeometry();
cgLeft.Geometry1 = rectLeft;
cgLeft.Geometry2 = ellipseGeo;
cgLeft.GeometryCombineMode = GeometryCombineMode.Intersect;
Path cgLeftPath = new Path();
cgLeftPath.Stroke = Brushes.Yellow;
cgLeftPath.Data = cgLeft;
grdMain.Children.Add(cgLeftPath);
lblEllipseAreaLeft.Content = String.Concat("Area Left Ellipse = ", cgLeft.GetArea());
CombinedGeometry cgRight = new CombinedGeometry();
cgRight.Geometry1 = rectRight;
cgRight.Geometry2 = ellipseGeo;
cgRight.GeometryCombineMode = GeometryCombineMode.Intersect;
Path cgRightPath = new Path();
cgRightPath.Stroke = Brushes.White;
cgRightPath.Data = cgRight;
grdMain.Children.Add(cgRightPath);
lblEllipseAreaRight.Content = String.Concat("Area Right Ellipse = ", cgRight.GetArea());
lblEllipseTotal.Content = String.Concat("Area Ellipse Total = ", cgLeft.GetArea() + cgRight.GetArea());
}
MainWindow.xaml
<Grid>
<StackPanel Orientation="Vertical" Background="Black">
<Grid Background="Black" Height="700" Name="grdMain">
</Grid>
<Grid Background="Black" Height="150">
<StackPanel Orientation="Vertical">
<Button Height="30" Width="70" Click="Button_Click">Click Me!!!</Button>
<StackPanel Orientation="Horizontal">
<Label Foreground="White" Name="lblEllipseArea"></Label>
<Label Foreground="White" Name="lblEllipseArea2" Margin="20 0 0 0"></Label>
<Label Foreground="White" Name="lblEllipseAreaRight" Margin="20 0 0 0"></Label>
<Label Foreground="White" Name="lblEllipseAreaRight2" Margin="20 0 0 0"></Label>
<Label Foreground="White" Name="lblEllipseAreaLeft" Margin="20 0 0 0"></Label>
<Label Foreground="White" Name="lblEllipseAreaLeft2" Margin="20 0 0 0"></Label>
</StackPanel>
<StackPanel Orientation="Horizontal">
<Label Foreground="White" Name="lblEllipseTotal" Margin="20 0 0 0"></Label>
<Label Foreground="White" Name="lblEllipseTotal2" Margin="20 0 0 0"></Label>
</StackPanel>
</StackPanel>
</Grid>
</StackPanel>
</Grid>
I think you might never get the "exact" areas from the CombinedGeometry. As expected, WPF does not use the "ideal" method to calculate this values. From MSDN: "Some Geometry methods (such as GetArea) produce or use a polygonal approximation of the geometry".
Check MSDN

Resources