精华内容
下载资源
问答
  • 最简单最通用最完美的WPF快速开发框架smart-Skin源码
  • wpf权限通用快速开发框架可以帮你减少30左右的开发量,并且界面美观、大气,框架里的功能大部分都可以根据项目需求自行修改的,下载地址会有介绍开发框架的使用说明。下载地址:https://pan.baidu.com/s/1kV8wRll
  • 请问有没有在WPF下的快速开发框架,或者是插件式的开发框架?
  • 当界面开发人员重写玩控件的模板后,一般都会把模板中写某些资源提取出来,放到公共的资源文件中去。这些被提取出来的资源一般包括:界面背景色、边框颜色、字体颜色、以及图片等。我们先看一个TabControl的自定义...

    题外话

        打开博客园,查看首页左栏的”推荐博客”,排名前五的博客分别是(此处非广告):Artech小坦克圣殿骑士腾飞(Jesse)数据之巅。再看看它们博客的最新更新时间:Artech(2014-08-07) 、小坦克(2012-02-13)、圣殿骑士(2015-06-30)、腾飞(Jesse)(2013-12-18) 、数据之巅(2016-02-19) 。虽然数据之巅在最近发表了一篇博客,但是再看看倒数第二篇,更新时间是2015-11-18。从数据的现象看感觉现在技术大牛们博客更新的越来越少,当然随着技术沉淀到一定程度,大牛们可能各自的重点不会放在只研究技术上面。但是,我想说的是,现在我们能深入学习的资源确实越来越少了。最近几年各种技术处在飞速发展中,浮躁的程序猿越来越多,大部分人都是处在熟练使用各种框架的程度,而能静下心来研究某些框架的具体实现少之甚少。往往比较大型的公司,真正需要的人却是那些不仅懂框架而且能分析框架的人。题外话就说到这里吧。。。

    显示效果图

        先简单罗列下这次系统写的几个简单控件。首先声明展示效果的目的是实现功能,请直接忽略各自的美观感受。首先是一个登录界面,效果如下:

    image

        MessageBox效果:

    image

        Window、可关闭Item的TabControl、可停靠的DockingPanel效果:

    image

    基础知识

    Style

        如果了解HTML和CSS,那么WPF的Style的学习起来也比较容易,Style的设计思想就是仿照CSS来实现的。首先我们看看WPF中一个Button实现:

    <Button FontSize=”22” Background=”Purple” Foreground=”White” Height=”50” Width=”50” RenderTransformOrigin=”.5,.5”>

        一个Button的代码和HTML元素内嵌样式非常相似:

    <Button Style="width:60px;heigth:50px;color:white;background:purple;font-size:22px" />

        我们知道THML可通过CSS把样式提取出去,WPF中的Style同样也可以。在一个Window界面,我们可以在Window.Resources中添加独立样式:

    <Style x:Key=”buttonStyle”>
        <Setter Property=”Button.FontSize” Value=”22”/>
        <Setter Property=”Button.Background” Value=”Purple”/>
         <Setter Property=”Button.Foreground” Value=”White”/>
        <Setter Property=”Button.Height” Value=”50”/>
        <Setter Property=”Button.Width” Value=”50”/>
        <Setter Property=”Button.RenderTransformOrigin” Value=”.5,.5”/>
    </Setter>
    </Style>

       在声明Style时,可标记TargetType声明样式应用到的元素类型。例如:

    <Style x:Key=”buttonStyle” TargetType=”{x:Type Button}”>

        一般的Style我们都会指定唯一的Key值,但有时候我们可以不指定Key,直接创建隐式样式。例如:

    <Style TargetType=”{x:Type Button}”>

        需要说明的是,这里没有指定Key并不是说该Style没有key值,其实在编译过程中,这总情况的Style都会默认按照TargetType的类型默认指定一个Key值。并且应用到所有类型为TargetType的元素上。

        在实现Style时,一般都会涉及到触发器,触发器包括:Property triggers、Data triggers、Event triggers、MultiTrigger。接下来分别介绍下这四个触发器。

        (1)Property Trigger:只能应用到依赖属性上,当某个依赖属性的值的满足某些条件就会触发某些改变。先看看代码:

    <Style x:Key=”buttonStyle” TargetType=”{x:Type Button}”>
    <Style.Triggers>
        <Trigger Property=”IsMouseOver” Value=”True”>
            <Setter Property=”RenderTransform”>
            <Setter.Value>
            <RotateTransform Angle=”10”/>
            </Setter.Value>
            </Setter>
            <Setter Property=”Foreground” Value=”Black”/>
        </Trigger>
    </Style.Triggers>
    </Style>

       上面的触发器实现的功能是,当鼠标移到Button上,字体颜色为Black,并且旋转10度。特别要注意的是,和HTML元素a:hover功能相似,触发器也有回置的功能,就是说当鼠标移出Button时,Button的字体颜色和旋转角度恢复到初始值。

        (2)DataTrigger:和Property Trigger非常相似,但除了应用到依赖属性外,DataTrigger可应用到任意的.Net属性上。DataTrigger的代码写法和PropertyTrigger还是有区别的,它是通过Bingding来绑定需要判断的属性。例如:

    <StackPanel Width=”200”>
        <StackPanel.Resources>
        <Style TargetType=”{x:Type TextBox}”>    
            <Style.Triggers>
                <DataTrigger
                Binding
    =”{Binding RelativeSource={RelativeSource Self}, Path=
    Text}”
                Value=”disabled”>
                <Setter Property=”IsEnabled” Value=”False”/>
                </DataTrigger>
                </Style.Triggers>
                <Setter Property=”Background”
                Value=”{Binding RelativeSource={RelativeSource Self}, Path=Text}”/>
            </Style>
        </StackPanel.Resources>
        <TextBox Margin=”3”/>
    </StackPanel>

        TextBox的Text属性是一个非依赖属性,如果Text的值为disabled,设置TextBox的IsEanbled为false。DataTrigger是通过Binding来绑定TextBox的Text属性。

        (3)EventTrigger:当元素某些事件被触发时,可执行某些动画或者元素的某些属性发生变化,这些动作可通过EventTrigger实现。看看下面的代码:

    <Button.Triggers>
        <EventTrigger RoutedEvent=”Button.Click”>
        <EventTrigger.Actions>
        <BeginStoryboard>
        <Storyboard TargetProperty=”Width”>
        <DoubleAnimation From=”50” To=”100”
        Duration=”0:0:5” AutoReverse=”True”/>
        </Storyboard>
        </BeginStoryboard>
        </EventTrigger.Actions>
        </EventTrigger>
    </Button.Triggers>

        上面的代码实现功能是,当用户点击按钮时,触发一个动画,按钮的Width在5秒钟的时间内从50增加到100个像素。

        (4)MultiTrigger:前面介绍的触发器都是当元素的某一个属性或者事件触发时操作。有些时候,我们需要判断多个条件是否同时满足,才执行某个操作。这个功能可通过MultiTrigger实现。例如,我们的Button按钮,当鼠标移到上面并且获取焦点时,设置字体颜色为Black和旋转度为10度。实现代码如下:

    <Style.Triggers>
        <MultiTrigger>
            <MultiTrigger.Conditions>
            <Condition Property=”IsMouseOver” Value=”True”/>
            <Condition Property=”IsFocused” Value=”True”/>
        </MultiTrigger.Conditions>
        <Setter Property=”RenderTransform”>
        <Setter.Value>
        <RotateTransform Angle=”10”/>
        </Setter.Value>
        </Setter>
            <Setter Property=”Foreground” Value=”Black”/>
        </MultiTrigger>
    </Style.Triggers>

    Templates

        常常在设计一个系统时,都会实现一套自己的UI界面,有些时候我们不得不重写样式比较简单的WPF Window界面。Template给开发者提供完全重写界面控件的技术,例如我们可以实现自己的窗口TitleBar,并且在上面添加菜单功能。我们在重写了控件UI的同时也保留了控件的各种功能。模板在分割可视化和逻辑上也功不可没,例如一些界面的样式交互效果我们完全可以在模板中实现,而源代码部分完全只考虑业务逻辑。我们看看下面一个常规的Button样式模板代码:

    <ControlTemplate x:Key=”buttonTemplate” TargetType=”{x:Type Button}”>
        <Grid>
            <Ellipse x:Name=”outerCircle” Width=”100” Height=”100”>
                    <Ellipse.Fill>
                    <LinearGradientBrush StartPoint=”0,0” EndPoint=”0,1”>
                    <GradientStop Offset=”0” Color=”Blue”/>
                    <GradientStop Offset=”1” Color=”Red”/>
                    </LinearGradientBrush>
                    </Ellipse.Fill>
                    </Ellipse>
                    <Ellipse Width=”80” Height=”80”>
                    <Ellipse.Fill>
                    <LinearGradientBrush StartPoint=”0,0” EndPoint=”0,1”>
                    <GradientStop Offset=”0” Color=”White”/>
                    <GradientStop Offset=”1” Color=”Transparent”/>
                    </LinearGradientBrush>
                    </Ellipse.Fill>
                </Ellipse>
        </Grid>
        <ControlTemplate.Triggers>
            <Trigger Property=”IsMouseOver” Value=”True”>
            <Setter TargetName=”outerCircle” Property=”Fill” Value=”Orange”/>
            </Trigger>
            <Trigger Property=”IsPressed” Value=”True”>
            <Setter Property=”RenderTransform”>
            <Setter.Value>
            <ScaleTransform ScaleX=”.9” ScaleY=”.9”/>
            </Setter.Value>
            </Setter>
            <Setter Property=”RenderTransformOrigin” Value=”.5,.5”/>
        </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>

        上面的代码完全重写了Button的默认样式,默认的Button是一个矩形,而现在变成了一个Ellipse椭圆形。并且使用渐变的颜色填充椭圆。我们知道默认的Button当我们鼠标移上去或者被单击时都会有样式变化。所以,我们还得给Button添加触发器,这里我们使用的是Property Trigger。当鼠标移到Button上(IsMouseOver=true),设置椭圆的填充颜色为Orange。当按钮被按下时(IsPressed=true),改变渐变颜色的起始位置。

    Theme

        一个比较完善的系统都提供了切换系统主题的功能, 我们就拿Window 10系统来说,看看下面的截图:

    image

        Window 10系统为用户提供了很多主题选择,当我们选择不同的主题,系统的菜单或者窗口的颜色都会发生变化,但是界面的结构是没有改变的。WPF的主题也完全一样。当界面开发人员重写玩控件的模板后,一般都会把模板中写某些资源提取出来,放到公共的资源文件中去。这些被提取出来的资源一般包括:界面背景色、边框颜色、字体颜色、以及图片等。我们先看一个TabControl的自定义模板:

    <Style x:Key="CustomTabControlStyle" TargetType="{x:Type control:CustomTabControl}" BasedOn="{StaticResource {x:Type TabControl}}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type control:CustomTabControl}">
                        <DockPanel LastChildFill="True">
                            <Border DockPanel.Dock="Top" Background="{DynamicResource Tab_Control_Background_Normal}"
                                BorderBrush="{DynamicResource CustomControlBorderBrush}"
                                BorderThickness="1, 1, 1, 0">
                                <TabPanel Margin="0,0,0,0" IsItemsHost="True" />
                            </Border>
                            <Border Background="White" BorderBrush="{DynamicResource CustomControlBorderBrush}" BorderThickness="1"> 
                                <ContentPresenter 
                                    ContentSource="SelectedContent" />
                            </Border>
                        </DockPanel>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Style.Triggers>
               ...
            </Style.Triggers>
        </Style>

        代码中,Border的背景色动态的使用了Tab_Control_Background_Normal资源,边框颜色使用CustomControlBorderBrush资源。而这些被引用的资源都是单独存放在一个资源文件中。这个资源文件的部分代码如下:

    <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <!-- Common Style -->
        <SolidColorBrush x:Key="CustomControlBackground" Color="#EDEDED" />
        <SolidColorBrush x:Key="CustomControlBorderBrush" Color="#b9bac1" />
        <!-- Button Style -->
        <LinearGradientBrush x:Key="ButtonBackgroundColorBusrh" EndPoint="0,1" StartPoint="0,0">
            <GradientStop Color="#fdfdfd" Offset="0"/>
            <GradientStop Color="#f5f5f5" Offset="0.5"/>
            <GradientStop Color="#e9e9e9" Offset="1"/>
        </LinearGradientBrush>
    </ResourceDictionary>

       基础部分就先简单的介绍到这里,接下来我们看看WPF的UI开发需要那些技能。

    具备技能

        1.Blend :要做WPF的界面开发,Blend是必备的技能。通过Blend可以很方便的画出UI,并且可以通过手动的方式添加动画效果。Blend的界面和Visual Studio很相似,所以上手也比较快。

        2.PhotoShop:不管是做Web前段还是WPF前段,使用PhotoShop切图以及简单的设计某些图片效果的技能也是需要掌握的。并且界面开发人员只掌握到这个程度就可以了。

        3.Snoop:是一个捕获WPF界面层次结构的工具,通过简单的操作就可以看到我们开发的WPF界面的完整层次结构,并且能够看到每层元素的属性值。当界面出现某些问题时,通过Snoop分析界面可达到事半功倍的效果。另外,在通过Visual Studio调试代码时,通过监视功能也能看到界面的层次结构。

        4.素材资源:这里提供一些资源网站Icon Find(http://findicons.com/)、Icon Finder(https://www.iconfinder.com/)。

    工程结构

        UI界面的工程一般包括:Component和Theme两个工程。自定义控件一般都是添加在Component工程里边,而控件的样式模板都会添加到Theme工程里边。

        先看下我们的Component工程,工程名称为HeaviSoft.FrameworkBase.Component。工程结构如下:

    image

        如果我们新增一个WPF的Custom Control,Visual Studio会自动在工程下面创建/Themes/Generic.xaml文件。并且自动在Generic.xaml文件中创建一个简单的控件模板。代码如下:

    <Style TargetType="{x:Type docking:CustomDocumentPanel}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type docking:CustomDocumentPanel}">
                        <Border Background="{TemplateBinding Background}"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}">
                            <ContentPresenter />
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        上面的模板展示出来就是一片空白的效果,什么都有没有。所以我们得重写Template。其实稍后我们在Theme会重写这些控件的模板,但为什么这里也需要重写?Component下写的模板是控件的默认模板。但我们没有任何主题时,就显示该默认模板。

        只要创建一个控件,Generic.xaml中就会增加一个TargetType为控件类型的模板,如果增加个10个控件,Generic.xaml中就增加了10个Style,代码量比较庞大。所以,我都会单独按照控件的名称在Theme下单独创建一个同名的资源文件。并把Generic.xaml中的对应样式移动到这个同名资源文件。而Generic.xaml中值用存放资源引用路径即可。例如:

    <ResourceDictionary
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:HeaviSoft.FrameworkBase.Component" 
        xmlns:docking="clr-namespace:HeaviSoft.FrameworkBase.Component.Docking">
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="/HeaviSoft.FrameworkBase.Component;component/Themes/CustomWindow.xaml" />
            <ResourceDictionary Source="/HeaviSoft.FrameworkBase.Component;component/Themes/CustomTextBox.xaml" />
            <ResourceDictionary Source="/HeaviSoft.FrameworkBase.Component;component/Themes/CustomMessageBox.xaml" />
            <ResourceDictionary Source="/HeaviSoft.FrameworkBase.Component;component/Themes/CustomTabControl.xaml" />
            <ResourceDictionary Source="/HeaviSoft.FrameworkBase.Component;component/Themes/CustomTabItem.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>

        Component工程结构介绍完了,接下来看Theme工程。我们的Theme工程命名为HeaviSoft.FrameworkBase.Theme。工程结构如下:

    image

        工程包含CustomControl和Themes两个文件夹,CustomControl文件夹下有多个以控件名称命名的.xaml文件,例如Button.xaml文件,它里边的代码就是真正我们自己实现的Button模板。Themes下包含一个GrayWhite文件夹,这个文件夹就是我们的一个主题,它下面存放了各个控件模板需要的资源,包括图片、颜色、字体等。例如,我们在CustomControl下添加了CustomWindow界面模板,界面右上角需要关闭、最大化、最小化按钮图片。这些图片资源存放在/Themes/GrayWhite/Images/Window下。而CustomWindow需要的颜色以及字体资源分别存放在/Themes/GrayWhite/ColorBrush.xaml和/Themes/GrayWhite/Text.xaml中。同Component相似,/Themes/Generic.xaml的内容就是引用模板、图片、颜色、字体资源文件。在系统启动时,只需要动态加载/Themes/Generic.xaml文件就可加载所有的资源文件了。

    开发步骤

        UI控件的开发步骤我们就拿实现自定义Window来举例。先看看实现的效果:

    image

        自定义窗口包含两个部分,TitleBar和Body。TitleBar从左到右分别包含了图标、标题、最小化、最大化、关闭按钮。而Body部分主要包含我们在界面添加的内容。接下来就让我们一步步的去实现上图的界面功能。

    第一步,Component添加自定义控件

        首先,选择HeaviSoft.FrameworkBase.Component工程,添加Custom Control(WPF)。添加后,CustomWindow的默认代码为:

    public class CustomWindow : Window
     {
            static CustomWindow()
            {
                DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomWindow), new FrameworkPropertyMetadata(typeof(CustomWindow)));
            }
    }

        CustomWindow类里边需要添加什么内容我们先不管。Visual Studio在添加CustomWindow文件的同时,会在/Themes/Generic.xaml中添加样式:

    <Style TargetType="{x:Type control:CustomWindow}">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type control:CustomWindow}">
                        <Border Background="{TemplateBinding Background}"
                                BorderBrush="{TemplateBinding BorderBrush}"
                                BorderThickness="{TemplateBinding BorderThickness}">
                        </Border>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

       上面的样式其实只是显示一个空的Window界面,它在什么时候会被用到?当我们没有引用任何的主题时,默认加载该样式。

    第二步,Theme中添加模板

       接下来我们切换到HeaviSoft.FrameworkBase.Theme工程,在CustomControl文件夹下添加资源文件CustomWindow.xaml。然后在/Themes/Generic.xaml中添加CustomWindow.xaml文件的引用。引用的代码如下:

    <ResourceDictionary Source="/HeaviSoft.FrameworkBase.Theme;component/CustomControl/CustomWindow.xaml" />

        接下来我们就在CustomWindow.xaml中实现自定义Window模板,这里先给出代码,然后慢慢分析:

    <Style x:Key="WindowStyle" TargetType="{x:Type control:CustomWindow}">
            <Setter Property="WindowStyle" Value="None" />
            <Setter Property="AllowsTransparency" Value="True" />
            <Setter Property="Background" Value="Transparent" />
            <Setter Property="ResizeMode" Value="NoResize" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type control:CustomWindow}">
                        <Border x:Name="MainBorder" Background="{DynamicResource CustomControlBackground}" CornerRadius="6" BorderBrush="{DynamicResource CustomControlBorderBrush}" BorderThickness="1" >
                            <Grid>
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="50"/>
                                    <RowDefinition Height="1" />
                                    <RowDefinition Height="*" />
                                </Grid.RowDefinitions>
                                <Border Grid.Row="0" Panel.ZIndex="101"  x:Name="PART_TITLEBAR" BorderThickness="0" CornerRadius="6, 6, 0, 0" Background="{DynamicResource Window_TitleBar_Background}">
                                    <DockPanel  LastChildFill="False" >
                                        <Image DockPanel.Dock="Left" Source="{TemplateBinding Icon}" />
                                        <Label VerticalAlignment="Center" 
                                               DockPanel.Dock="Left" 
                                               Content="{TemplateBinding Title}"
                                               Style="{DynamicResource TitleStyle}"/>
                                        <Button x:Name="PART_CLOSE" 
                                                Style="{DynamicResource Window_Titlebar_ButtonStyle}" 
                                                DockPanel.Dock="Right">
                                            <Image x:Name="c" Margin="3, 0" 
                                                   Style="{DynamicResource TitleButtonImageStyle}"
                                                   DockPanel.Dock="Right" Width="24" Height="24" 
                                                   Source="{DynamicResource Window_Button_Close_ImageBrush}"   />
                                        </Button>
                                        <Button x:Name="PART_MAXIMIZE_RESTORE"  
                                                Style="{DynamicResource Window_Titlebar_ButtonStyle}" 
                                                DockPanel.Dock="Right">
                                            <Image Name="MaximizeRestoreImage" Margin="3, 0" 
                                                   Style="{DynamicResource TitleButtonImageStyle}"
                                                   DockPanel.Dock="Right" Width="24" Height="24" 
                                                   Source="{DynamicResource Window_Button_Max_ImageBrush}"  />
                                        </Button>
                                        <Button x:Name="PART_MINIMIZE" 
                                                Style="{DynamicResource Window_Titlebar_ButtonStyle}" 
                                                DockPanel.Dock="Right">
                                            <Image Margin="3, 0" 
                                                   Style="{DynamicResource TitleButtonImageStyle}"
                                                   DockPanel.Dock="Right" Width="24" Height="24" 
                                                   Source="{DynamicResource Window_Button_Min_ImageBrush}" 
                                                   RenderTransformOrigin="0.5,0.5"  >
                                            </Image>
                                        </Button>
                                    </DockPanel>
                                </Border>
                                <Border BorderBrush="{DynamicResource CustomControlBorderBrush}" BorderThickness="1" Grid.Row="1" />
                                <ContentPresenter Grid.Row="2" Margin="8, 5" />
                            </Grid>
                        </Border>
                        <ControlTemplate.Triggers>
                            <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=WindowState}" Value="Maximized">
                                <Setter TargetName="MaximizeRestoreImage" Property="Source" Value="{DynamicResource Window_Button_Maximum_ImageBrush}" />
                            </DataTrigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        首先我们不会使用Window默认的TitleBar,所以我们要隐藏掉Window默认的TitleBar,通过设置:WindowStyle为None就可隐藏掉标题栏和边框。在之前看到的效果图中,我们看到Window有使用圆角效果,那么必须设置三个属性值:

    <Setter Property="AllowsTransparency" Value="True" />
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="ResizeMode" Value="NoResize" />

        AllowsTransparency为True允许窗口透明,Background为Transparent继承父窗口的背景色(也即设置背景色为透明)。ResizeMode为NoResize禁止手动拖动窗口大小,隐藏虚线边框。

        然后设置Template的Value,一般都是直接创建一个ControlTemplate节点,并指定TargetType为CustomWindow。

    <ControlTemplate TargetType="{x:Type control:CustomWindow}">
        ...
    </ControlTemplate>

        接下就该添加模板的具体实现内容,首先我们要设置有圆角的边框,那么我们可以在Root节点添加一个Border节点,设置背景色Background和边框宽度BorderThickness、边框颜色BorderBrush,并设置CornerRadius圆角度。代码如下:

    <Border x:Name="MainBorder" Background="{DynamicResource CustomControlBackground}" CornerRadius="6" BorderBrush="{DynamicResource CustomControlBorderBrush}" BorderThickness="1" >
        ...
    </Border>

        我们知道整个界面包含标题和内容,我还会在标题和内容之间添加一条分割线,所以我们可通过一个包含三行的Grid来实现,第零行为标题栏、第一行为一条Bordrer实现的分割线、第二行就直接为我们的内容,可通过ContentPresenter存放这些内容。第一行和第二行的实现如下:

    <Grid>
         <Grid.RowDefinitions>
               <RowDefinition Height="50"/>
               <RowDefinition Height="1" />
               <RowDefinition Height="*" />
               </Grid.RowDefinitions>
                ...省略标题栏代码
        <Border BorderBrush="{DynamicResource CustomControlBorderBrush}" BorderThickness="1" Grid.Row="1" />
        <ContentPresenter Grid.Row="2" Margin="8, 5" />
     </Grid>

        这里需要说明的是,一般在写自定义控件时,控件的内容可通过ContentPresenter来显示。

        其实整个界面实现比较复杂的部分是标题栏部分,我们要显示图标、标题、最小化、最大化、关闭按钮。图标、标题靠左显示,而最小化、最大化、关闭按钮靠右显示。这里我们想起了DockPanel面板,图标和标题实现如下:   

    <DockPanel  LastChildFill="False" >
         <Image DockPanel.Dock="Left" Source="{TemplateBinding Icon}" />
         <Label VerticalAlignment="Center"  DockPanel.Dock="Left"  Content="{TemplateBinding Title}"
          Style="{DynamicResource TitleStyle}"/>
        ...
    </DockPanel>

        显示的图标和标题我们可通过TemplateBinding直接绑定Window的图标和标题。DockPanel右边主要是三个按钮,我们这里拿关闭按钮分析:   

    <Button x:Name="PART_CLOSE"  Style="{DynamicResource Window_Titlebar_ButtonStyle}" 
              DockPanel.Dock="Right">
            <Image x:Name="c" Margin="3, 0"   Style="{DynamicResource TitleButtonImageStyle}" DockPanel.Dock="Right" Width="24" Height="24"  Source="{DynamicResource Window_Button_Close_ImageBrush}"   />
     </Button>

        关闭按钮是一个Button类型元素,它的内容就是显示一个图标,我们知道默认的Button按钮,本身有一些Normal、MouseOver、Press状态的效果。但这些效果不是我们想要的,所以必须得重写,上面代码我们设置Button的Style为Window_Titlebar_ButtonStyle就是重写设置了Button的样式,而嵌套的Image样式也是引用的TitleButtonImageStyle样式。Window_Titlebar_ButtonStyle的样式代码为:

    <Style x:Key="Window_Titlebar_ButtonStyle" TargetType="{x:Type ButtonBase}">
            <Setter Property="BorderThickness" Value="0" />
            <Setter Property="Background" Value="Transparent" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ButtonBase}">
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsMouseOver" Value="True">
                                <Setter Property="Background" Value="Transparent"/>
                                <Setter Property="BorderThickness" Value="0" />
                            </Trigger>
                            <Trigger Property="IsEnabled" Value="False">
                                <Setter Property="Background" Value="Transparent" />
                                <Setter Property="BorderThickness" Value="0" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                        <ContentPresenter … />                    
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>

        代码中,通过触发器重写了MouseOver和IsEnabled为False状态的显示效果,CustomWindow.xaml模板就分析到这里。

    第三步,实现界面逻辑

        现在我们只是实现了界面的可视化界面,当我们点击最小化按钮时需要把窗口最小化,怎样实现这样的功能?我们又不得不回到HeaviSoft.FrameworkBase.Component工程中添加的CustomWindow类,我们可以通过窗口的可视化层次关系找到最小化按钮,在查找按钮之前我们必须知道它们的Name,所以一般我们都会在类的头部声明模板部件名称。如下所示:

    /// <summary>
        /// 自定义界面
        /// </summary>
        [TemplatePart(Name = "PART_TITLEBAR", Type = typeof(UIElement))]
        [TemplatePart(Name = "PART_CLOSE", Type = typeof(Button))]
        [TemplatePart(Name = "PART_MAXIMIZE_RESTORE", Type = typeof(Button))]
        [TemplatePart(Name = "PART_MINIMIZE", Type = typeof(Button))]
        public class CustomWindow : Window
    {
    }

        上面的代码可以让我们很直观的知道最小化按钮的名称为PART_MINIMIZE,然后我们声明一个Button属性:

    /// <summary>
    /// 最小化按钮
     /// </summary>
    private Button MinimizeButton { get; set; }

       接下来在什么时机捕获显示的按钮呢?肯定是必须得等在HeaviSoft.FrameworkBase.Theme工程下添加的CustomWindow.xaml模板被加载完后才能查找,正好Control控件为我们提供了可重写的方法OnApplyTemplate,我们可以在该方法中去递归遍历查找控件。实现代码如下:

    public override void OnApplyTemplate()
            {
                base.OnApplyTemplate();
                AttachToVisualTree();
            }
    
            /// <summary>
            /// 附加可视化树到模板
            /// </summary>
            private void AttachToVisualTree()
            {
                AttachCloseButton();
                AttachMinButton();
                AttachMaximizeRestoreButton();
                AttachTitleBar();
            }
    
    
            /// <summary>
            /// 附加最小化按钮
            /// </summary>
            private void AttachMinButton()
            {
                if(MinimizeButton != null)
                {
                    MinimizeButton.Command = null;
                }
                var minimizeButton = GetChildControl<Button>("PART_MINIMIZE");
                if(minimizeButton != null)
                {
                    minimizeButton.Command = MinimizedCommand;
                    MinimizeButton = minimizeButton;
                }
            }

        OnApplyTemplate方法调用了AttachToVisualTree方法,而AttachToVisualTree方法中又调用了AttachMinButton方法,AttachMinButton方法通过GetChildControl<Button>("PART_MINIMIZE")来查找模板中的最小化按钮,找到之后绑定MinimizedCommand命令,命名的初始化代码片段如下:

    <public CustomWindow()
    {
            CreateCommandBindings();
    }
    
    /// <summary>
    /// 创建绑定命令
    /// </summary>
    private void CreateCommandBindings()
    {
            CommandBindings.Add(new CommandBinding(ApplicationCommands.Close, (a, b) => { Close(); }));
            CommandBindings.Add(new CommandBinding(MinimizedCommand, (a, b) => { WindowState = WindowState.Minimized; }));
            CommandBindings.Add(new CommandBinding(MaximizeRestoreCommand, (a, b) => { WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized; }));
    }
    
    /// <summary>
     /// 最小化指令
    /// </summary>
     private readonly RoutedCommand MinimizedCommand = new RoutedUICommand("Minmize", "Minmize", typeof(CustomWindow));
    
     /// <summary>
    /// 最大化复原指令
    /// </summary>
    private readonly RoutedCommand MaximizeRestoreCommand = new RoutedUICommand("MaximizeRestore", "MaximizeRestore", typeof(CustomWindow));

        代码也很简单,首先声明一个Command,然后在CreateCommandBindings方法中为声明的Command绑定命名,比如最小化指令绑定的方法为:(a, b) => { WindowState = WindowState.Minimized; },直接设置WindowState的值为Minimized最小化。其他的操作,例如最大化、关闭以及窗口的拖拽可直接查看源代码了解,这里就不在做分析了。

    第四步,提取主题资源

        一个自定义控件的实现我们差不多完成了90%,还剩一个任务就是把模板文件中的主题资源(例如颜色、图片)提取到主题/Themes/下面去。例如前面实现的Window模板中,最小化、最大化等按钮有使用图片,比较好的设计肯定是不会直接使用图片路径,所以我们把这些资源提取到/Themes/ColorBrush.xaml资源文件中去。提取资如下:

    <SolidColorBrush x:Key="Window_Title_Font_Color" Color="#77818b" />
    <SolidColorBrush x:Key="Window_Font_Color" Color="#77818b" />
    <SolidColorBrush x:Key="Window_Dark_Font_Color" Color="#fff" />
    <BitmapImage x:Key="Window_Button_Close_ImageBrush" UriSource="Images/Window/close.png" />
    <BitmapImage x:Key="Window_Button_Min_ImageBrush" UriSource="Images/Window/min.png" />
    <BitmapImage x:Key="Window_Button_Max_ImageBrush" UriSource="Images/Window/max.png" />
    <BitmapImage x:Key="Window_Button_Maximum_ImageBrush" UriSource="Images/Window/maximum.png" />

        Window模板中我们只使用这些Key值即可。而不关系图片的具体路径。这样设计,以后我们切换主题时就很方便。到目前为止,怎样完完整整的添加一个自定义控件就介绍完了。由于代码量比较大,所以在上面介绍时只贴出了部分代码,完整的代码可在GitHub上获取。

    总结

        能看到这里的,也辛苦各位了。本片主要介绍了:

        (1).WPF界面设计的几个基础技术点,包括Style、Template、Theme。

        (2)界面设计的工具和资源,包括Blend、PhotoShop、Snoop以及网站资源。

       (3)工程结构。

       (4)自定义控件实现过程。

        如果本篇内容对大家有帮助,请点击页面右下角的关注。如果觉得不好,也欢迎拍砖。你们的评价就是博主的动力!下篇内容,敬请期待!

    源代码

        完整的代码存放在GitHub上,代码路径:https://github.com/heavis/Documentor_V01R01/

    展开全文
  • DMSkin-for-WPF是一个基于WPF的.Net WPF开源界面库,实现了无边框的WPF开发方案,内置部分控件模板. 你可以参照模板自行修改完善。(以下简称DFW)。 核心 DFW实现了比较完美的无边框窗体方案,并且拖拽全部采用WIN...

    DMSkin-for-WPF是一个基于WPF的.Net WPF开源界面库,实现了无边框的WPF开发方案,内置部分控件模板. 你可以参照模板自行修改完善。(以下简称DFW)。

    核心

    DFW实现了比较完美的无边框窗体方案,并且拖拽全部采用WIN32消息实现。拖拽依靠桌面边缘完美,高DPI支持,窗体不会变形或异常

    另外,由于我对MVVM不擅长,所以DEMO并不是采用MVVM框架。

    版本更新

    2.0.0.1 (2018-01-30)

    1.新增一个窗口Demo。

    2.0.0.0 (2017-10-15)

    1.移除WindowMode。

    2.目前WIN7有点小瑕疵。

    3.0 (2017-9-21)

    1.WIN7以及以下采用单层。

    2.WIN8、WIN10采用双层。

    2.4 (2017-9-21)

    1.窗口边缘拉伸(右,右下,下)。

    2.阴影恢复速度调为200ms

    3.阴影可以完全关闭(高效率,配合窗口虚线使用)

    2.3 (2017-9-20)

    1.修复ALT+TAB 出现2个窗体的BUG。

    2.阴影层背景色,拉伸 拖拽时 出现的颜色。选择跟主窗体 接近的颜色 用户体验更好

    2.2 (2017-9-20)

    1.修复多个窗口无法激活聚焦的BUG。

    2.拖动窗口支持显示阴影层

    3.阴影层延迟显示的BUG修复

    2.1 (2017-9-19)

    1.优化最小化恢复阴影顺序,不会像网易云音乐一样出现双层了。

    2.去除窗口裁剪代码(之前的裁剪操作多此一举)

    3.拖动窗口位置时隐藏阴影提高效率

    【2.0版本】采用双层窗体+Win32实现无边框,2.0版本不支持圆角窗体,不支持窗体透明,但是拥有完美最小化的动画。如果采用虚线边框,则可以去除双层窗体。

    1.0版本】采用WindowStyle.None + 透明实现无边框,版本缺陷是无边框通病,窗体最小化 动画失效了。但是我用xaml实现了动画(动画流畅程度取决于显卡),需要这个版本的源码请点击我的头像进到另外一个1.0项目中获取

    2.0 (2017-9-13)

    1.版本升级到2.0,最小化动画终于解决,此方案可以移植到winform无边框中,这是我所知道的世界第一例WPF/winfrom无边框最小化动画方案。

    0.8 (2017-8-26)

    1.修复最小化动画以及恢复动画(尚可优化)

    0.7 (2017-8-25)

     

    1.代码托管到GITHUB

    2.新增Demo:周杰伦音乐播放器

    3.新增Demo:默认模板窗体

    0.6 (2017-3-6)

    1.新增DMSystemButtonHoverColor 系统按钮鼠标悬浮的背景色(圆角窗体请设为透明,效果更好)

    2.新增窗体模式:扁平化Metro+阴影Shadow 2种风格窗体

     

     

    使用说明

    1.引用DMSkin.WPF.DLL
    2.Window继承修改为:MainWindow : DMSkinWindow
    3.添加引用:xmlns:DMSkin="clr-namespace:DMSkin.WPF;assembly=DMSkin.WPF"
    4.XAML继承修改为: DMSkin:DMSkinWindow x:Class="DMSkin.WPF.Test.MainWindow"
    

      

    窗体属性

          
    Foreground="White"                    //前景色 
    Background="White"                    //背景色 
    DMShowMin="True"                      //显示系统按钮-最小化
    DMShowMax="True"                      //显示系统按钮-最大化
    DMShowClose="True"                    //显示系统按钮-关闭
    DMWindowShadowSize="10"               //窗体边框阴影大小
    DMWindowShadowColor="#FFC8C8C8"       //窗体边框阴影颜色
    DMWindowShadowDragVisibility="False"  //窗体拖动时是否显示阴影层
    DMWindowShadowVisibility="False"      //窗体是否有阴影层[关闭阴影层]
    DMWindowShadowBackColor="#FF323CAD"   //阴影背景色,选择跟主窗体相近的颜色 拉伸跟拖动 用户体验更好|#FF323CAD 为蓝色
    DMSystemButtonSize="50"               //系统按钮大小
    DMSystemButtonForeground="#FF666666"  //系统按钮[文字]颜色
    DMSystemButtonHoverColor="#33000000"  //系统按钮的鼠标悬浮[背景]色
    DMSystemButtonHoverForeground="White" //系统按钮的鼠标悬浮[文字]颜色
    DMSystemButtonCloseHoverColor="Red"   //系统【关闭】按钮的鼠标悬浮[背景]色-默认为红色
    DMSystemButtonShadowEffect="0"        //系统按钮的阴影大小
    ResizeMode="CanResize"                //边框拉伸方案CanResiz和CanResizeWithGrip
    Height="700" Width="1000"             //窗体大小
    MinHeight="268" MinWidth="360"        //窗体最大以及最小属性
    WindowStartupLocation="CenterScreen"  //窗体初始位置
    
    DMMetroBorderColor="#FFC8C8C8"  //窗体边框颜色-仅Metro有效   --2.0中移除
    DMMetroBorderSize="1"           //边框大小-仅Metro有效   --2.0中移除
    DMWindow="Shadow"               //Shadow-阴影模式  Metro-线条扁平化模式   --2.0中移除
    

      

    资源引用

    <Application.Resources>
                <ResourceDictionary>
                    <ResourceDictionary.MergedDictionaries>
                    <ResourceDictionary Source="pack://application:,,,/DMSkin.WPF;Component/Themes/DMSkin.xaml" />
                    <ResourceDictionary Source="pack://application:,,,/DMSkin.WPF;Component/Themes/DMColor.xaml" />
                    <ResourceDictionary Source="pack://application:,,,/DMSkin.WPF;Component/Themes/DMScrollViewer.xaml" />
                    <ResourceDictionary Source="pack://application:,,,/DMSkin.Wpf;component/Themes/DMButton.xaml" />
                    <ResourceDictionary Source="pack://application:,,,/DMSkin.Wpf;component/Themes/DMTabControl.xaml" />
                    <ResourceDictionary Source="pack://application:,,,/DMSkin.Wpf;component/Themes/DMRadioButton.xaml" />
                    <ResourceDictionary Source="pack://application:,,,/DMSkin.Wpf;component/Themes/DMTreeView.xaml" />
                    <ResourceDictionary Source="pack://application:,,,/DMSkin.Wpf;component/Themes/DMDataGrid.xaml" />
                    <ResourceDictionary Source="pack://application:,,,/DMSkin.Wpf;component/Themes/DMListBox.xaml" />
                    <ResourceDictionary Source="pack://application:,,,/DMSkin.Wpf;component/Themes/DMSlider.xaml" />
                    <ResourceDictionary Source="pack://application:,,,/DMSkin.Wpf;component/Themes/DMCheckBox.xaml" />
                    <ResourceDictionary Source="pack://application:,,,/DMSkin.Wpf;component/Themes/DMContextMenu.xaml" />
                    </ResourceDictionary.MergedDictionaries>
                </ResourceDictionary>
    </Application.Resources>
    

      

    DMSkin-for-WPF 2.0.0.1

     

     

    开源地址:https://github.com/944095635/DMSkin-for-WPF

    转载于:https://www.cnblogs.com/DMSkin/p/8398492.html

    展开全文
  • WPF快速入门系列(8)——MVVM快速入门 一、引言  在前面介绍了WPF一些核心的内容,其中包括WPF布局、依赖属性、路由事件、绑定、命令、资源样式和模板。然而,在WPF还衍生出了一种很好的编程框架,即WVVM,...

    WPF快速入门系列(8)——MVVM快速入门

     

    一、引言

      在前面介绍了WPF一些核心的内容,其中包括WPF布局、依赖属性、路由事件、绑定、命令、资源样式和模板。然而,在WPF还衍生出了一种很好的编程框架,即WVVM,在Web端开发有MVC,在WPF客户端开发中有MVVM,其中VM就相当于MVC中C(Control)。在Web端,微软开发了Asp.net MVC这样的MVC框架,同样在WPF领域,微软也开发了Prism这样的MVVM框架。Prism项目地址是:http://compositewpf.codeplex.com/SourceControl/latest。大家有兴趣的可以下载源码研究下。

      本文所有源码下载:FristMVVMProject.zip

    二、MVVM模式是什么?

      既然讲到MVVM模式,自然第一个问题就是MVVM的含义。MVVM是Model-View-ViewModel的缩写形式,它通常被用于WPF或Silverlight开发。这三者之间的关系如下图所示:

      下面我们分别来介绍下这三部分。

    模型(Model)

      Model——可以理解为带有字段,属性的类。

    视图(View)

      View——可以理解为我们所看到的UI。

    视图模型(View Model)

    View Model在View和Model之间,起到连接的作用,并且使得View和Model层分离。View Model不仅仅是Model的包装,它还包含了程序逻辑,以及Model扩展,例如,如果Model中有一个公开属性不需要在UI上显示,此时我们可以不再View Model中去定义它。

    在MVVM模式下,WPF程序的运行流程如下图所示:

     

      在MVVM中,VM的地位可以说是举足轻重。使用MVVM模式具有以下几个特点:

    • 视图的cs文件包括极少的代码,其核心逻辑都被放在View Model类中,从而使得程序逻辑与视图耦合度降低。
    • ViewModel类作为View的DataContext。
    • 在MVVM下,所有的事件和动作都被当成命令,如按钮的点击操作,此时不是触发点击事件,而是绑定到一个点击命令,再由命令去执行对应的逻辑。

    三、使用MVVM模式来实现WPF程序

      前面介绍了MVVM一些基础知识,下面通过一个实例来说明下如何在WPF程序中应用MVVM模式。在之前实现WPF程序时,我们可能会把所有的后台逻辑都放在视图的后台文件中,这样的实现方式的好处更直观,方便,对于一些小的应用程序这样做当然没什么问题,但是对于复杂的应用程序这样写的话,可能会导致后台代码显得非常臃肿,到最好变得难以维护。此时想到的解决方案就是职责分离,使后台的逻辑分离到其他类中,MVVM其实我理解就是达到这个目的。下面我们按照MVVM的组成部分来实现下这个MVVM程序。

      第一步:自然是数据部分了,即Model层的实现。在这里定义了一个Person类,其中包含了2个基本的属性。

    public class Person
        {
            public string Name { get; set; }
            public int Age { get; set; }
        }

      为了进行测试,下面创建一个静态方法来获得测试数据。

    复制代码
    public class PersonDataHelper
        {
            public static ObservableCollection<Person> GetPersons()
            {
                ObservableCollection<Person> samplePersons = new ObservableCollection<Person>();
                samplePersons.Add(new Person() {Name = "张三", Age = 33});
                samplePersons.Add(new Person() { Name ="王五", Age= 22 });
                samplePersons.Add(new Person() { Name = "李四", Age = 35 });
                samplePersons.Add(new Person() { Name = "LearningHard", Age = 27 });
                return samplePersons;
            }
        }
    复制代码

      第二步:实现ViewModel层,实现数据和界面之间的逻辑。在视图模型类中,包含了属性和命令,因为在MVVM中,事件都当成命令来进行处理,其中命令只能与具有Command属性的控件进行绑定。既然要包含命令,首先就需要实现一个命令,这里自定义的命令需要实现ICommand接口。这里我们定义了一个QueryCommand。具体的实现代码如下所示:

    复制代码
    public class QueryCommand :ICommand
        {
            #region Fields
            private Action _execute;
            private Func<bool> _canExecute;
            #endregion 
    
            public QueryCommand(Action execute)
                : this(execute, null)
            { 
            }
            public QueryCommand(Action execute, Func<bool> canExecute)
            {
                if (execute == null)
                    throw new ArgumentNullException("execute");
                _execute = execute;
                _canExecute = canExecute;
            }
    
            #region ICommand Member
    
            public event EventHandler CanExecuteChanged
            {
                add
                {
                    if (_canExecute != null)
                    {
                        CommandManager.RequerySuggested += value;
    
                    }
                }
                remove
                {
                    if (_canExecute != null)
                    {
                        CommandManager.RequerySuggested -= value;
    
                    }
                }
            }
    
            public bool CanExecute(object parameter)
            {
                return _canExecute == null ? true : _canExecute();
            }
    
            public void Execute(object parameter)
            {
                _execute();
            }
            #endregion
        }
    复制代码

      接下来就是定义我们的ViewModel类了,具体的实现代码如下所示:

    复制代码
     1 public class PersonListViewModel : INotifyPropertyChanged
     2     {
     3         #region Fields
     4         private string _searchText;
     5         private ObservableCollection<Person> _resultList;
     6         #endregion 
     7 
     8         #region Properties
     9 
    10         public ObservableCollection<Person> PersonList { get; private set; }
    11 
    12         // 查询关键字
    13         public string SearchText
    14         {
    15             get { return _searchText; }
    16             set
    17             {
    18                 _searchText = value;
    19                 RaisePropertyChanged("SearchText");
    20             }
    21         }
    22 
    23         // 查询结果
    24         public ObservableCollection<Person> ResultList
    25         {
    26             get { return _resultList; }
    27             set
    28             {
    29                 _resultList = value;
    30                 RaisePropertyChanged("ResultList");
    31             }
    32         }
    33 
    34         public ICommand QueryCommand 
    35         { 
    36             get { return new QueryCommand(Searching, CanSearching); } 
    37         }
    38 
    39         #endregion 
    40 
    41         #region Construction
    42         public PersonListViewModel()
    43         {
    44             PersonList = PersonDataHelper.GetPersons();
    45             _resultList = PersonList;
    46         }
    47 
    48         #endregion
    49         
    50         #region Command Handler
    51         public void Searching()
    52         {
    53             ObservableCollection<Person> personList = null;
    54             if (string.IsNullOrWhiteSpace(SearchText))
    55             {
    56                 ResultList = PersonList;
    57             }
    58             else
    59             {
    60                 personList = new ObservableCollection<Person>();
    61                 foreach (Person p in PersonList)
    62                 {
    63                     if (p.Name.Contains(SearchText))
    64                     {
    65                         personList.Add(p);
    66                     }
    67                 }
    68                 if (personList != null)
    69                 {
    70                     ResultList = personList;
    71                 }
    72             }
    73         }
    74 
    75         public bool CanSearching()
    76         {
    77             return true;
    78         }
    79 
    80         #endregion 
    81 
    82         #region INotifyPropertyChanged Members
    83 
    84         public event PropertyChangedEventHandler PropertyChanged;
    85 
    86         #endregion
    87 
    88         #region Methods
    89         private void RaisePropertyChanged(string propertyName)
    90         {
    91             // take a copy to prevent thread issues
    92             PropertyChangedEventHandler handler = PropertyChanged;
    93             if (handler != null)
    94             {
    95                 handler(this, new PropertyChangedEventArgs(propertyName));
    96             }
    97         }
    98         #endregion 
    99     }
    复制代码

      第三步:实现View层,设计我们的视图,设置它的DataContext属性为ViewModel类。具体的XAML代码如下所示:

    复制代码
    <Window x:Class="MVVMDemo.View.PersonsView"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            xmlns:local="clr-namespace:MVVMDemo.ViewModel"
            Title="PersonsView" Height="350" Width="400">
        <!--设置DataContex是ViewModel类,当然你也可以使用后台代码设置-->
        <Window.DataContext>
            <local:PersonListViewModel />
        </Window.DataContext>
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="50"/>
                <RowDefinition Height="*"/>
            </Grid.RowDefinitions>
            <TextBox Grid.Row="0" Name="searchtxt" Text="{Binding  Path=SearchText, Mode=TwoWay}" HorizontalAlignment="Left" Height="30" Width="280" Margin="10,0,0,0"></TextBox>
            <Button Grid.Row="0" Name="searchBtn" Content="Search" Command="{Binding Path=QueryCommand}" Width="80" Height="30" HorizontalAlignment="Right" Margin="0,0,10,0"></Button>
            <DataGrid Grid.Row="1" Name="datGrid" 
                      HorizontalAlignment="Center"
                      VerticalAlignment="Top" ItemsSource="{Binding Path=ResultList}" Width="300"></DataGrid>
            
        </Grid>
    </Window>
    复制代码

      到此,我们的MVVM的WPF程序就已经完成了,下面就是要看看程序是否达到我们预期的目的。具体的运行结果如下图所示:

    四、总结

      到这里,本文的内容就分享完了,并且本文也是WPF系列的最后一篇了,希望这个系列可以使得初学者快速上手WPF编程。在接下来的时间里,我打算写一些具有实战性的内容,因为我之前都是分享一些初级的入门系列,接下来打算分享一些实际的项目实现,以及领域驱动设计方面的内容,希望得到大家的督促和支持。

    转载于:https://www.cnblogs.com/Jeely/p/11076953.html

    展开全文
  • 所以这里向wpf技术栈的开发者分享一套wpf教程,基于.net5框架进行开发本系列每一期视频长度平均在15分钟左右,并利用自己多年开发经验精炼内容帮助有基础的新手或有经验的开发者快速学习wpf这项技术。 二、详细 卡片...

    一、概要

    在工作中大家会遇到需要学习新的技术或者临时被抽调到新的项目当中进行开发。通常这样的情况比较紧急没有那么多的时间去看书学习。所以这里向wpf技术栈的开发者分享一套wpf教程,基于.net5框架进行开发本系列每一期视频长度平均在15分钟左右,并利用自己多年开发经验精炼内容帮助有基础的新手或有经验的开发者快速学习wpf这项技术。

    二、详细

    卡片中提供了整套视频的地址点击跳转即可:

    https://www.bilibili.com/video/BV19K411M72o

    整套视频一共有十一期:

    1.初识wpf 【WPF的技术的认识、应用行业、未来发展】

    2.XAML布局【布局在WPF中是最基础也是最重要的一环,它直接决定你界面的样子。掌握熟练度决定它的美观度和可交互性。】

    3.控件、依赖项属性【控件体现于在窗口中可视化、可交互并实现某些行为。依赖属性在wpf主要扮演数据驱动中的重要角色,它能配合绑定一起实时数据更新UI显示、动画、自定义控件等。】

    4.绑定【绑定顾名思义,是wpf最基础也是最重要的一环,是将我们获取到的数据和UI上的控件绑定起来利用数据的变化来更新界面所看到的内容。】

    5.命令【命令表示应用程序任务,并且跟踪任务是否能够被执行。然而,命令实际上不包含执行应用程序任务的代码。】

    6.MVVM模式【MVVM是一种开发模式,是一种开发标准。在WPF中应用到MVVM是非常常见的,MVVM全称为Model、View、ViewModel。】

    7.资源、样式【1.WPF资源系统是一种保管一系列有用对象(如常用的画刷、样式和模板)的简单方法,从而使您可以更容易地重用这些对象。每个元素都有Resources属性,该属性存储了一个资源字典集合(它是ResourceDictionary类的实例)。资源集合可包含任意类型的对象,根据字符串编写索引。2.样式是修改View(窗体、控件)样式的主要手段,主要作用更改控件的外观以及增强用户体验】

    8.Convert【Convert可以将源数据和目标数据之间进行特定的转化。】

    9.模板【模板应用在View层,它的主要作用是修改控件的样式、交互、数据展示。】

    10.线程【1.线程是一个可执行的路径,它可以独立于其他线程执行。2.每个线程都在操作系统的进程内执行,而操作系统进程提供了程序运行的独立环境。3.单线程应用,在进程的独立环境里只跑一个线程,所以该线程拥有独占权。4.多线程应用,单个进程中会跑多个线程,他们会共享当前的执行环境(内存)等。5.进程和线程的对应关系,一个进程可以拥有多个线程,多个线程只能属于一个进程。 例如:一个非常耗时的操作(读数据库、复杂耗时的计算),如果只用主线程执行UI线程会“假死”专业术语叫线程阻塞。】

    11.项目【新手快速入门的最后一章,主要讲解企业级项目中的结构、一款客户端应用程序我们该如何去设计、Nuget的使用、 完成一个具有播放器基础功能的项目。】

    如果能帮助到各位开发者希望关注我。

    展开全文
  • 开发这套控件的目的是为了可以让项目快速开发成型,这套控件封装了一些项目中常用的控件,用来放置菜单导航,树形列表等等。 ,只需要提供相应的数据和简单的设置一些控件属性就可以了 MVVM 工具包实现了一套简易的...
  • 这篇快速入门文章包括如何使用Prism进行编码、探索和创建模块的示例 创建模块: 模块是实现了IModule接口的类, 可以通过声明特性来给模块命名、控制其初始化、定义依赖等 注册模块: 模块可以通过以下方式注册...
  • 前言 QQ、微信截图功能已很强大了,似乎没必要在开发一个截图...没有掌握WPF之前,我是不会开发这么一个程序的,如果采用MFC、winform框架,工作量是相当的大,开发出来的效果肯定也比较low。本人用WPF,花了一天...
  • 最近在研究学习Swift,苹果希望它迅速取代复杂的Objective-C开发,引发了一大堆热潮去学它,放眼望去各个培训机构都已打着Swift开发0基础快速上手的招牌了。不过我觉得,等同于无C++基础上手学习C#一样,即使将来OC...
  • WPF框架之MVVM系列(一)

    2015-09-25 14:06:00
    WPF框架之MVVM系列(一) (一)定义:MVVM(Model-View-ViewModel)是在MVC(Model-View-Control)模式之后引出的新的开发模式,他与MVC模式一样用于把视图(界面)和数据进行解耦,不同的是采用...
  • Orchestra是建立在之上的成熟,可组合的WPF Shell和框架。 它已经进行了超过5年的积极开发(首次提交2012/06/18),并且每天在众多的业务线(LoB)应用中使用。 Orchestra由一个主外壳组成,该外壳包含一个SDK,...
  • 前言 QQ、微信截图功能已很强大了,似乎没必要在开发一个截图...没有掌握WPF之前,我是不会开发这么一个程序的,如果采用MFC、winform框架,工作量是相当的大,开发出来的效果肯定也比较low。本人用WPF,花了一天...
  • 【翻译】WPF应用程序模块化开发快速入门(使用Prism框架)【上】 【翻译】WPF应用程序模块化开发快速入门(使用Prism+MEF)【中】 系统启动 系统使用Bootstrapper类型来启动程序,并初始化主窗口 /// <...
  • 【翻译】WPF应用程序模块化开发快速入门(使用Prism框架)【上】 编译并运行快速入门 需要在VisualStudio 2010上运行此快速入门示例 代码下载:ModularityWithMef.zip 先重新生成解决方案 再按F5运行此示例 ...
  • WPF/MVVM快速指引

    2015-05-23 19:16:00
    这边文章会很长,所以我会用几个例子的形式来展示一个小型MVVM框架的诞生以及怎样使用。所有的例子基于.net 4.0,使用的开发工具是Visual Studio Community 2013。 基础知识 1.对WPF而...

空空如也

空空如也

1 2 3 4 5 ... 7
收藏数 132
精华内容 52
关键字:

wpf快速开发框架