随笔 - 215  文章 - 8 评论 - 357 trackbacks - 19
<2009年7月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

公告:发表时间超过两个月的随笔的源码一般都被删除了,请勿留言索取。

Click Here
free-counter-plus.com

与我联系

搜索

 

常用链接

留言簿

随笔分类

随笔档案

文章分类

收藏夹

.net学习

最新评论

阅读排行榜

评论排行榜

60天内阅读排行

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文件中执行权限为即可。具体方法如下:

  1. 在项目上选择添加新项->常规->应用程序清单文件(Add->Add new item->Application Manifest File),将app.manifest文件添加至项目中。
  2. 打开该文件,在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)编辑

Slider控件有一个我比较喜欢的属性"AutoToolTip",可以在拖动的过程中显示当前刻度,然而这个刻度却不支持模板定制,并且就连自定义格式也不行。这就大大的限制了它的使用范围。网上有篇文章Modifying the auto tooltip of a Slider(由于wordpress被和谐了,这个地址是无法访问的)解决了这个问题,可以实现自定义显示格式

代码如下: 

Code

 

使用起来也很简单。
<local:FormattedSlider
     AutoToolTipFormat="{}{0}% used"
    AutoToolTipPlacement="BottomRight" />

其实原理也不复杂,通过反射设置"_autoToolTip"变量,从而实现自定义AutoToolTip格式

private ToolTip AutoToolTip
{
    get
    {
        if (_autoToolTip == null)
        {
            FieldInfo field = typeof(Slider).GetField(
                "_autoToolTip",
                BindingFlags.NonPublic | BindingFlags.Instance);

            _autoToolTip = field.GetValue(this) as ToolTip;
        }

        return _autoToolTip;
    }
}

posted @ 2009-07-04 23:57 天方 阅读(8) | 评论 (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()    //
如果不适用附加属性也可以直接使用此函数
    {
        //
判断单实例的方式有很多,如mutexprocess,文件锁等,这里用的是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);
    }
}

代码其实很简单,前半部分是注册依赖属性,然后根据依赖属性判断是否启用单实例模式;后半部分就是一个传统的单实例模式的功能了。也就不介绍了。

使用这段代码也很简单:

  1. Xaml方式:在主窗口的xaml文件中加入附加属性即可
    <Window xmlns:src="clr-namespace:WpfApplication1" src:SingletonWindow.IsEnabled="true">

     

  2. 传统方式:直接使用代码中后半部分,和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时,时常会出现图像模糊的情况,有两种方法可以解决这一问题:

  1. 设置Image的SnapsToDevicePixels属性为true。
    <Image Source="images/OrderedList.png" Width="20" Height="20" SnapsToDevicePixels="True" />

    网上所能查到的方法大多是这种方法,然而有的时候,这种方法也不起作用,这时候我们可以试试下面这种方法。

  2. 设置附加属性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中就有一种简单而有效的实现。但作者附录的代码有如下几个问题:

  1. 无法对每个并发任务分别制定不同的线程数
  2. 算法本身有点问题,任务执行完会报错
  3. 不能快速响应异常

针对以上几点,我对那段代码做了一点小改进,代码如下:

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的留言中还有一些朋友在等待这款软件,实在感到有些抱歉。虽然这个软件目前还没有到正式发布的程度,但基本上算好用的,故每周放个临时版本出来,也来个所谓的"迭代"开发吧。

下载地址如下:点击下载

 使用方式:

  1. 通过新建命令输入flv视频所在的网页和视频名称即可下载。
  2. 也可以将视频的链接直接拖到主窗口可以更快的实现下载。

功能说明:

支持的功能:

  1. 支持大多数网站的Flv视频下载。(由于视频网站的解析算法多变,测试的不多,如果有不支持的地址,请留言。另外,土豆的黑豆暂不支持)
  2. 支持土豆的豆单和优酷专辑下载。(其它网站我去的较少,就没有做,需要的朋友可以联系我)
  3. 支持google的mp3专辑下载
  4. 支持某些漫画网站的漫画批量下载。(该功能目前还不大稳定,就不说支持哪些网站了)
  5. 另外,对比以前的FlvDownloader,加入了一些方便的控制和配置功能,估计基本上看着就会用啦

下载任务控制说明:

目前大多数网站都有并发连接限制,超过了此限制会导致无法成功下载。目前本程序的限制条件如下:

  1. 全局最多4个任务同时下载
  2. 每个任务最多4个线程同时下载。
  3. 优酷视频最多只能1个任务,每个任务最多2个线程同时下载

目前不可用的功能:

  1. 临时版本中,不可使用window7的超级任务栏的进度实时显示功能(多平台发布比较麻烦,正式版本中有此功能)。
  2. 临时版本中,不可使用视频探测器和视频预览功能(目前尚不完善,正式版本中有此功能)。
  3. 漫画解析器和视频格式转换功能目前不可用(正式版本中估计也不会加),预计在下个版本中提供。
  4. 视频下载的暂停和速度控制功能目前不可用(目前尚不完善,正式版本可能有此功能)

说明:

  1. 我一般也只去几个固定的网站,故没有充分的测试,加上各视频网站的加密算法会时常更新,在使用中如果有什么问题欢迎留言,我会尽量解决。
  2. 本程序是用的wpf写的,xp用户需要安装.net 3.0 才能使用
  3. 这个软件我基本上每周会有一点更新的,如果大家有什么意见和建议,欢迎在此留言。
  4. Flv下载、预览及探测的算法我前面的文章里都有提及,本版本和FlvDownloader在算法上没有改进,只是做了一下功能集成和界面的改善。如果要探讨相关的技术,欢迎查看我以前的文章,请不要在此留言。目前也没有打算开放源代码,索取源码的帖子也请不要留言。
posted @ 2009-06-27 22:19 天方 阅读(28) | 评论 (3)编辑

Vist下的Aero的玻璃效果非常漂亮,但要想把这个效果应用到我们的.net winform程序中却不是那么容易,开启玻璃效果之后字体和图片等都变得非常难以辨认,各种控件基本上都无法正常显示,要解决这些问题需要花费很大的精力。

今天无意中在wpf下也使用了一下玻璃效果,发现wpf的控件渲染机制可以非常完美的支持aero,开启了玻璃效果后对各控件没有任何影响,并且在开启玻璃效果后,各控件的特效(模糊,投影等)可以更好的发挥出效果,感兴趣的不妨一试

在wpf中启用玻璃效果可以参看这篇文档:http://www.codeproject.com/KB/WPF/WPFGlass.aspx

posted @ 2009-05-25 00:16 天方 阅读(69) | 评论 (0)编辑

昨天看到了Office的下载地址,就安装了一个试了试,总体感觉和Office2007变化不大,外观配色上看起来非常舒服,Aero效果用得更多了,具体功能改进还没有细研究(其实一直感觉到了2003后的更新大都只是在界面和适用性方面调整,功能没有大变化),不过打开速度飞快,那个启动画面都没看清就启动了(所以很多人说启动画面的那个动画只能第一次才能看到),这一点让人十分感动,看来新版的Office在低配的机器上也会有不俗的表现。

Word2010的界面截图

posted @ 2009-05-17 06:05 天方 阅读(109) | 评论 (0)编辑