`

创建你的第一个WPF项目

阅读更多
WPF基础知识
快速学习绝不是从零学起的,良好的基础是快速入手的关键,下面先为大家摞列以下自己总结的学习WPF的几点基础知识:

1) C#基础语法知识(或者其他.NET支持的语言):这个是当然的了,虽然WPF是XAML配置的,但是总还是要写代码的,相信各位读者应该也都有这个基础了。

2) HTML语言:虽然WPF是窗体程序但是由于使用的XAML语言,如果以前接触过HTML、XHTML、ASP.NET之路的东西的话会,接受这些标签会很有帮助的,如果以前一直是从事win form开的人来说可能就要适应一下了。

3) 对C#中的代理、事件要做到熟练掌握,在.NET 3.0 版本后有增加了Routed Events,要想日后不糊涂,这个是基础。

4) 有一定的winform或ASP.NET经验,主要是对控件事件的处理要有写了解。

5) 拥有良好的面向对象的思想:思想是语言的升华(本人的OO思想完全是Java中领悟来的)。在WPF中,经常要灵活运用各种继承关系、多态、重载等,因此一定要把基础知识打牢固。

6) DataBinding要有所了解:Binding是WPF的一大亮点,在接触它以前如果接触国ADO.NET里面的DataBinding的话对相对起来会容易接受一点,虽然这两个有一定的不同。

7) 对设计模式要有一定的了解:当然是越深入越好了,在实际项目中,各种设计模式经常交融使用。



   快速的识别并合理的运用,无论是在开发还是调试时都是非常高效的。另外,WPF存在的初衷即是表现与逻辑的松耦合,最普遍的情况就是XAML作为表现层,背后.cs文件作为逻辑层。因此,日后在从事项目工作时,要时刻谨记这一点,千万不可背道而驰。

   不要为了凸显自己的某一些代码特长而将各种逻辑混写在一起,这样非常不实际的,这一点都在校生应该尤为重要。

8) 对XML的理解:XAML也是XML,对XML的理解绝对有助于快速的接受和使用XAML,并不需要多XML有多么高深的见解。

    但是最好还是有空看一看XML相关的书籍http://www.w3.org/TR/2000/WD-xml-2e-20000814。无论是WPF、WCP以及底层一些的SOAP等都是很有帮助的。

WPF入手练习基础环境
开发环境:VS 2008

数据库:本机SQL Server 2005(这里给大家一个提醒,如果大家的机子是Windows XP的话,无论是home 或是 professional.

一定不要安装SQL Server 2005 Enterprise Edition,在《安装 SQL Server 2005 的硬件和软件要求》中“操作系统要求”列表有写到2005 Enterprise Edition 不支持 XP),建议安装Developer Edition,一定要安装数据库实例。

WPF练习内容
具备以上基础条件后,开始入手练习,大家不要抱怨入手练习有写难度,毕竟是快速入门要有一定的跳跃性:

我们要做一个WPF程序,功能很简单:

1)   从数据库(本地数据库(local)/AdventureWorks中的person.contact表中提取用户的ContactID,FirstName,LastName,EmailAddress数据,展示到Form上的一个ListView上。(由于是WPF练习,对于ADO.NET相关的东西在此不做介绍,知识使用而已)

2) 当鼠标或其他设备选中结果某一项记录时,在List框下面展示出细节。

3) 修改其中的内容后,结果联动更新到List框及数据库中。

想象以下这样一个东西如果在以前使用winform实现会是什么样子呢?

应该会写不少的方法、属性用于界面之间及界面与数据库之间的联动。

今天这个练习就先展示以下WPF的技术亮点之一:

DataBinding。在制作过程中,还会为大家不断接受一些控件、布局等相关知识和技巧,部分相关的知识内容与此练习关系不大的,我将会用浅灰色字体带过,对于熟悉这部分内容的读者可以直接跳过。

建立一个WPF项目
打开VS 2008 新建一个WPF应用程序

首先会看到一个靓丽的小方框,将鼠标放在方框的边缘点击就会产生相应的分割线。



现在我们要做的内容需要将窗体分成三行,可以先随便分割一下,以后在调整相互的大小。这时候会注意到下方的XML代码区域。每个RowDefinition作为一个行被定义出来

这里先给大家接受以下高度、宽度的几种定义方式(写过HTML的人可以跳过去了):

绝对尺寸(Absolut sizing):就是给一个实际的数字,像现在例子中那样

自动(Autosizing):值为Auto,实际作用就是取实际控件所需的最小值(Setting Height or Width to Auto, which gives child elements the space that they need and no more)。

Proportional sizing(也可以称之为star sizing因为有个*号表示):

值为*或N*,实际作用就是取尽可能大的值,当某一列或行被定义为*则是尽可能大,当出现多列或行被定义为*则是代表几者之间按比例方设置尺寸。

下面我们来将上面的知识应用以下看看。

将这三行的值设置为如下数值,结果会是如何:



            <RowDefinition Height="*" />

            <RowDefinition Height="Auto" />

            <RowDefinition Height="22" />



第0行设为*用来放置ListView,这样会在实际运行过程中尽可能的充满整个区域



第1行设为Auto尽量紧凑配列,少占用空间(如果里面没有东西的话,他会小到0,这是你会看不到它)



第2行设为22固定值,只是用来放一个Button使用

对于初学者往往会习惯直接使用控件拖拽的形式来将需要的内容添加到窗体上。



会使控件在一定的坐标上固定位置,这是一种不推荐的做法,在此给大家讲解以下WPF中的布局观(Layout Philosophy):



在WPF窗体中,一个窗体只能持有一个控件,当多个控件想要在窗体中展现时,就需要首先设置一个容器控件(Container)



然后将其他控件放到这个控件里面,形成树状结构.



因此,布局观第一条就是,控件的布局应该有容器来决定,而不是通过自身使用margin之类的东西来控制位置。



因为这些属性原本应该是控制自己内部展现或与邻里之间关系的;



第二条,控件应避免明确的定义具体的尺寸,因为显示器分辨率及windows窗体的大小都有可能随时改变,如果明确的定义尺寸。



当窗体变动后就会出现大面积的空白或是缺失。但为了控件功能及效果的展示,应该限定一个可接受的最大及最小尺寸。



通过MinWidth, MinHeight, MaxWidth, MaxHeight属性可以实现这一点。第三条,不要将界面元素位置设置成与屏幕坐标相关.



现在显示器分辨率比较多样话(800×600、1024×768,我的显示器是一台是1400×1050,还有一个是1024×1280竖式的),这样的做法还是比较有风险的。



第四条,容器应将有效空间共享给其子控件,这也是为了不在窗体调整后,遗留出大块的空余。



第五条,容器嵌套使用,因为不同的容器,表现效果不同,必要时应结合使用。

接下来在工具箱(Tool Box)中双击ListView,一个小框会出现在界面上。



接下来在工具箱(Tool Box)中双击WrapPanel,又一个大框会出现在界面上。



再增加一个Button。



这是你会感觉到界面有点乱了,刚才我们在顶层Grid上面画线到底起什么作用了(到目前位置还没有),让我们来调整一下下面的XAML语句,最终结果如下:

    <Grid>

        <Grid.RowDefinitions>

            <RowDefinition Height="*" />

            <RowDefinition Height="Auto" />

            <RowDefinition Height="22" />

        </Grid.RowDefinitions>

        <ListView Name="listView1" MinWidth="280" >

            <ListView.View>

                <GridView x:Name="gridView1">

                    <GridViewColumn Header="ContactID"></GridViewColumn>

                    <GridViewColumn Header="FirstName"></GridViewColumn>

                    <GridViewColumn Header="LastName"></GridViewColumn>

                    <GridViewColumn Header="EmailAddress"></GridViewColumn>

                </GridView>

            </ListView.View>

        </ListView>

        <WrapPanel Grid.Row="1" Orientation="Horizontal"></WrapPanel>

        <Button Grid.Row="2" HorizontalAlignment="Right" Click="button1_Click" Name="button1">Refresh</Button>

    </Grid>

这里有几点又需要解释以下了:



1)      介绍以下容器控件Panel,现在界面中有两个容器型的控件一个是Grid跟元素,另一个是WrapPanel。它们都是容器型控件,不过表现上有所不同。



   Grid顾名思义“网格”,在之前我们已经定义了三行高度分辨是*, Auto, 22,其实还可以功过ColumnDefinition定义多个列,他的子控件被放在一个一个实现定义好的小格子里面,整齐配列。



   而WrapPanel则是将各个控件按照行或列的顺序摞列,当长度或高度不够是就会自动调整换例或行。



   还有一个常用控件这里稍后会用到StackPanel,将控件按照行或列来顺序排列不会回行。



2)      大家应该注意到了在WrapPanel及Button上面的Grid.Row="n",这个就是Attached Properties(不知道怎么翻译了,可能叫‘附着属性’).



   用来设置WrapPanel及Button应该在父容器的什么位置。这是WPF的特性之一,通俗的理解起来就是,别人有的属性,由于你跟他产生了关系所以你也有了这个属于他的属性。



记得FantasiaX‘水之真谛’曾经给我通俗的解释过这个特性,这里照搬出来分享给大家:一个小学生,身高、体重是他的自身属性,而这个小学生由于是N年级的X班的学生.



   因此,这个小学生又带有了一个附加的属性,N年级X班。在这个例子里如果学校作为一个Grid容器,N年级X班可以看作一个小格子,小学生是其中的一个实例,那么,小学生因为安置在这个班级.



因此获得了这个班级所拥有的这个属性,当学期末,老师说,这个学生已经升到N+1年级X班时,这个学生以后就跑到另一个小格子里去上课了。



  Attached Properties的XAML用法就是在自己的属性设置地方直接使用容器的类型名称.容器属性名称(Grid.Row)设置对应的值。

3)      大家应该注意到类似与ListView.View及Grid.RowDefinitions用法,这个叫做Complex Properties(应该叫‘复杂属性’吧).



   其实就是元素的某一个属性由于不能够简单的用名值对实现,因此需要单独标签话声明一下。



4)      再有就是x:Name="gridView1"这种用法,叫做Markup Extensions(‘标记扩展’吧),这个可是一个满不错的特性.



由于后面代码中要使用到GridView对象,然而GridView对象有没有Name属性,如果后台代码想要调用他的话就不得不从父容器向下遍历来找到想要的对象,这样无疑增加了后台代码与前台界面之间的耦合度,试想如果那天突然有需求说要把这个对象从这里移到另一个容器上去.



那么界面的变动伴随着的就是后台代码的一起变动,这与视图/逻辑分离显然背道而驰,有了Markup Extensions.



   我们想定位一个没有名字属性的控件,直接为扩展一个名称出来,这个可太方便了(当然,Markup Extensions不只是用来扩展名称的)。

如果希望每个TextBlock和TextBox成为一对出现的话,应该如何呢?

自然是需要一个容器将他们组织起来.

同时,希望他们在一条线上不回行。这就用到了,我们前面说到的一个容器StackPanel。组织后的代码如下:

<WrapPanel Grid.Row="1" Orientation="Horizontal">

            <StackPanel Orientation="Horizontal" Margin="5,2,5,2">

                <TextBlock Name="textBlock_ContactID" Text="ContactID:" />

                <TextBox Name="textBox_ContactID" MinWidth="100" />

            </StackPanel>

            <StackPanel Orientation="Horizontal" Margin="5,2,5,2">

                <TextBlock Name="textBlock_FirstName" Text="FirstName:" />

                <TextBox Name="textBox_FirstName" MinWidth="100" />

            </StackPanel>

            <StackPanel Orientation="Horizontal" Margin="5,2,5,2">

                <TextBlock Name="textBlock_LastName" Text="LastName:" />

                <TextBox Name="textBox_LastName" MinWidth="100" />

            </StackPanel>

            <StackPanel Orientation="Horizontal" Margin="5,2,5,2">

                <TextBlock Name="textBlock_EmailAddress" Text="EmailAddress:" />

                <TextBox Name="textBox_EmailAddress" MinWidth="100" />

            </StackPanel>

        </WrapPanel>

这样情况下在试试就可以看到效果了,无论窗体边缘怎么托拉TextBlock和TextBox总是成对的.



同时,随着窗体的拖动,控件会不断的改变位置一适应最小原则(如果想要让他固定下来的话.



那就需要将WrapPanel换成其他的Panel就可以了)。如下图所示:










补充知识:

WPF中所有单一内容控件的统称,对应的父类为System.Windows.Controls.ContentControl;



与ContentControl平行的还有一类控件叫做 ItemsControl,WPF中所有控件集合对象的统称,例如:ListView, ListBox, TreeView等,这一点一定要区分清楚.



可以认为这一类控件是为了展现有效的展现及组织一组控件而设计的,对应的父类为System.Windows.Controls.ItemsControl,这些控件和Panel容器类控件是不同的概念,要了解他们之间的关系可以参看下面这张类继承关系图:





WPF后台逻辑代码编写
在后台用ADO.NET写一个获取DataTable的方法及一个共有属性拥有获取DataTable.DefaultView.

同时,实现Refresh按钮的方法。此部分不是练习重点这里不做介绍了,代码如下:

  public Window2()

        {

            InitializeComponent();

            getData();

        }



        SqlDataAdapter sda;

        DataTable dt;



        void getData()

        {

            //init sqlconnection

            SqlConnectionStringBuilder connbuilder = new SqlConnectionStringBuilder();

            connbuilder.DataSource = "(local)";

            connbuilder.IntegratedSecurity = true;

            connbuilder.InitialCatalog = "AdventureWorks";



            //start to make sql query

            SqlConnection conn = new SqlConnection(connbuilder.ConnectionString);

            sda = new SqlDataAdapter("select ContactID,FirstName,LastName,EmailAddress from person.contact where ContactID<=100;", conn);

            SqlCommandBuilder commbuilder = new SqlCommandBuilder(sda);

            sda.UpdateCommand = commbuilder.GetUpdateCommand();

            dt = new DataTable();

            sda.AcceptChangesDuringUpdate = true;

            sda.Fill(dt);

        }



        private void button1_Click(object sender, RoutedEventArgs e)

        {

            getData();

        }

WPF前台界面与后台数据的Binding
Binding这个此翻译起来有争议,为了避免误导大家,因此我还是不写成中文了。



好了!接下来终于到最精彩的地方了。各位读者们一会会为后面所发生的事情而感到震惊的,一切就是这样的完成了……



1)      为ListView控件指明数据源,通过ItemSource属性设置,这一部分需要通过代码来实现(目前位置还不知道如何通过XAML语言来实现,哪位大哥可以指点一下)。



   在void getData()方法的结尾处增加一行代码“listView1.ItemsSource = dt.DefaultView;”

2)      给GridViewColumn指明当前列对应于数据源的哪一项,可以通过DisplayMemberBinding属性来实现.

其中的值便是上一步中指明的数据源dt.DefaultView的每一个数据项的名称.

  <GridViewColumn Header="ContactID" DisplayMemberBinding="{Binding Path=ContactID}"></GridViewColumn>

                    <GridViewColumn Header="FirstName" DisplayMemberBinding="{Binding Path=FirstName}"></GridViewColumn>

                    <GridViewColumn Header="LastName" DisplayMemberBinding="{Binding Path=LastName}"></GridViewColumn>

                    <GridViewColumn Header="EmailAddress" DisplayMemberBinding="{Binding Path=EmailAddress}"></GridViewColumn>



此时试着F5 Debug运行以下,应该已经可以看到这样的画面了





但是,当我们点击ListView中的记录时,里面的书籍并没有映射到下面的文本框中。

接下来我们就来实现这部分功能。

1)      为了简化代码,在WrapPanel元素中指明一个公共的上下文,可以通过增加属性

DataContext="{Binding ElementName=listView1,Path=SelectedItem}



2)      分别为四个输入框指明相应的数据源,可以通过TextBox元素中的Text属性实现,实现后代码如下:



<TextBox Name="textBox_ContactID" MinWidth="100" Text="{Binding ContactID}" />

……

<TextBox Name="textBox_FirstName" MinWidth="100" Text="{Binding FirstName,UpdateSourceTrigger=PropertyChanged }" />

……

<TextBox Name="textBox_LastName" MinWidth="100" Text="{Binding LastName}" />

……

<TextBox Name="textBox_EmailAddress" MinWidth="100" Text="{Binding EmailAddress}" />

上面这一段代码相信有很多刚刚接触WPF的人一定会感到陌生,这里解释一下:



上面这段XAML语句里面,大家看着最不顺眼的应该就是{Binding …}这种语句了吧,这个就是WPF重要特性之一“Binding”。



他是用来实现界面元素的属性与后台数据之间的Binding,通过这种形式将前台界面与后台数据联系在一起达到界面与数据耦合的目的。



与直接覆值相比较,存在这如下几点特性上的差异:



1)      Binding可以通过XAML语句实现界面与数据(可以是界面元素或后台对象)的耦合(也可以通过代码来实现)。



这一实现主要是依靠WPF的另一个特性Dependency Property来实现的。示意图如下:







2)      Binding可以实现制定方向的绑定,方向有三种,OneWay, TwoWay, OneWaytoSource,其形象的表示如下图所示:






3)      可配置触发器,这一特性用来解释,界面与数据的Binding是什么时候发生的,可以通过UpdateSourceTrigger属性实现,存在如下几种值



LostFocus :当控件失去焦点时触发,前面例子里

TextBox.Text默认就是这种形式的



PropertyChanged:当属性改变时触发



Explicit:这个就可以看作是需要显示调用了,需要主动取调用相应的UpdateSource方法才可以触发



用法可以像这样“{Binding FirstName,UpdateSourceTrigger=PropertyChanged }”

4)      不抛出异常,这一点对于开发及测试人员来说可能并不怎么好。



当一个数据Binding失败是,程序运行是不受影响的,只是相应的属性值为空了,对于开发人员来讲只能通过VS Debug时的输出窗口看到Binding失败的调试信息。



而对于测试人员来说那就只能是靠肉眼了。



具体内容可以参考MSDN中的” Data Binding Overview”这篇文章,这里只是为大家实现一个引路的工作



现在再试试应该已经可以实现ListView与TextBox的联动了,不过此时,对数据的改动还无法更新到数据库中。



需要继续修改。



1)      在button1_Click事件中增加一条语句用于接受书籍更新sda.Update(dt);

2)      在XAML设计区,最顶层Window元素中增加属性,Closed="Window_Closed",然后在后台实现相应的方法代码如下



两个方法具体代码如下,由于与WPF关系不大,因此不做讲解了:



private void button1_Click(object sender, RoutedEventArgs e)

        {

            sda.Update(dt);

            getData();

        }

        private void Window_Closed(object sender, EventArgs e)

        {

            sda.Update(dt);

        }

分享到:
评论

相关推荐

    第一个WPF程序

    在本教程中,我们将深入探讨如何创建你的**第一个WPF程序**,这将帮助WPF新手快速入门。 首先,让我们了解**建立WPF项目**的基本步骤。在Visual Studio中,选择"新建项目",然后在模板列表中找到"C# → Windows → ...

    VS2010 教程:创建一个 WPF 应用程序

    本教程旨在指导读者使用 VS2010 Beta 1 创建一个 WPF 应用程序。首先,读者需要安装完整的 VS2010 产品,包括 TFS。然后,创建一个新的 WPF 应用程序项目,命名为 PicViewer。这个项目将包括一个表格窗体和默认生成...

    如何搭建新的WPF项目框架

    下面就WPF项目框架搭建步骤一步一步的分享给大家。 在WPF项目开发中最常用的...第一步: 利用反射创建VM构造器   public class ViewModelFactory { private static Dictionary&lt;string&gt; vmMap = new Dictionary&lt;st

    WPF非常精美界面 WPF漂亮首页界面 WPF精美首页可以直接使用在项目当中 WPF入门布局的完美Demo 经典WPF快速入门漂

    - `Sample1`: 可能是一个示例项目的名称,其中包含了实际的WPF应用代码。 - `packages`: 这个目录可能包含项目所依赖的NuGet包,如LiveCharts等第三方库。 综上所述,这个压缩包提供了一个全面的学习资源,涵盖了...

    WPFGrowlNotification, WPF项目的轻量级咆哮通知.zip

    1. 安装:首先确保你的项目已经安装了.NET Framework 4.0或更高版本,因为WPF Growl通常基于这个框架。然后,你可以通过NuGet包管理器搜索并安装WPFGrowlNotification库,或者手动将源代码添加到项目中。 2. 引用:...

    WPF 基础视频教程(共50集)-3.只使用代码创建WPF应用程序

    创建WPF项目 首先,你需要在Visual Studio中创建一个新的WPF应用程序项目。选择"文件" -&gt; "新建" -&gt; "项目",然后在模板中找到"C# Windows" -&gt; "WPF应用程序"。给项目命名后,Visual Studio会自动生成一个基本的...

    使用WPF实现流程图的创建

    在提供的`WpfDiagram-master`压缩包中,很可能是包含了一个示例项目,展示了如何使用WPF实现流程图的创建。通过研究该项目,你可以看到上述知识点的具体实现,包括代码结构、数据模型、UI设计和事件处理等方面。 总...

    在wpf中创建一个指定延迟的闪屏

    首先,我们需要创建一个新的WPF项目。在Visual Studio中,选择“新建项目”,然后在模板列表中找到“WPF应用”并点击“下一步”。为项目命名并选择保存位置,然后点击“创建”。 接着,我们需要设计闪屏窗口。在...

    2个wpf开源界面库(wpftoolkit mordenUI)

    WPFToolKit是由Xceed软件公司开发并维护的一个强大的WPF组件集合,它包含了一系列对微软基础WPF控件的扩展和增强。这个库最初是CodePlex上的一个项目,后来被Xceed接手并持续更新,提供了一系列实用的控件和工具,如...

    利用PB.net创建WPF应用教程第1部分

    ### 利用PB.net创建WPF应用教程第1部分 #### 知识点解析: **一、PowerBuilder .NET(PB12)简介** PowerBuilder .NET,也称为PB12,是PowerBuilder系列的一个重要版本,计划在2010年上半年发布,并已经进入了...

    wpf实现拖拽效果 把一个listbox中的一条数据放到另外一个listbox中

    在这个场景中,我们的源是第一个ListBox,目标是第二个ListBox。 要实现这个功能,我们需要遵循以下步骤: 1. **启用拖放支持**: - 首先,我们需要在两个ListBox控件上启用拖放支持。可以通过设置`AllowDrop`...

    wpf大屏看板全部源码

    WPF是.NET Framework的一部分,它为开发者提供了丰富的用户界面设计工具,能够创建出美观且功能强大的桌面应用程序。本项目着重于大屏展示的应用场景,通常用于数据可视化、监控中心或者报告展示等用途。 WPF大屏...

    WPF 远程桌面连接

    例如,使用SharpRDP,你可以创建一个自定义控件,如下: ```csharp using SharpRDP; public partial class RDPControl : UserControl { public RDPControl() { InitializeComponent(); RDP rdp = new RDP(this...

    WPF中checkbox使用

    在WPF的用户界面XAML文件中,定义一个Canvas,并创建一个ItemsControl来显示Checkbox。ItemsControl将遍历数据源,并为每个Department创建一个Checkbox。 ```xml Canvas.Left="{Binding X...

    WPF和Silverlight项目设计实例光盘源代码3

    1.11WPF电子相册:这个项目可能演示了如何使用WPF来创建一个功能齐全的电子相册应用,涵盖了图像显示、浏览和管理等功能。WPF的控件库和数据绑定机制在此类项目中起着关键作用。 1.12WPF电子钟:这是一个展示WPF...

    WPF颜色选择器

    要添加颜色选择器到你的WPF项目,首先需要在XAML文件中声明控件。例如: ```xml ``` 这里我们使用了Telerik的`RadColorPicker`控件。当然,如果你没有使用Telerik库,可以考虑使用自定义控件或者模拟颜色选择器...

    WPF实现多屏幕显示

    要创建一个主屏幕(主程序),我们需要定义一个WPF窗口类,这通常是一个继承自`System.Windows.Window`的类。在这个窗口类中,我们将编写业务逻辑和UI布局。XAML文件用于描述UI的视觉元素,如控件、布局和样式。 接...

    wpf进度条,wpf等待条,wpf loading

    在实际开发中,为了使用这个库,开发者需要将`LoadingIndicators.WPF` 文件夹添加为项目的引用,然后在XAML文件中引入相应的命名空间,并选择合适的等待条或进度条控件。通过设置控件的属性(如颜色、大小、速度等)...

    WPF_Themes_HR.zip_-csdn_WPF_wpf 主题_wpf主题_wpf项目

    这些主题提供了丰富的设计元素和控件外观,可以方便地应用到各种WPF项目中,以提高开发效率并增强用户体验。 WPF(Windows Presentation Foundation)是微软.NET Framework的一部分,它提供了丰富的图形层、媒体...

    wpf经典代码集合 wpf经典学习笔记 wpf从菜鸟到精通

    通过阅读教程,开发者可以系统地学习WPF,从创建第一个窗口到实现复杂的交互功能。 综上所述,"wpf经典代码集合 wpf经典学习笔记 wpf从菜鸟到精通" 提供了一套完整的WPF学习资源,无论你是刚接触WPF的新手,还是...

Global site tag (gtag.js) - Google Analytics