Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Building a Personal Diary with WPF

0.00/5 (No votes)
12 Jan 2008 1  
An article on how to build a personal diary with WPF.

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:

  1. Source code of Personal Diary WPF
  2. Source code of ASP.NET
  3. 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.

Web site project run

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;
 //Response.BinaryWrite(movieContents);
 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)
 {
 //Quick and Dirty
  ...
 me.Height *= 4;
 me.Width *= 4;
  ...
 }
 else
 {
 //Quick and Dirty
 ...
 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

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here