这两天不大舒服,请了两天假,终于有一点空余时间了,准备把以前写DownloaderPlus时用wpf开发时遇到的一些问题的解决方案作为blog贴出来。主要有如下几篇:
- WPF下通过附加属性实现单实例启动
- WPF中的单实例对象数据绑定
- WPF中图像模糊的处理方法
- 自定义WPF中Slider的Autotooltip模板
- WPF中菜单样式的绑定
前三篇已经完成了,后两篇准备在明天写完。文章内容大体都比较肤浅,也没有深入的写,欢迎指正。
posted @
2009-07-02 17:49 天方 阅读(6) |
评论 (0) |
编辑
本文没有什么技术含量,单实例启动基本上也是一个做烂了的功能,一搜网上一大把,这里主要是顺便练习一下wpf的附加属性而已。
代码如下:
class
SingletonWindow
{
//注册附加属性
public
static
readonly
DependencyProperty IsEnabledProperty =
DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(SingletonWindow), new
FrameworkPropertyMetadata(OnIsEnabledChanged));
public
static
void SetIsEnabled(DependencyObject element, Boolean value)
{
element.SetValue(IsEnabledProperty, value);
}
public
static
Boolean GetIsEnabled(DependencyObject element)
{
return (Boolean)element.GetValue(IsEnabledProperty);
}
//根据附加属性的返回值使能单实例模式
public
static
void OnIsEnabledChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
if ((bool)args.NewValue != true)
{
return;
}
Process();
return;
}
public
static
void Process() //如果不适用附加属性也可以直接使用此函数
{
//判断单实例的方式有很多,如mutex,process,文件锁等,这里用的是process方式
var process = GetRunningInstance();
if (process != null)
{
HandleRunningInstance(process);
Environment.Exit(0);
}
}
const
int WS_SHOWNORMAL = 1;
[System.Runtime.InteropServices.DllImport("User32.dll")]
static
extern
bool ShowWindowAsync(IntPtr hWnd, int cmdShow);
[System.Runtime.InteropServices.DllImport("User32.dll")]
static
extern
bool SetForegroundWindow(IntPtr hWnd);
[System.Runtime.InteropServices.DllImport("user32.dll")]
static
extern
bool FlashWindow(IntPtr hWnd,bool bInvert);
static System.Diagnostics.Process GetRunningInstance()
{
var current = System.Diagnostics.Process.GetCurrentProcess();
var processes = System.Diagnostics.Process.GetProcessesByName(current.ProcessName);
foreach (var process in processes)
{
if (process.Id != current.Id)
if (System.Reflection.Assembly.GetExecutingAssembly().Location.Replace("/", "\\") == current.MainModule.FileName)
return process;
}
return
null;
}
static
void HandleRunningInstance(System.Diagnostics.Process instance)
{
if (instance.MainWindowHandle!=IntPtr.Zero)
{
for (int i = 0; i < 2; i++)
{
FlashWindow(instance.MainWindowHandle, 500);
}
SetForegroundWindow(instance.MainWindowHandle);
ShowWindowAsync(instance.MainWindowHandle, WS_SHOWNORMAL);
}
else
{
//else 处理有点麻烦,简化如下
MessageBox.Show("已经有一个实例在运行,无法启动第二个实例");
}
}
static
void FlashWindow(IntPtr hanlde, int interval)
{
FlashWindow(hanlde, true);
System.Threading.Thread.Sleep(interval);
FlashWindow(hanlde, false);
System.Threading.Thread.Sleep(interval);
}
}
代码其实很简单,前半部分是注册依赖属性,然后根据依赖属性判断是否启用单实例模式;后半部分就是一个传统的单实例模式的功能了。也就不介绍了。
使用这段代码也很简单:
Xaml方式:在主窗口的xaml文件中加入附加属性即可
<Window
xmlns:src="clr-namespace:WpfApplication1"
src:SingletonWindow.IsEnabled="true">
- 传统方式:直接使用代码中后半部分,和winform下没什么区别。在主窗口的构造函数里面加入这句话。
SingletonWindow.Process();
posted @
2009-07-02 17:28 天方 阅读(6) |
评论 (0) |
编辑
WPF的数据绑定非常强大,可以省去我们在winform下的不少难写代码。本文主要探讨一下WPF中单实例对象数据绑定。
WPF的单实例对象数据绑定的需求主要起源于我写的一个下载工具,我写了一个自动关机的功能,然后想把这个自动关机的状态同时双向绑定到工具栏和菜单中,而工具栏和菜单是分别在两个不同的UserControl中写的,它们之间不共享数据。这样把配置数据绑定到多个不同的控件的需求还有不少。
首先我想到的方式是将配置数据写成静态属性,然后通x:Static过标记直接绑定静态属性到控件上,但是很快就发现了这样的局限性:不能实现双向数据绑定。原因很简单:双向数据绑定的时候,是基于实现了INotifyPropertyChanged的对象通知的。静态属性的变更自然无法通知出去。
既然静态属性的形式不行,就想到了将数据绑定到单实例对象中,便用SINGLETON模式实现了一个,实例代码如下:
<StackPanel>
<CheckBox Content="AutoPowerOff1" IsChecked="{Binding Path=AutoPowerOff, Mode=Default, Source={x:Static src:ConfigData.Instance}}"/>
<CheckBox Content="AutoPowerOff2" IsChecked="{Binding Path=AutoPowerOff, Mode=Default, Source={x:Static src:ConfigData.Instance}}"/>
</StackPanel>
//SINGLETON模式
class
ConfigData : System.ComponentModel.INotifyPropertyChanged
{
public
static
ConfigData Instance = new
ConfigData();
private ConfigData() { }
bool autoPowerOff = false;
public
bool AutoPowerOff
{
get { return autoPowerOff; }
set { autoPowerOff = value; NotifyPropertyChange("AutoPowerOff"); }
}
#region INotifyPropertyChanged 成员
public
event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
void NotifyPropertyChange(string proper)
{
if (PropertyChanged == null)
return;
PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(proper));
}
#endregion
}
这个代码毕竟简单,将两个checkbox的check状态分别绑定到ConfigData的AutoPowerOff属性中,通过静态属性Source={x:Static src:ConfigData.Instance}指定到同一个数据源,所以一个checkbox的状态变更会同步更新到另一个checkbox。实现了我们的需求。
SINGLETON模式非常简单有效,然而有一点让我们不大爽的地方,那就是不支持blend自动绑定,需要手工敲不少代码。Blend的数据绑定中,只能自动生成对象,然后将数据绑定到生产的对象中,类似这种形式。
<StackPanel>
<StackPanel.Resources>
<src:ConfigData x:Key="ConfigDataDataSource" d:IsDataSource="True"/>
<src:ConfigData x:Key="ConfigDataDataSource1" d:IsDataSource="True"/>
</StackPanel.Resources>
<CheckBox Content="AutoPowerOff1" IsChecked="{Binding Path=AutoPowerOff, Mode=Default, Source={StaticResource ConfigDataDataSource}}"/>
<CheckBox Content="AutoPowerOff2" IsChecked="{Binding Path=AutoPowerOff, Mode=Default, Source={StaticResource ConfigDataDataSource1}}"/>
</StackPanel
要让我们的单实例对象能在blend生成的绑定代码下工作,可以通过使用MonoState模式来实现。代码如下:
class
ConfigData : System.ComponentModel.INotifyPropertyChanged
{
static
bool autoPowerOff = false;
public
bool AutoPowerOff
{
//把这个属性设置为static也可以双向绑定,但由于blend不会列出对象的静态属性,需要手工输入属性名
get { return autoPowerOff; }
set { autoPowerOff = value; NotifyPropertyChange("AutoPowerOff"); }
}
#region INotifyPropertyChanged 成员
static
List<ConfigData> instanceList = new
List<ConfigData>();
System.ComponentModel.PropertyChangedEventHandler propertyChanged;
public
event System.ComponentModel.PropertyChangedEventHandler PropertyChanged
{
add { propertyChanged += value; instanceList.Add(this); }
remove { propertyChanged -= value; instanceList.Remove(this); }
}
static
void NotifyPropertyChange(string proper)
{
foreach (var obj in instanceList)
{
if (obj.propertyChanged == null)
continue;
obj.propertyChanged(obj, new System.ComponentModel.PropertyChangedEventArgs(proper));
}
}
#endregion
}
MonoState模式比较巧妙的将blend创建的多个对象的数据共享了起来,对于blend来说,创建了多个对象,而用起来却和一个实例对象一样。更具有通用性。
在WPF的数据绑定中使用MonoState模式关键是PropertyChangedEventHandler事件的通知,由于WPF是基于对象通知的,而所有的对象是共享的数据,因此需要把该事件通知到所有实例。这里我通过采用保存实例列表来实现所有通知的,比较简单,没有考虑效率问题(这种对象一共也创建不了几个),但应该有更高效的方式来实现同样的功能。
本文的代码中还存在一个问题,那就是在代码中对AutoPowerOff属性的访问问题,在不创建对象的前提下,一种方式是把它设置为static类型,这样就可以在代码中通过ConfigData.AutoPowerOff直接的访问,但这样blend不能直接列出该属性,需要手工输入一下。另外一种方式是综合SINGLETON模式创建一个静态的实例,在代码中通过ConfigData. Instance.AutoPowerOff访问。
posted @
2009-07-02 15:47 天方 阅读(12) |
评论 (0) |
编辑
在wpf中使用Image时,时常会出现图像模糊的情况,有两种方法可以解决这一问题:
设置Image的SnapsToDevicePixels属性为true。
<Image Source="images/OrderedList.png" Width="20" Height="20"
SnapsToDevicePixels="True" />
网上所能查到的方法大多是这种方法,然而有的时候,这种方法也不起作用,这时候我们可以试试下面这种方法。
- 设置附加属性RenderOptions.BitmapScalingMode="NearestNeighbor"
<Image Source="images/OrderedList.png" Width="20" Height="20"
RenderOptions.BitmapScalingMode="NearestNeighbor" />
这个附加属性也可以放在window中,这样就对整个窗体的所有image对象都起作用了。
<Window
RenderOptions.BitmapScalingMode="NearestNeighbor" />
posted @
2009-07-02 15:40 天方 阅读(10) |
评论 (0) |
编辑
在.net的Task Parallel Library中有一个很方便的功能Parallel.ForEach,可以实现多任务的并发执行,另外还带着栅栏功能,非常好用。但是这一功能必须需要clr4.0支持(CTP版的不大好用),对于低版本的.net要实现类似功能只有自己写一个了。
codeproject上面文章Poor Man's Parallel.ForEach Iterator中就有一种简单而有效的实现。但作者附录的代码有如下几个问题:
- 无法对每个并发任务分别制定不同的线程数
- 算法本身有点问题,任务执行完会报错
- 不能快速响应异常
针对以上几点,我对那段代码做了一点小改进,代码如下:
static class Parallel
{
public static void ParallelForEach<T>(this IEnumerable<T> enumerable, Action<T> action, int NumberOfParallelTasks)
{
var syncRoot = new object();
if (enumerable == null) return;
var enumerator = enumerable.GetEnumerator();
InvokeAsync<T> del = InvokeAction;
var seedItemArray = new T[NumberOfParallelTasks];
var resultList = new List<IAsyncResult>(NumberOfParallelTasks);
var waitHanles = new List<WaitHandle>(NumberOfParallelTasks);
for (int i = 0; i < NumberOfParallelTasks; i++)
{
lock (syncRoot)
{
if (!enumerator.MoveNext())
break;
seedItemArray[i] = enumerator.Current;
}
var iAsyncResult = del.BeginInvoke(enumerator, action, seedItemArray[i], syncRoot, i, null, null);
resultList.Add(iAsyncResult);
waitHanles.Add(iAsyncResult.AsyncWaitHandle);
}
var taskCount = waitHanles.Count;
for (int i = 0; i < taskCount; i++)
{
var index = WaitHandle.WaitAny(waitHanles.ToArray());
del.EndInvoke(resultList[index]);
resultList[index].AsyncWaitHandle.Close();
waitHanles.RemoveAt(index);
resultList.RemoveAt(index);
}
}
delegate void InvokeAsync<T>(IEnumerator<T> enumerator,
Action<T> achtion, T item, object syncRoot, int i);
static void InvokeAction<T>(IEnumerator<T> enumerator, Action<T> action,
T item, object syncRoot, int i)
{
//if (String.IsNullOrEmpty(Thread.CurrentThread.Name))
// Thread.CurrentThread.Name =
//String.Format("Parallel.ForEach Worker Thread No:{0}", i);
bool moveNext = true;
while (moveNext)
{
try
{
action.Invoke(item);
}
catch (Exception)
{
throw;
}
lock (syncRoot)
{
moveNext = enumerator.MoveNext();
if (moveNext)
item = enumerator.Current;
}
}
}
}
整个算法非常简洁,这里就不多介绍了。如果有错误欢迎指正。
posted @
2009-06-28 11:41 天方 阅读(9) |
评论 (0) |
编辑
本来打算五一节后就写完的,可是由于工作较忙而耽搁了。其实基本功能在五一前就大致差不多了,我也一直在用,可是有许多细节不完善,而我也一直懒得弄,故迟迟没有发布,看blog的留言中还有一些朋友在等待这款软件,实在感到有些抱歉。虽然这个软件目前还没有到正式发布的程度,但基本上算好用的,故每周放个临时版本出来,也来个所谓的"迭代"开发吧。
下载地址如下:点击下载
使用方式:
- 通过新建命令输入flv视频所在的网页和视频名称即可下载。
- 也可以将视频的链接直接拖到主窗口可以更快的实现下载。
功能说明:
支持的功能:
- 支持大多数网站的Flv视频下载。(由于视频网站的解析算法多变,测试的不多,如果有不支持的地址,请留言。另外,土豆的黑豆暂不支持)
- 支持土豆的豆单和优酷专辑下载。(其它网站我去的较少,就没有做,需要的朋友可以联系我)
- 支持google的mp3专辑下载
- 支持某些漫画网站的漫画批量下载。(该功能目前还不大稳定,就不说支持哪些网站了)
- 另外,对比以前的FlvDownloader,加入了一些方便的控制和配置功能,估计基本上看着就会用啦
下载任务控制说明:
目前大多数网站都有并发连接限制,超过了此限制会导致无法成功下载。目前本程序的限制条件如下:
- 全局最多4个任务同时下载
- 每个任务最多4个线程同时下载。
- 优酷视频最多只能1个任务,每个任务最多2个线程同时下载
目前不可用的功能:
- 临时版本中,不可使用window7的超级任务栏的进度实时显示功能(多平台发布比较麻烦,正式版本中有此功能)。
- 临时版本中,不可使用视频探测器和视频预览功能(目前尚不完善,正式版本中有此功能)。
- 漫画解析器和视频格式转换功能目前不可用(正式版本中估计也不会加),预计在下个版本中提供。
- 视频下载的暂停和速度控制功能目前不可用(目前尚不完善,正式版本可能有此功能)
说明:
- 我一般也只去几个固定的网站,故没有充分的测试,加上各视频网站的加密算法会时常更新,在使用中如果有什么问题欢迎留言,我会尽量解决。
- 本程序是用的wpf写的,xp用户需要安装.net 3.0 才能使用
- 这个软件我基本上每周会有一点更新的,如果大家有什么意见和建议,欢迎在此留言。
- Flv下载、预览及探测的算法我前面的文章里都有提及,本版本和FlvDownloader在算法上没有改进,只是做了一下功能集成和界面的改善。如果要探讨相关的技术,欢迎查看我以前的文章,请不要在此留言。目前也没有打算开放源代码,索取源码的帖子也请不要留言。
posted @
2009-06-27 22:19 天方 阅读(24) |
评论 (3) |
编辑
昨天看到了Office的下载地址,就安装了一个试了试,总体感觉和Office2007变化不大,外观配色上看起来非常舒服,Aero效果用得更多了,具体功能改进还没有细研究(其实一直感觉到了2003后的更新大都只是在界面和适用性方面调整,功能没有大变化),不过打开速度飞快,那个启动画面都没看清就启动了(所以很多人说启动画面的那个动画只能第一次才能看到),这一点让人十分感动,看来新版的Office在低配的机器上也会有不俗的表现。
Word2010的界面截图
posted @
2009-05-17 06:05 天方 阅读(109) |
评论 (0) |
编辑
windows7中带的ie8一直存在一个问题:无法将链接拖放到迅雷等第三方工具(ie7中会提示是否允许的,在win7的ie8中却直接给禁止了,没有提示对话框)。
这个问题很明显是保护模式引起的,由于我以前一直换版本比较频繁,做好了一个月装一次的准备,所以一直也就是将保护模式给直接关了。现在装了windows7rc版后,准备好好用一段时间,所以便想解决这个问题了。
找了找资料,微软的文章了解并运行Internet Explorer 保护模式很好的说明了如何解决IE的拖放问题。
解决方法很简单,这里就不多介绍了,只要在注册表HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Low Rights\DragDrop中添加程序的路径即可。如下是一个例子。
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Internet Explorer\Low Rights\DragDrop\{aaa04479-982b-479f-95eb-c6972fb8c767}]
"Policy"=dword:00000003
"AppPath"=hex(2):43,00,3a,00,5c,00,50,00,72,00,6f,00,67,00,72,00,61,00,6d,00,\
20,00,46,00,69,00,6c,00,65,00,73,00,5c,00,61,00,64,00,6f,00,62,00,65,00,5c,\
00,61,00,63,00,72,00,6f,00,62,00,61,00,74,00,20,00,36,00,2e,00,30,00,5c,00,\
72,00,65,00,61,00,64,00,65,00,72,00,00,00
"AppName"="acrord32.exe"
把这个注册表导入后,把GUID、AppPath及AppName改一下就可以了。
其实这个只是个临时的解决方案,估计在正式版的ie8中应该没有这个问题。
更新:2009-07-03
今天装了个windows7-7264,发现还是存在这一问题,就自己写了个程序添加删除拖放支持,运行时需要管理员权限,需要重启IE才能生效。点击下载
posted @
2009-05-07 22:55 天方 阅读(182) |
评论 (0) |
编辑
最近打算把基础知识总结一下,主要包括以下几个部分
- C/C++、数据结构、算法等基础知识
- 数据库,UML等工具
- 设计模式,软件工程,操作系统等理论点的知识。
另外,还需要把英语加强一下。
要实现这些目标,就需要专心点了,切实把计划落实下来。那些写着玩的小程序暂且不写,动画片和电视剧等也只看一直连载的系列了,尽量减少分心的东西。
posted @
2009-04-13 02:18 天方 阅读(37) |
评论 (0) |
编辑