新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论高级C/C++编程、代码重构(Refactoring)、极限编程(XP)、泛型编程等话题
    [返回] 计算机科学论坛计算机技术与应用『 C/C++编程思想 』 → VC# 2005 Screen Saver Starter kit的主窗体分析 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 3068 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: VC# 2005 Screen Saver Starter kit的主窗体分析 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客楼主
    发贴心情 VC# 2005 Screen Saver Starter kit的主窗体分析

    首先我们来看一下RssItem类实现一个IItem接口,我们发现这个接口是存放在UI文件夹下的,这个接口本身很简单:

        public interface IItem

        {

            string Description { get; }

            string Title { get; }

    }

    它的具体实现是由UI文件夹下的另两个类来实现的。

    我们首先考察ItemListView类,这个类的作用就是封装了Item List的显示方式:每一个的description在一个列表中显示,某一个Item处于选中状态。

    我们查看代码可以发现它是一个泛型类

    public class ItemListView<T> : IDisposable where T : IItem

    类本身需要实现IDisposable接口,以释放一些资源,还定义了一个泛型约束,以使我们的T的类型都要实现IItem接口(关于泛型约束请参考我的文章)

    类具体的内容都是GDI+的编程实现,大家可以自己查看

    另一个类ItemDescriptionView.cs的作用是封装了每一个Item的Description的显示方式。

    接下来我们看一下主窗体:ScreenSaverForm

    在类构造器里,初始化组件以后,我们看到第一个方法是SetupScreenSaver(),这个方法的作用是把主窗体设为一个全屏的屏保程序

    我们查看一下代码:

                // 使用双倍缓冲增加绘制的表现,其实这里使用的OptimizedDoubleBuffer我查看了VS2003

                //的文档,并没有这个枚举值,只有DoubleBuffer,看样子是2.0新增的

                this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);

                //获取鼠标

                this.Capture = true;

                // 把应用程序设为全屏,然后隐藏鼠标

                Cursor.Hide();

                Bounds = Screen.PrimaryScreen.Bounds;

                WindowState = FormWindowState.Maximized;

                ShowInTaskbar = false;

                DoubleBuffered = true;

                BackgroundImageLayout = ImageLayout.Stretch;

    接下来的方法是LoadBackgroundImage()

                // 初始化图像

                backgroundImages = new List<Image>();

                currentImageIndex = 0;

                if (Directory.Exists(Properties.Settings.Default.BackgroundImagePath))

                {

                    try

                    {

                        // 尝试载入用户给出的图像

                        LoadImagesFromFolder();

                    }

                    catch

                    {

                        // 如果失败,再入默认图像

                        LoadDefaultBackgroundImages();

                    }

                }

                // 如果没有图像可载入,就载入默认的

                if (backgroundImages.Count == 0)

                {

                    LoadDefaultBackgroundImages();

                }

    其实查看Properties.Settings,我们发现BackgroundImagePath刚开始是没有值的,所以肯定会载入默认图像。

    这里又涉及到两个方法,第一个是

            private void LoadImagesFromFolder()

            {

                DirectoryInfo backgroundImageDir = new DirectoryInfo(Properties.Settings.Default.BackgroundImagePath);

                // 遍历每一个图片扩展名 (.jpg, .bmp, etc.)

                foreach (string imageExtension in imageExtensions)

                {

                    //遍历用户提供的文件夹内的每一个文件

                    foreach (FileInfo file in backgroundImageDir.GetFiles(imageExtension))

                    {

                        // 尝试载入图像

                        try

                        {

                            Image image = Image.FromFile(file.FullName);

                            backgroundImages.Add(image);

                        }

                        catch (OutOfMemoryException)

                        {

                            // 如果图像无法再入,继续下一个文件

                            continue;

                        }

                    }

                }

            }

    第二个是

            private void LoadDefaultBackgroundImages()

            {

                //如果背景图像由于任何原因不能被载入

                //使用储存在resources里的图像

                backgroundImages.Add(Properties.Resources.SSaverBackground);

                backgroundImages.Add(Properties.Resources.SSaverBackground2);

            }

    我们查看Properties.Resources可以看到

            internal static System.Drawing.Bitmap SSaverBackground {

                get {

                    object obj = ResourceManager.GetObject("SSaverBackground", resourceCulture);

                    return ((System.Drawing.Bitmap)(obj));

                }

            }

            

            internal static System.Drawing.Bitmap SSaverBackground2 {

                get {

                    object obj = ResourceManager.GetObject("SSaverBackground2", resourceCulture);

                    return ((System.Drawing.Bitmap)(obj));

                }

            }

    而这里的SSaverBackgroud是嵌入在资源文件里的图片的名称

    那么发布出去的屏保文件是否包含着两张图片呢,我们就动手做一个小实验

    首先以默认的情况下发布,我们发现生成的exe文件为120K大小,而然后我在资源文件里添加一张1.17M的bmp文件,然后让程序显示我们新添加的这张默认图片

    在Properties.Resources添加如下语句(图片文件名为_7)

            internal static System.Drawing.Bitmap SSaverBackground3

            {

                get

                {

                    object obj = ResourceManager.GetObject("_7", resourceCulture);

                    return ((System.Drawing.Bitmap)(obj));

                }

            }

    然后在ScreenSaverForm的LoadDefaultBackgroundImages()方法添加语句

    backgroundImages.Add(Properties.Resources._7);

    然后编译运行,我们就可以看到我们新添加的默认图片,然后查看生成的exe文件,大小变为1.29M

    所以我们可以得知新加入的图片已经嵌入在PE文件里面了。

    接下来的就是  LoadRssFeed();方法了

            private void LoadRssFeed()

            {

                try

                {

                    //尝试从用户的settings或者rssFeed

                    rssFeed = RssFeed.FromUri(Properties.Settings.Default.RssFeedUri);

                }

                catch

                {

                    //如果载入Rss有问题,则载入一个错误信息

                    rssFeed = RssFeed.FromText(Properties.Resources.DefaultRSSText);

                }

            }

    这里的RssFeed类在前面已经分析过了

    后面的是

                // 初始化显示ItemListView来显示RssItem里的Items列表

                // 放在屏幕的左侧    

                rssView = new ItemListView<RssItem>(rssFeed.MainChannel.Title, rssFeed.MainChannel.Items);

                InitializeRssView();

    InitializeRssView()方法里设置了我们需要的具体数值

            private void InitializeRssView()

            {

                rssView.BackColor = Color.FromArgb(120, 240, 234, 232);

                rssView.BorderColor = Color.White;

                rssView.ForeColor = Color.FromArgb(255, 40, 40, 40);

                rssView.SelectedBackColor = Color.FromArgb(200, 105, 61, 76);

                rssView.SelectedForeColor = Color.FromArgb(255, 204, 184, 163);

                rssView.TitleBackColor = Color.Empty;

                rssView.TitleForeColor = Color.FromArgb(255, 240, 234, 232);

                rssView.MaxItemsToShow = 20;

                rssView.MinItemsToShow = 15;

                rssView.Location = new Point(Width / 10, Height / 10);

                rssView.Size = new Size(Width / 2, Height / 2);

            }

    接下来的ItemDescriptionView雷同

    至此,我们的屏保就可以显示出来的,但是作为屏保,我们还缺少一些对鼠标、键盘事件的响应。

            private void ScreenSaverForm_MouseMove(object sender, MouseEventArgs e)

            {

                // 只有在这个事件第一次调用的时候才设置IsActive和MouseLocation

                if (!isActive)

                {

                    mouseLocation = MousePosition;

                    isActive = true;

                }

                else

                {

                    // 在第一次调用以后,如果鼠标发生位置移动,就关闭窗体

                    if ((Math.Abs(MousePosition.X - mouseLocation.X) > 10) ||

                        (Math.Abs(MousePosition.Y - mouseLocation.Y) > 10))

                    {

                        Close();

                    }

                }

            }

    当然还有

            private void ScreenSaverForm_MouseDown(object sender, MouseEventArgs e)

            {

                Close();

            }

    关于键盘事件,可以根据我们的需要定制

            private void ScreenSaverForm_KeyDown(object sender, KeyEventArgs e)

            {

                switch (e.KeyCode)

                {

                    case Keys.Down:

                        rssView.NextArticle();

                        break;

                    case Keys.Up:

                        rssView.PreviousArticle();

                        break;

                    default:

                        Close();

                        break;

                }


       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/12/13 14:32:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 C/C++编程思想 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2025/7/18 14:43:57

    本主题贴数1,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    78.125ms