浅析Family Show 2.0的子窗体实现

Family Show的子窗口并不是通过新建模式/非模式窗口实现的,而是通过一些UserControl来实现,例如Welcome窗口、Add a family member窗口,一个窗口就是一个UserControl,如果你想在设计时看到这些窗口的最终效果,建议在Expression Blend里面打开,因为Expression Blend支持在设计时读取动态资源(Dynamic Resources),这要比VS2005的WPF Extension直观许多。

在MainWindow.xaml.cs中,我们可以找到一些以Show和Hidden开头的方法,如ShowDetailsPane、HideDetailsPane、HideFamilyDataControl、HideNewUserControl、ShowNewUserControl、ShowWelcomeScreen、HideWelcomeScreen,这些方法就是用来显示和隐藏用户控件——也就是我们所说的子窗口。以ShowWelcomeScreen和HideWelcomeScreen为例:
        /// <summary>
        
/// Show the Welcome Screen user control and hides the other controls.
        
/// </summary>

        private void ShowWelcomeScreen()
        
{
            HideDetailsPane();
            HideNewUserControl();
            DiagramControl.Visibility 
= Visibility.Hidden;

            WelcomeUserControl.Visibility 
= Visibility.Visible;
        }


        
/// <summary>
        
/// Hides the Welcome Screen.
        
/// </summary>

        private void HideWelcomeScreen()
        
{
            WelcomeUserControl.Visibility 
= Visibility.Hidden;
        }

我们发现,其中除了设置WelcomeUserControl.Visibility为Hidden以外,也对其他不应该显示的用户控件做了隐藏处理。

另外,这些用户控件是直接在Xaml中初始化的,你可以在MainWindow.xaml中找到它们:
      <!-- New User Control -->
      
<local:NewUserControl x:Name="NewUserControl" HorizontalAlignment="Center" VerticalAlignment="Center" AddButtonClick="NewUserControl_AddButtonClick" CloseButtonClick="NewUserControl_CloseButtonClick" />

      
<!-- Welcome User Control -->
      
<local:Welcome x:Name="WelcomeUserControl" HorizontalAlignment="Center" VerticalAlignment="Center" ImportButtonClick="WelcomeUserControl_ImportButtonClick" NewButtonClick="WelcomeUserControl_NewButtonClick" OpenButtonClick="WelcomeUserControl_OpenButtonClick" OpenRecentFileButtonClick="WelcomeUserControl_OpenRecentFileButtonClick" />

      
<!-- Person Info Control -->
      
<local:PersonInfo x:Name="PersonInfoControl" Opacity="0" Visibility="Hidden" CloseButtonClick="PersonInfoControl_CloseButtonClick" />

      
<!-- Family Data Control -->
      
<local:FamilyData x:Name="FamilyDataControl" Opacity="0" Visibility="Hidden" CloseButtonClick="FamilyDataControl_CloseButtonClick" />

这样的声明同时也会在MainWindow.g.cs中生成一个对应的internal变量,如internal Microsoft.FamilyShow.Welcome WelcomeUserControl;,这有点像ASP.NET的aspx中声明asp.net控件(在aspx.cs中也会生成一个对应的控件变量)。

在这段声明中,我们会看到有一些事件的定义,如AddButtonClick、CloseButtonClick等,这些事件其实就是WPF中的路由事件。所谓路由事件是指当事件触发时,触发消息会遍历可视树和逻辑树,这样就可以通知其他父控件或子控件做出适当反应,由于这种事件的最大特点是可以沿着树传递,所以称为路由事件。要定义路由事件,必须提供一个公共的事件属性,如WelcomeUserControl的OpenButtonClick就是一个典型事件属性,我们来看看它的定义:
        // Expose this event for this control's container
        public event RoutedEventHandler OpenButtonClick
        
{
            add 
{ AddHandler(OpenButtonClickEvent, value); }
            remove 
{ RemoveHandler(OpenButtonClickEvent, value); }
        }

但仅有这样一个属性是不够的,还要有一个注册动作,告诉WPF的事件管理器这是一个路由事件:
        public static readonly RoutedEvent OpenButtonClickEvent = EventManager.RegisterRoutedEvent(
            
"OpenButtonClick", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(Welcome));

这里的参数中指定路由策略为Bubble,即冒泡,具体的有关路由策略的区别,大家可以去看《WPF Unleashed》里面有详细的讲解;这里的第一个参数name被指定为OpenButtonClick,就是我们刚才定义的那个事件属性的名称,但请注意,这个参数的值可以与刚才指定的事件属性不同,只是一个key而已。到目前为止,我们并没有指定由哪个控件来触发这个路由事件,所以就有了下面的代码:

        private void OpenButton_Click(object sender, RoutedEventArgs e)
        
{
            RaiseEvent(
new RoutedEventArgs(OpenButtonClickEvent));
        }


这就是负责触发事件的代码,这里的OpenButton_Click对应于Welcome用户控件中的Open按钮的Click事件。所以大家以后写WPF程序,可以考虑参考family show的程序模型,采用User Control而非Window来开发应用程序,这样就不需要考虑窗口间的交互问题,因为从头到尾都是在一个窗口中进行处理。而对于那些Flash的开发人员来说,这种模式更是熟悉不过了,你可以把User Control理解成MovieClip对象。

下面我们来看看子窗口的构成,



Welcome用户控件



NewUserControl用户控件

WPF从发布之初就一直在强调UI的设计,Family Show的窗口就很好的体现了这一点,当然我说这句话绝对不是因为这些窗口漂亮,而是因为这样的窗口设计越来越像网页设计,我们以Welcome用户控件为例,做一些分析:



在这张图中,整个窗口被分成了三部分:Header、Content和Footer,整个窗口被放置在一个StackPanel中,其中的三部分都是由一个Border作为容器控件。Footer的实现最简单,Border控件中仅放了一个Label控件;Header的Border中放了一个Grid,其中的Welcome文本则是一个TextBlock控件,至于后面的图则是一个Rectangle控件(用动态资源WelcomeHeaderBg作为Fill的值,这个动态资源其实就是一个DrawingBrush,所以可以赋给Rectangle.Fill);Content的实现相对复杂些,其中也用了一个Grid用于布局,三个按钮New、Open、Import则放在一个StackPanel中,那条灰色的水平线是一个GridSplitter,Open Recent其实是一个Label控件,其实在OpenRecent下面还有一个StackPanel控件,用来放最近打开的文件列表的,由于文件列表是通过代码生成的,所以这里看不到有任何数据。(如果你在Expression Blend中看这个用户控件会一目了然,在Object and Timeline窗口中有一个树型视图,可以方便地了解布局控件的包含关系。)

从这个窗口的布局来看,大家是不是觉得越来越有网页设计的味道的,WPF中的style其实就是从css引进过来的,虽然功能上比css强大很多,但这个概念绝对是一种借鉴,从这一点来看,WPF正在改变一种观念,未来的用户界面设计将越来越方便,我们不再需要一些第三方的skin机制来实现友好且漂亮的界面,直接用WPF就可以搞定了,当然这种理念对于过去开发WinForm的人来说可能过于时尚了,要让大量的开发人员接受这种理念还需要相当长的一段时间。

posted @ 2012-03-22 13:54  JunBird  阅读(396)  评论(0编辑  收藏  举报