Introduction
Some people like to write down any event that occurs like their birthday or parties. They record the event from the recording device and save it everywhere so it can easily get lost. This little software just helps to organize the event and organize the video.
Background
Windows Presentation Foundation (WPF) is a new technology from Microsoft that allows the developer to manipulate the user interface more easily. The developer can act as a designer to build a user interface in a more interactive manner.
Until today, WPF does not have a property or method that handles stream data like MediaElement
. MediaElement
uses property Source
to grab the media or streaming media; it cannot grab from byte[]
or Stream
. It is not nice since it can come from many difference sources.
Requirement
- Windows XP with SP2 or Window Vista
- Microsoft Visual C# 2008 and Visual Web Developer 2008 Express Edition
- SQL Express 2005 with SP2
- XCeed Component: Datagrid for WPF. You can download XCeed Component: Datagrid for WPF. You can download it for free here
- Some videos with *.wmv extension to try these samples. I already tried with video having a maximum length of 100 MB
Prepare Everything
There are three files that need to be downloaded:
- Source code of Personal Diary WPF
- Source code of ASP.NET
- An SQL file
The source code of Personal Diary WPF is the main application. It can be opened using Visual C# 2008 Express Edition.
The source code of ASP.NET as video stream is the ASP.NET that can only produce the media streaming. For now I only produce Windows Video (*.wmv) and MPEG video (*.mpg). It can be opened using Visual Web Developer 2008 Express Edition.
An SQL file can be opened using SQL Server Management Studio Express. My database name is persdiary. You can change it and don't forget to change the connection string in the app.config. Run the SQL so that the tables are ready to use.
Open both projects - WPF project and Web site. Since not all windows installation equips with IIS, I use a built-in web server that comes with the product. Run the website first to make sure the Web server up and running.
After the website runs and the browser shows the result, copy the address URL from the browser to the value in the appSetting and find the key of hostpath.
Code from Webstream Website Project
Since MediaElement control with Source
property from WPF cannot receive byte[]
or Stream
, we can manipulate it using ASP.NET as a video stream. First ASP.NET will load the data from the database. The video itself saves in the varbinary
data types in the mstvideo
table.
On the Page Load Method
if (Request.QueryString.Count > 0)
{
string videoid = Request.QueryString["vid"];
if (!string.IsNullOrEmpty(videoid))
{
bool loadFull = false;
string loadFullStr = Request.QueryString["loadfull"];
if (!string.IsNullOrEmpty(loadFullStr)) loadFull = Convert.ToBoolean(loadFullStr);
else
{
Response.Write("<h1>Full Load or not?</h1>");
}
byte[] result = ReadVideo(videoid, loadFull);
WriteVideoToPage(result);
}
else
{
Response.Write("<h1>Need Video ID</h1>");
}
}
else
{
Response.Write("<h1>Need Video ID</h1>");
}
This code will detect the query string and call the method to query data from the database and write the result to this page. There are only two query strings: vid
and loadfull
. Query string vid
is a video id that is useful to load video data by video id from the table. Query string loadfull
is a flag not to load the video fully; in this case I will load 1/8th of total bytes received from the table.
On the ReadVideo Method
using (SqlConnection connection = new SqlConnection(ConnectionString))
{
connection.Open();
SqlCommand command = connection.CreateCommand();
command.CommandType = CommandType.Text;
StringBuilder sb = new StringBuilder();
sb.Append("SELECT ");
sb.Append("DATALENGTH(vdcontent) AS vdcontent_length, ");
sb.Append(" vdcontent,vdformat ");
sb.Append(" FROM mstvideo ");
sb.Append(" WHERE vdid=@vdid ");
command.CommandText = sb.ToString();
command.Parameters.Add("@vdid", SqlDbType.Char).Value = videoid;
using (SqlDataReader reader = command.ExecuteReader())
int startIdx = 0;
long retval = 0;
if (!reader.HasRows)
{
Response.Write("<h1> Don't have rows ! </h1>");
}
while (reader.Read())
{
if (string.Compare(reader.GetString(reader.GetOrdinal
("vdformat")),".wmv",true)==0)
Response.ContentType = "video/x-ms-wmv";
else if (string.Compare(reader.GetString
(reader.GetOrdinal("vdformat")),".mpg",true)==0)
Response.ContentType = "video/mpeg";
int buffersize = reader.GetInt32(reader.GetOrdinal("vdcontent_length"));
if (!loadFull)
buffersize /= 8;
movieContainer = new byte[buffersize];
retval =reader.GetBytes(reader.GetOrdinal("vdcontent"), startIdx,
movieContainer, 0, buffersize);
}
}
}
The code above queries the video from the table. The select statement DATALENGTH(vdcontent)
measures the length of data that is stored in vdcontent
. I use command.ExecuteReader()
to execute the query. The result is the length of the video, the video itself and the video format. The video format is useful to choose the MIME type of the ASP.NET. For now, I only support WMV and MPG. Again if loadfull is false
, then I set the buffersize smaller; in this case I only divide the size by 8.
On the WriteVideoToPage Method
Response.BufferOutput = true;
BinaryWriter binWriter = new BinaryWriter(Response.OutputStream);
binWriter.Write(videoData);
binWriter.Flush();
ASP.NET will render the video by writing the contents using System.IO.BinaryWriter
. The ASPX file itself is empty.
RegistryKey regKey = Registry.CurrentUser;
regKey = regKey.OpenSubKey("Software");
regKey = regKey.OpenSubKey("Nicko");
if (regKey != null)
{
result = regKey.GetValue("ConnectionString").ToString();
}
I use the registry key to get the connection string. So you can change the connection string once in the WPF app.config.
Code from Personal Diary WPF Project
This is the main application that is written in WPF. The application can add a diary event and the video. You can click the video in the list below to enlarge the video to the center. Don't forget to add a reference to XCeed datagrid for WPF to make this work.
First I add two new namespaces in MainWindow.xaml - The XCeed datagrid and local namespace:
xmlns:local="clr-namespace:Diary.WPF"
xmlns:xceed="clr-namespace:Xceed.Wpf.DataGrid;assembly=Xceed.Wpf.DataGrid"
I add the Xceed datagrid to the grid layout:
<xceed:DataGridControl HorizontalAlignment="Left" Margin="10,10,0,0"
x:Name="dgvEvents" VerticalAlignment="Top" Width="380"
MaxHeight="180" Height="180" SelectionMode="Single" AutoCreateColumns="False"
NavigationBehavior="RowOnly" TabIndex="0" ReadOnly="True"
Mouse.MouseUp="Datagrid_MouseUp" Keyboard.KeyUp="DataGrid_KeyUp">
<xceed:DataGridControl.Columns>
<xceed:Column FieldName="EvDate" VisiblePosition="0" Title="Date " Width="80" />
<xceed:Column FieldName="EvName" VisiblePosition="1" Title="Name" Width="80"
TextWrapping="Wrap"/>
<xceed:Column FieldName="EvStories" VisiblePosition="2" Title="Stories"
TextWrapping="Wrap" Width="190"/>
</xceed:DataGridControl.Columns>
</xceed:DataGridControl>
The grid will change the selected item when the mouse is clicked and keyboard is pressed up and down. It will trigger to refresh the video content.
private void Datagrid_MouseUp(object sender, MouseButtonEventArgs e)
{
RefreshContents();
}
....
private void DataGrid_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Up || e.Key == Key.Down)
{
RefreshContents();
}
}
I add the buttons that have a function to add event and add video.
...
<Button Height="23" x:Name="btnAddEvent" Width="30"
Click="btnAddEvent_Click">Add</Button>
...
<Button x:Name="btnAddNewVideo" Click="btnAddNewVideo_Click">Add new</Button>
The button will trigger an event to open a dialog window.
private void btnAddEvent_Click(object sender, RoutedEventArgs e)
{
...
bool? result = dlgInsert.ShowDialog();
...
}
}
The method will open the new event dialog window:
private void btnAddNewVideo_Click(object sender, RoutedEventArgs e)
{
...
bool? result = dlgVideo.ShowDialog();
...
}
The method will open the new video dialog window:
When I click one of the videos below, the video will enlarge in the middle and play the video in full. When I click that large video, it will go back to the list below:
private void me_MouseUp(object sender, MouseButtonEventArgs e)
MediaElement me = sender as MediaElement;
if (stkpanVideo.Children.IndexOf(me) >= 0)
{
...
me.Height *= 4;
me.Width *= 4;
...
}
else
{
...
me.Height /= 4;
me.Width /= 4;
...
}
}
I create an XAML to act as a window dialog to add events to the table. It is Insert InsertEventDialog.xaml. I use a grid layout to create a table-like layout with textblocks and textboxes as input controls. I use a Windows Form DateTimePicker
to choose event date. I create InsertNewVideo.xaml to add a video for the selected diary event.
...
...
<TextBlock .../>
<TextBox .../>
...
<WindowsFormsHost Name="wpfHost">
<windowsform:DateTimePicker ...</WindowsFormsHost>
Points of Interest
I learnt how to bind data to the WPF control using the ObservableCollection <of T>
, not only Dataset
or Datatable
. I just write the property name to the binding source in the control property and automatically bind.
ASP.NET can be used as a streaming video source, so I can use it as a dynamic source for MediaElement
.
History
- Version 1.0: 28 December 2007 - Application released