WPF 初识

时间:2021-10-17 20:05:51

1.WPF 与Winform比较

1.1、WPF所有的操作都不依赖于GDI和GDI+,而是间接依赖于强大的Direct3D,这就意味着通过WPF可以做出以前WinFrom无法想象的视觉效果,包括3D效果的应用程序。

1.2、WPF实现彻底把程序架构,业务逻辑和用户界面(UI)彻底分开,WPF引擎把XAML描述的UI元素解释为相应的.NET对象,从而在应用程序创建相应的控件,UI人员和程序人员均可对此控件进行编辑加载,从而实现用户界面和程序架构的彻底分离,而Winform这一点是做不到的。

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

优点:

1、wpf这个ui框架是很前沿的,包含了很多界面开发的特性,非常灵活,因为个人做界面开发比较多,包括web,iOS,android都接触过,wpf是我认为目前设计的最好的ui界面库
2、好的界面库有好的ide支持,vs的好用大家都懂得
3、一个功能多种实现方案,虽说灵活但实际对开发人员的要求更高,需要有更好的设计能力,追求优雅设计,MVVM模式讲究让界面代码尽量xaml化
4、强大的.net类库支持,ms平台的特性一向是入门简单,精通难,而且方案更新快,开发人员都比较依赖微软更新,开源力量还不够
5、基于DirectX渲染,渲染效率好,确实有很多优点
缺点:

1、内存占用高,基于.net的反射机制和wpf的内部机制,你想把控内存真心是难,只能一定程度控制,c++就不一样,内存是可控的
2、功能太多导致学习成本非常高,传统开发理念需要转换MVVM,但由于控件支持不完善,会出现不伦不类的情况
3、平台推广不利,这个才是最大原因,可以说是本人在.net多年学了很多,写代码是需要思考才能积累经验的,这种模式不适合互联网的快递跌代节奏,市场从业的机会太少,导致发展缓慢
4、功能不够完善,ms的客户端技术方案太多了,wpf只是其中一种,本来wp是一个机会,奈何wp又推不起来,个人感觉wp开发功能较wpf缩水厉害,后面又为silverlight加了很多轻量技术方案然后引入wpf,搞得不伦不类,已经把平台分为wpf,sl,wp,win 8 morden ui
5、平台真心混乱 自从 win8出来以后,我看到了win 8 morden ui,看了下sdk,又是一套api,感觉又是一个坑,果断决定放弃了,因为平台没有市场,去学习毫无意义,太多时间耗费在.net平台上了,感觉微软的策略真心是有问题,也得出一个结论,微软是一个比较喜欢玩技术的公司,跟着玩你会发现真的被玩了,现在看ios开发,反而感觉设计的好简单暴力,有时候给太多技术方案给开发人员不是一个好事情

最后总结
1、wpf技术是个好东西,是界面开发技术的精华,值得学习
2、wpf不是一切,是界面开发的一种方案,PC客户端开发坑很多,精通的话门槛很好,广会.net是不够的,底层得学
3、平台推广不给力是关键

作者:匿名用户
链接:https://www.zhihu.com/question/26862663/answer/76386239
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
2.WPF 中XAML界面设计

一、基本语法

同XML类似,XAML中最基本的语法元素就是标签、属性、内容。

2.1、标签

标签是通常是以<>开始,以</>结束的,一个标签的声明通常表示一个对象。如<Window></Window>、<Grid></Grid>分别定义了一个窗体对象及一个Grid对象,标签定义有两种常用写法:

非自闭合签:<Window></Window>、<Grid></Grid>

自闭合标签:< Window />、 <Grid/>这种自闭合标签用于无内容情况下,可以让代码看上去更简洁,当然,正常情况下Window及Grid都是有内容的。

2.2、属性

属性通常以键值对形式出现,如<Window><Window/>标签中的Title="MainWindow" Height="350" Width="525",等号左边表示Window标签的属性,等号右边表示该属性的值。

2.3、内容

一组标签对之间夹杂的文本或其他标签都称为这个标签之间的内容。此处Window标签的内容就是一个<Grid><Grid/>标签

二、命名空间

细心的朋友肯定会问<Window>标签里的

 x:Class="WpfExam.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

这些奇奇怪怪的类似网站地址的是什么东东,难不成也是属性?没错,它们的确算是Window的属性,但他们又有自己独特的含义,基本上我们在WPF中新建任何窗体、用户控件,这三个属性都会出现。我们分别介绍这三个属性的是什么意思。

x:Class="WpfExam.MainWindow":这里指定了我们XAML窗体界面对应的C#类,是WpfExam命名空间下的MainWindow这个分部类。

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation":表示引用wpf界面表现相关的命名空间,类似于我们C#类中的using。

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml":表示引用xaml相关的命名空间。

这个xmlns:x中的x只是一个默认的标识符,如果我们将他改成y的话,编译我们的程序,将会报错找不到属性Class,此时我们就需要将x:Class="WpfExam.MainWindow"修改为y:Class="WpfExam.MainWindow"。

同样,如果我们将xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"也增加一个标示符号的话,假如我们改成xmlns:a="http://schemas.microsoft.com/winfx/2006/xaml/presentation",那么将会提醒你找不到类型

Window、Grid,你需要标签<Window>、<Grid>替换为<a:Window>、<a:Grid>

3.WPF控件基础

3.1 WPF控件简介

通过上一篇XAML语言的介绍,我们知道,XAML是一个树形结构,同样,WPF控件作为构成整个XAML树的一部分,也是一个树形结构。我们看一个简单的例子。

WPF 初识
<Button.Content>
<DockPanel>
<Image Source="F:\01.Code\01.MyCode\WpfApplication1\WpfApplication1\Resources\荷.jpg" Width="30"/>
<TextBlock Text="红色" Foreground="Red" VerticalAlignment="Center"></TextBlock>
<TextBlock Text="绿色" Foreground="Green" VerticalAlignment="Center"/>
<TextBlock Text="蓝色" Foreground="Blue" VerticalAlignment="Center"/>
</DockPanel>
</Button.Content>
WPF 初识

界面展现效果如图:

WPF 初识

没有接触过WPF的朋友可能会惊讶,一个button里怎么会有那么多内容?我们可以通过VS的对象浏览器查看Button类的继承关系为:

Button->ButtonBase->ContentControl->Control->FrameworkElement。

FrameworkElement是所有WPF界面元素的展现基类(FrameworkElement向上继承自UIElement->Object),Button继承ContentControl,使得Button具备了一个容器控件应有的特性,即允许Button拥有一个Content,而这个Content可以为另一个容

器或控件,至于里面具体是什么,完全取决于你想要什么。这也正是WPF的魅力所在,任何控件都是一个树形结构,且树形结构的每一级都可以*定制,理论上我们可以自定义任何一个层级的样式,来实现任何我们想要的界面展现效果。

3.2 WPF控件分类

上面我们讲到了Button继承自ContentControl,应该说是属于一个内容控件,下面我们系统的对WPF控件进行一下分类,按照我自己的理解,我们有两种分类方法。

一、按照控件的继承特性分类

我们先看一下整个WPF界面元素类之间的继承关系:

WPF 初识

  • Panel:布局控件,包括Canvas、DockPanel、StackPanel等,可以容纳多个基本控件(Control)或者嵌套其他布局控件。用于整体界面的布局,Panel下的控件我们会在下一篇讲解。
  • Control:基本控件
  • ContentControl:内容控件,其内容只能是一个基本控件或布局控件,通常使用一个布局控件作为其Content,然后在布局空间里使用多个基本空间,从而实现复杂的界面效果,其下基本控件有Label、Button、Tooltip。
  • HeaderContentControl:带标题的内容控件,继承自ContentControl,在ContentControl的基础上,增加了一个Header属性。其下基本控件有TabItem、GroupBox等
  • ItemsControl:多条目控件,其下基本控件有ListBox、Menu等
  • HeaderItemsControl:继承自ItemsControl,增加了Header属性,其下控件有MenuItem、TreeViewItem、ToolBar等
  • TextBox:基本文本输入控件。
  • TextBlock、Image:直接继承自FrameworkElemet基类,属于最普通的基本控件。

二、按照控件的感官特性分类

这里的感官特性我定义的是,站在一个普通用户的角度上,能看到、感知到的东西。这里讲WPF控件分为两类:

1、 基本控件:用户可以看到并与之交互的控件,如按钮、输入框等,主要涵盖上述的Control、TextBlock、Image部分

2、 布局控件:用户察觉不到的,却对这个系统界面布局有着决定性作用的容器类控件,主要涵盖上述Panel及其子类部分

文章在以后的描述中将以这种分类方法进行阐述。

3.3 WPF控件属性

通过上面的介绍,我们知道所有的控件(基本控件、容器控件)都继承自FrameworkElement这个基类,这个基类里定义了WPF控件用到的大部分属性,我们这里先对这些WPF控件都具有的属性进行介绍(常用的标红),后续分别介绍常用基本控件时再对独有的属性进行介绍。

  • ActualHeight:获取此元素的呈现高度。
  • ActualWidth:获取次元素的呈现宽度。
  • BindingGroup:获取或设置用于该元素的 System.Windows.Data.BindingGroup
  • ContextMenu:获取或设置该元素的上下文菜单,通常是邮件菜单。
  • Cursor:获取或设置当鼠标指针悬停在此元素上时显示的光标
  • DataContext:获取或设置元素参与数据绑定时的数据上下文。
  • DefaultStyleKey:在使用或定义主题样式时,获取或设置用于引用此控件的样式的键。
  • FlowDirection:获取或设置文本和其他UI元素在控制它们布局的任何父元素中的流动方向。是一个枚举值。默认值为 System.Windows.FlowDirection.LeftToRight
  • FocusVisualStyle:获取或设置一个属性,该属性支持自定义将在此元素捕获键盘焦点时应用于此元素的外观、效果或其他样式特征
  • ForceCursor:获取或设置一个值,该值指示此元素是否应该强制UI按照Cursor属性所声明的方式呈现光标。
  • Height:获取或设置元素的建议高度。
  • HorizontalAlignment:获取或设置在父元素(如面板或项控件)中构成此元素时应用于此元素的水平对齐特征。
  • InputScope:获取或设置此元素使用的输入上下文
  • IsInitialized:获取一个值,该值指示此元素是否已初始化。
  • IsLoaded:获取一个值,该值指示是否已加载此元素以供呈现。
  • Language:获取或设置适用于某个元素的本地化/全球化语言信息
  • LayoutTransform:获取或设置在执行布局时应该应用于此元素的图形转换方式。
  • LogicalChildren:获取此元素的逻辑子元素的一个枚举器。
  • Margin:获取或设置元素的外边距。认值是所有属性都等于 0(零)的 System.Windows.Thickness。
  • MaxHeight:获取或设置元素的最大高度约束。
  • MaxWidth:获取或设置元素的最大宽度约束。
  • MinHeight:获取或设置元素的最小高度约束。
  • MinWidth:获取或设置元素的最小宽度约束。
  • Name:获取或设置元素的标识名称。该名称提供一个引用,以便当 XAML 处理器在处理过程中构造标记元素之后,后台代码可以对该元素进行引用。
  • OverridesDefaultStyle:获取或设置一个值,该值指示此元素是否合并了主题样式中的样式属性。
  • Parent:获取此元素的逻辑父级元素。
  • Resources:获取或设置本地定义的资源字典。
  • Style: 获取或设置此元素在呈现时使用的样式。
  • Tag: 获取或设置一个可用于存储有关此元素的自定义信息的任意对象值。
  • TemplatedParent: 获取一个对此元素的模板父级的引用。如果此元素不是通过模板创建而成,则此属性并不相关
  • ToolTip: 获取或设置在UI中为此元素显示的工具提示对象。
  • Triggers: 获取直接在此元素上建立或在子元素中建立的触发器的集合。
  • VerticalAlignment:获取或设置在父元素(如面板或项控件)中组合此元素时应用于此元素的垂直对齐特征。
  • VisualChildrenCount: 获取此元素内的可视化子元素的数目。
  • Width: 获取或设置元素的宽度。

       其中读写属性(带有获取或设置描述的属性)可以在XAML中使用,只读属性(只有获取描述的属性)只能在后台代码中使用。

4.WPF 中底层框架MVVM设计

View 和ViewModel 使用的是双向的数据属性

View 想把操作传到ViewModel ,它使用的是单向的命令属性

WPF 初识

数据传输的数据属性

NotificationObject是ViewModel的基类

如果ViewModel属性借助Binding关联到界面上某个控件上,当这个值变化时候, Binding监听PropertyChanged这个事件有没有发生,一旦这个事件发生它将变化后的值送到界面上去。

Class NotificationObject:INotifyPropertyChanged

{

Public event PropertyChangedEventHandler PropertyChanged;

Public void RaisePropertyChange(string propertyName

{

  if(this.PropertyChanged!=null){

    this.PropertyChanged.Invoke(this,new PropertyChangedEventArgs(propertyName)); //告诉Data Binding 哪个属性值发生变化

  }

}

}

操作传输的命令属性

Class DelegateCommand:ICommand{

  public bool CanExecute(object parameter){

  if(this.CanExecuteFunc == null){

  return true;

  }

  this.CanExecuteFunc(parameter);

  } //这个方法用来帮助命令的呼叫者判断这个命令能不能执行

  public event EventHandler CanExecuteChanged; //当这个命令能不能执行这个状态发生改变的时候,有机会通知一下命令的调用者,告诉它状态change了

  public void Execute(object parameter){

   if(this.ExecuteAction == null){

   return;

   }

   this.ExecuteAction(parameter)

  }//命令执行的时候,你想做什么事情

  public Action<object> ExecuteAction{get;set;}//接受object类型Action

  public Func<object,bool> CanExecuteFunc{get;set;}//接受object类型,返回bool类型Func

}

public DelegateCommand SaveCommand{get;set;}

private void Save(object parameter){

  SaveFileDialog dlg= new SaveFileDialog();

  dlg.ShowDialog();

}

public MainWinowViewModel(){

  this.SaveCommand = new DelegateCommand();

  this.SaveCommand.ExecuteAction = new Action<object>(this.Save);

}

5.WPF 中async await - 性能开销和其他陷阱

1.避免对(耗时)短的方法使用async/await,避免在小循环内使用await语句(可以将整个循环放到一个async方法内)

2.忽略结果:忘记await一个函数返回的任务,将会导致乱序执行。

3.避免混合使用阻塞和非阻塞代码,这可能会导致死锁,更加复杂的错误处理以及上下文线程意外阻塞。

 所以在下面文章中防止死锁采用最好的方法是方案2,因为在方法中添加await调用,达到Context实际上永远不会被Block,所有的await都是asynchronous waits

 参照文章:http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html