用 WPF 构建个人日记






3.53/5 (6投票s)
一篇关于如何使用WPF构建个人日记的文章。

引言
有些人喜欢写下发生的任何事件,比如生日或聚会。他们从录音设备记录事件并保存在任何地方,这样很容易丢失。这个小软件就是为了帮助组织事件和组织视频。
背景
Windows Presentation Foundation (WPF) 是微软的一项新技术,它允许开发者更轻松地操作用户界面。开发者可以像设计师一样,以更具互动性的方式构建用户界面。
直到今天,WPF还没有处理像MediaElement
这样的流数据的属性或方法。MediaElement
使用Source
属性来抓取媒体或流媒体;它无法从byte[]
或Stream
中抓取。这很不方便,因为它可能来自许多不同的来源。
要求
- Windows XP with SP2 或 Windows Vista
- Microsoft Visual C# 2008 and Visual Web Developer 2008 Express Edition
- SQL Express 2005 with SP2
- XCeed Component: Datagrid for WPF。您可以下载XCeed Component: Datagrid for WPF。您可以 在此处免费下载。
- 一些扩展名为*.wmv的视频,用于试用这些示例。我已尝试过最长100 MB的视频。
准备一切
需要下载三个文件
- 个人日记WPF源代码
- ASP.NET源代码
- 一个SQL文件
个人日记WPF源代码是主应用程序。可以使用Visual C# 2008 Express Edition打开。
ASP.NET作为视频流的源代码是ASP.NET,它只能产生媒体流。目前我只生成Windows视频(*.wmv)和MPEG视频(*.mpg)。可以使用Visual Web Developer 2008 Express Edition打开。
SQL文件可以使用SQL Server Management Studio Express打开。我的数据库名称是persdiary。您可以更改它,并且不要忘记更改app.config中的连接字符串。运行SQL,以便表已准备好使用。
打开两个项目 - WPF项目和网站。由于并非所有Windows安装都配备了IIS,我使用产品自带的内置Web服务器。先运行网站,确保Web服务器正常运行。

网站运行并显示结果后,将浏览器中的URL地址复制到appSetting的值中,并找到hostpath的键。

Webstream网站项目中的代码
由于WPF的Source
属性的MediaElement控件无法接收byte[]
或Stream
,我们可以通过ASP.NET作为视频流来处理它。首先,ASP.NET将从数据库加载数据。视频本身保存在mstvideo
表中的varbinary
数据类型中。
在Page Load方法中
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>");
}
此代码将检测查询字符串并调用方法从数据库查询数据并将结果写入此页面。只有两个查询字符串:vid
和loadfull
。查询字符串vid
是视频ID,用于按视频ID从表中加载视频数据。查询字符串loadfull
是一个标志,表示不完全加载视频;在这种情况下,我将加载从表中接收到的总字节的1/8。
在ReadVideo方法中
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);
}
}
}
上面的代码从表中查询视频。SELECT语句DATALENGTH(vdcontent)
测量存储在vdcontent
中的数据的长度。我使用command.ExecuteReader()
来执行查询。结果是视频的长度、视频本身和视频格式。视频格式对于选择ASP.NET的MIME类型很有用。目前,我只支持WMV和MPG。同样,如果loadfull是false
,那么我将缓冲区大小设置得更小;在这种情况下,我只将大小除以8。
在WriteVideoToPage方法中
Response.BufferOutput = true;
//Response.BinaryWrite(movieContents);
BinaryWriter binWriter = new BinaryWriter(Response.OutputStream);
binWriter.Write(videoData);
binWriter.Flush();
ASP.NET将使用System.IO.BinaryWriter
写入内容来渲染视频。ASPX文件本身是空的。
RegistryKey regKey = Registry.CurrentUser;
regKey = regKey.OpenSubKey("Software");
regKey = regKey.OpenSubKey("Nicko");
if (regKey != null)
{
result = regKey.GetValue("ConnectionString").ToString();
}
我使用注册表键来获取连接字符串。因此,您可以在WPF的app.config中一次性更改连接字符串。
个人日记WPF项目中的代码
这是用WPF编写的主应用程序。该应用程序可以添加日记事件和视频。您可以单击下方列表中的视频,将其放大到中心。不要忘记添加对XCeed datagrid for WPF的引用,以使其正常工作。
首先,我在MainWindow.xaml中添加了两个新的命名空间——XCeed datagrid和本地命名空间。
xmlns:local="clr-namespace:Diary.WPF"
xmlns:xceed="clr-namespace:Xceed.Wpf.DataGrid;assembly=Xceed.Wpf.DataGrid"
我将Xceed datagrid添加到grid布局中。
<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>
当鼠标点击并按下键盘上下键时,grid将更改选定的项。这将触发刷新视频内容。
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();
}
}
我添加了具有添加事件和添加视频功能的按钮。
...
<Button Height="23" x:Name="btnAddEvent" Width="30"
Click="btnAddEvent_Click">Add</Button>
...
<Button x:Name="btnAddNewVideo" Click="btnAddNewVideo_Click">Add new</Button>
按钮将触发一个事件来打开对话框窗口。
private void btnAddEvent_Click(object sender, RoutedEventArgs e)
{
...
bool? result = dlgInsert.ShowDialog();
...
}
}
该方法将打开新的事件对话框窗口。

private void btnAddNewVideo_Click(object sender, RoutedEventArgs e)
{
...
bool? result = dlgVideo.ShowDialog();
...
}
该方法将打开新的视频对话框窗口。

当我点击下方的一个视频时,视频将在中间放大并全屏播放。当我点击那个大视频时,它将回到下方列表。
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;
...
}
}
我创建了一个XAML作为对话窗口,用于向表中添加事件。它是InsertEventDialog.xaml。我使用grid布局来创建类似于表格的布局,其中包含textblocks和textboxes作为输入控件。我使用Windows Form的DateTimePicker
来选择事件日期。我创建了InsertNewVideo.xaml来为选定的日记事件添加视频。
...
...
<TextBlock .../>
<TextBox .../>
...
<WindowsFormsHost Name="wpfHost">
<windowsform:DateTimePicker ...</WindowsFormsHost>
关注点
我学会了如何使用ObservableCollection <of T>
将数据绑定到WPF控件,而不仅仅是Dataset
或Datatable
。我只需要在控件属性中写属性名作为绑定源,就会自动绑定。
ASP.NET可以用作流媒体视频源,因此我可以使用它作为MediaElement
的动态源。
历史
- 版本1.0:2007年12月28日 - 应用程序发布