WPF中通过可以很容易通过数据绑定生成菜单,例如:
<Window.Resources>
<local:Source x:Key="src"/>
</Window.Resources>
<StackPanel>
<Menu>
<MenuItem Header="Animals" ItemsSource="{Binding Source={StaticResource src}}" />
</Menu>
</StackPanel>
public class Source : ObservableCollection<object>
{
public Source()
{
//Spiders
Add("Golden Silk Spider");
Add("Black Widow Spider");
//BigCats
Add("Jaguar");
Add("African Wildcat");
Add("Cheetah");
//Amphibians
Add("California Newt");
Add("Tomato Frog");
Add("Green Tree Frog");
}
}
这段代码生成了如下所示的菜单
这种方式简单易用,但有个问题,缺乏灵活性:比如说如果我们要根据不同的类别分组,通过seperator隔离,生成如下图所示的菜单,这时该如何做呢?
我试过几种方法,如设置ItemStyleSelecter,DataTemplate等,效果都不是很好,最后找到一种很简单的方法:在数据源中直接插入Seperator即可。
public class Source : ObservableCollection<object>
{
public Source()
{
Add("Golden Silk Spider");
Add("Black Widow Spider");
Add(new Separator());
Add("Jaguar");
Add("African Wildcat");
Add("Cheetah");
Add(new Separator());
Add("California Newt");
Add("Tomato Frog");
Add("Green Tree Frog");
}
}
这种方式简单易用,并且能保持和系统主题样式一致,是目前我知道的最好的方法了。ToobBar的Seperator也可以用这种方式生成。
posted @
2009-07-06 00:17 天方 阅读(2) |
评论 (0) |
编辑
这几天写了几个修改注册表的程序,因为需要管理员权限,所以就搜了一下如何构建启动时申请管理员权限的程序(UAC支持,也就是程序左下角多了个小盾牌)。
其实方法很简单,就是修改manifest文件中执行权限为即可。具体方法如下:
- 在项目上选择添加新项->常规->应用程序清单文件(Add->Add new item->Application Manifest File),将app.manifest文件添加至项目中。
- 打开该文件,在requestedExecutionLevel节中, 把level值改为requireAdministrator,重新编译即可。
<?xml
version="1.0"
encoding="utf-8"?>
<asmv1:assembly
manifestVersion="1.0"
xmlns="urn:schemas-microsoft-com:asm.v1"
xmlns:asmv1="urn:schemas-microsoft-com:asm.v1"
xmlns:asmv2="urn:schemas-microsoft-com:asm.v2"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<assemblyIdentity
version="1.0.0.0"
name="MyApplication.app" />
<trustInfo
xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges
xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel
level="requireAdministrator"
uiAccess="false" />
</requestedPrivileges>
<applicationRequestMinimum>
<defaultAssemblyRequest
permissionSetReference="Custom" />
<PermissionSet
class="System.Security.PermissionSet"
version="1"
Unrestricted="true"
ID="Custom"
SameSite="site" />
</applicationRequestMinimum>
</security>
</trustInfo>
</asmv1:assembly>
posted @
2009-07-06 00:05 天方 阅读(0) |
评论 (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 天方 阅读(7) |
评论 (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 天方 阅读(15) |
评论 (1) |
编辑
在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 天方 阅读(11) |
评论 (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 天方 阅读(28) |
评论 (3) |
编辑
昨天看到了Office的下载地址,就安装了一个试了试,总体感觉和Office2007变化不大,外观配色上看起来非常舒服,Aero效果用得更多了,具体功能改进还没有细研究(其实一直感觉到了2003后的更新大都只是在界面和适用性方面调整,功能没有大变化),不过打开速度飞快,那个启动画面都没看清就启动了(所以很多人说启动画面的那个动画只能第一次才能看到),这一点让人十分感动,看来新版的Office在低配的机器上也会有不俗的表现。
Word2010的界面截图
posted @
2009-05-17 06:05 天方 阅读(109) |
评论 (0) |
编辑