0,Task线程:超时取消,等待,暂停,继续,取消

  1. /// <summary>
  2. /// 超时取消
  3. /// </summary>
  4. public void TestTaskTimeOutCancel()
  5. {
  6. CancellationTokenSource tokenSource = new CancellationTokenSource();
  7. Task.Factory.StartNew(() => {
  8. while( ! tokenSource.IsCancellationRequested)
  9. {
  10. Console.WriteLine(DateTime.Now);
  11. Thread.Sleep(1000);
  12. }
  13. }, tokenSource.Token);
  14. Console.WriteLine("Press any key to cancle ");
  15. Console.ReadLine();
  16. tokenSource.Cancel();//取消操作
  17. Console.WriteLine("Done");
  18. Console.ReadLine();
  19. //很多时候,除了像上例中的那样手动取消外,我们往往也要对任务设置一个预期执行时间,
  20. //对超时的任务自动取消。之前一般做法是新启动一个计时器,
  21. //在计时器的超时回调中执行CancellationTokenSource.Cancel方法。
  22. //在.Net 4.5中,该操作得到了进一步的简化,
  23. //我们可以通过在创建CancellationTokenSource时设置超时来实现这一功能。
  24. var cancelTokenSource = new CancellationTokenSource(3000);
  25. //除此之外,也可以通过如下代码实现同样的效果。
  26. cancelTokenSource.CancelAfter(3000);
  27. }
  28. /// <summary>
  29. /// Task线程 启动,等待,暂停,取消
  30. /// </summary>
  31. public void TestTaskPasueCancelContinue()
  32. {
  33. CancellationTokenSource tokenSource = new CancellationTokenSource();
  34. CancellationToken token = tokenSource.Token;
  35. ManualResetEvent resetEvent = new ManualResetEvent(true);
  36. //2.定义Task
  37. Task task = new Task(async () => {
  38. while (true)
  39. {
  40. if (token.IsCancellationRequested)
  41. {
  42. return;
  43. }
  44. // 初始化为true时执行WaitOne不阻塞
  45. resetEvent.WaitOne();
  46. // Doing something.......
  47. // 模拟等待100ms
  48. await Task.Delay(100);
  49. }
  50. }, token);
  51. task.Start(); //启动线程
  52. resetEvent.Reset();//暂停Task
  53. resetEvent.Set(); //继续Task
  54. tokenSource.Cancel(); //线程取消
  55. }

0,Socket 通信
WPF知识要点 - 图1

  1. public class SocketServer
  2. {
  3. private string _ip = string.Empty;
  4. private int _port = 0;
  5. private Socket _socket = null;
  6. private byte[] buffer = new byte[1024 * 1024 * 2];
  7. /// <summary>
  8. /// 构造函数
  9. /// </summary>
  10. /// <param name="ip">监听的IP</param>
  11. /// <param name="port">监听的端口</param>
  12. public SocketServer(string ip, int port)
  13. {
  14. this._ip = ip;
  15. this._port = port;
  16. }
  17. public SocketServer(int port)
  18. {
  19. this._ip = "0.0.0.0";
  20. this._port = port;
  21. }
  22. public void StartListen()
  23. {
  24. try
  25. {
  26. //1.0 实例化套接字(IP4寻找协议,流式协议,TCP协议)
  27. _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  28. //2.0 创建IP对象
  29. IPAddress address = IPAddress.Parse(_ip);
  30. //3.0 创建网络端口,包括ip和端口
  31. IPEndPoint endPoint = new IPEndPoint(address, _port);
  32. //4.0 绑定套接字
  33. _socket.Bind(endPoint);
  34. //5.0 设置最大连接数
  35. _socket.Listen(int.MaxValue);
  36. Console.WriteLine("监听{0}消息成功", _socket.LocalEndPoint.ToString());
  37. //6.0 开始监听
  38. Thread thread = new Thread(ListenClientConnect);
  39. thread.Start();
  40. }
  41. catch (Exception ex)
  42. {
  43. }
  44. }
  45. /// <summary>
  46. /// 监听客户端连接
  47. /// </summary>
  48. private void ListenClientConnect()
  49. {
  50. try
  51. {
  52. while (true)
  53. {
  54. //Socket创建的新连接
  55. Socket clientSocket = _socket.Accept();
  56. clientSocket.Send(Encoding.UTF8.GetBytes("服务端发送消息:"));
  57. Thread thread = new Thread(ReceiveMessage);
  58. thread.Start(clientSocket);
  59. }
  60. }
  61. catch (Exception)
  62. {
  63. }
  64. }
  65. /// <summary>
  66. /// 接收客户端消息
  67. /// </summary>
  68. /// <param name="socket">来自客户端的socket</param>
  69. private void ReceiveMessage(object socket)
  70. {
  71. Socket clientSocket = (Socket)socket;
  72. while (true)
  73. {
  74. try
  75. {
  76. //获取从客户端发来的数据
  77. int length = clientSocket.Receive(buffer);
  78. Console.WriteLine("接收客户端{0},消息{1}", clientSocket.RemoteEndPoint.ToString(), Encoding.UTF8.GetString(buffer, 0, length));
  79. }
  80. catch (Exception ex)
  81. {
  82. Console.WriteLine(ex.Message);
  83. clientSocket.Shutdown(SocketShutdown.Both);
  84. clientSocket.Close();
  85. break;
  86. }
  87. }
  88. }
  89. }
  1. public class SocketClient
  2. {
  3. private string _ip = string.Empty;
  4. private int _port = 0;
  5. private Socket _socket = null;
  6. private byte[] buffer = new byte[1024 * 1024 * 2];
  7. /// <summary>
  8. /// 构造函数
  9. /// </summary>
  10. /// <param name="ip">连接服务器的IP</param>
  11. /// <param name="port">连接服务器的端口</param>
  12. public SocketClient(string ip, int port)
  13. {
  14. this._ip = ip;
  15. this._port = port;
  16. }
  17. public SocketClient(int port)
  18. {
  19. this._ip = "127.0.0.1";
  20. this._port = port;
  21. }
  22. /// <summary>
  23. /// 开启服务,连接服务端
  24. /// </summary>
  25. public void StartClient()
  26. {
  27. try
  28. {
  29. //1.0 实例化套接字(IP4寻址地址,流式传输,TCP协议)
  30. _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  31. //2.0 创建IP对象
  32. IPAddress address = IPAddress.Parse(_ip);
  33. //3.0 创建网络端口包括ip和端口
  34. IPEndPoint endPoint = new IPEndPoint(address, _port);
  35. //4.0 建立连接
  36. _socket.Connect(endPoint);
  37. Console.WriteLine("连接服务器成功");
  38. //5.0 接收数据
  39. int length = _socket.Receive(buffer);
  40. Console.WriteLine("接收服务器{0},消息:{1}", _socket.RemoteEndPoint.ToString(), Encoding.UTF8.GetString(buffer,0,length));
  41. //6.0 像服务器发送消息
  42. for (int i = 0; i < 10; i++)
  43. {
  44. Thread.Sleep(2000);
  45. string sendMessage = string.Format("客户端发送的消息,当前时间{0}", DateTime.Now.ToString());
  46. _socket.Send(Encoding.UTF8.GetBytes(sendMessage));
  47. Console.WriteLine("像服务发送的消息:{0}", sendMessage);
  48. }
  49. }
  50. catch (Exception ex)
  51. {
  52. _socket.Shutdown(SocketShutdown.Both);
  53. _socket.Close();
  54. Console.WriteLine(ex.Message);
  55. }
  56. Console.WriteLine("发送消息结束");
  57. Console.ReadKey();
  58. }
  59. }
  1. namespace SocketServerApp
  2. {
  3. class Program
  4. {
  5. static void Main(string[] args)
  6. {
  7. SocketServer server = new SocketServer(8888);
  8. server.StartListen();
  9. Console.ReadKey();
  10. }
  11. }
  12. }
  13. namespace SocketClientApp
  14. {
  15. class Program
  16. {
  17. static void Main(string[] args)
  18. {
  19. SocketClient client = new SocketClient(8888);
  20. client.StartClient();
  21. Console.ReadKey();
  22. }
  23. }
  24. }

1,跨线程操作(Dispatcher)多线程(异步编程和async/await)

2,template(模板类型【控件模板、数据模板、面板模板】、逻辑树【UI界面的组成元素】、可视化树【逻辑树的扩展版本,将元素分成更小的部分】)
WPF中模板是用于定义或重定义控件结构,或者说对象的外观。
WPF中模板有两类,
一个是控件模板(ControlTemplate)
一个是数据模板(DataTemplate),它们都派生自FrameworkTemplate抽象类。

总共有三大模板 ControlTemplate,ItemsPanelTemplate,DataTemplate。
1 ControlTemplate 主要用途是更改控件的外观。它有两个重要属性:VisualTree(视觉树)内容属性和Triggers触发器,对于触发器可以不用过多考虑,触发器可有可无。VisualTree就是呈现我们所画的控件。Triggers可以对我们的视觉树上的元素进行一些变化。
2 ItemsPanelTemplate 是个特殊的空间模板,主要用来标明多条目控件如何显示它所包含的多项数据。也可以说是指定用于项的额布局的面板。多用于多个内容控件的目标。多为Panel属性或者Panel结尾的属性。
3 DataTemplate 主要用于数据的呈现。也被称为显示绑定数据对象的模板。

3,binding(绑定源、绑定模式【default、OneWay、TwoWay、OntTime、OneWayToSource】、触发绑定更新的事件【Default、Explicit(手动BindingExpression.UpdayeSource())、PropertyChange、LostFocus】、优先级PriorityBinding)

4,trigger(4种,属性触发器,数据触发器,事件触发器,多条件触发器)
属性触发器







数据触发器

事件触发器








5,style
属性值的集合,能被应用到一个合适的元素中,或者说能将一组属性应用到多个元素;
样式也支持触发器,通过属性的改变,触发一组活动,包括改变某个控件的样式。
WPF中元素只能使用一个样式。
样式有继承的特性,样式可以继承样式。

6, Behavior 行为
行为是一类事物的共同特征,在WPF中通过行为可以封装一些通用的界面功能,从而实现代码重用来提高开发效率, System.Windows.Interactivity.dll文件。

7,convert转换器
将源数据和目标数据之间进行特定的转化,需要继承接口IValueConverter
Convert:会进行源属性传给目标属性的特定转化
ConvertBack:会进行目标属性传给源属性的特定转化

8,visual、 uielement、 frameworkelement、 control
依次的继承关系是:
Visual继承UIElement,UIElement继承FrameworkElement,FrameworkElement继承Control。
1 Visual主要作用是为WPF提供2D呈现支持,主要包括输出显示,坐标转换,区域剪切等。
2 UIElement的主要作用是构建WPF元素和基本呈现特征的基类。例如其中定义很多与输入和焦点有关的特性,例如键盘事件,鼠标,还有一些与WPF事件模型有关的API。
3 FrameworkElement的主要作用是为定义的WPF元素添加一些功能。例如,布局定义 逻辑树 对象生命周期事件 支持数据绑定和动态资源引用 支持样式和动画。
4 Control的主要作用是为自定义应用程序控件提供基础。因为它是创建自定义应用程序控件的基类,作用就是可以重写Control类所提供的属性,方法,事件等,为自定义控件添加自定义逻辑。构建WPF应用程序页面的Window类也派生自它。

9,用户控件(将控件组合成一个新控件) 自定义控件(重新制造一个控件)
用户控件(组合)

  • 将多个现有的控件组合成一个可重用的“组”。
  • 由一个XAML文件和一个后台代码文件。
  • 不能使用样式和模板。
  • 继承自UserControl类。

自定义控件(扩展)

  • 在现有的控件上进行扩展,增加一些新的属性方法等。
  • 包括一个代码文件和一个默认的主题文件。
  • 可以使用样式和模板。
  • 构建控件库的好方法。

10,依赖属性 附加属性
优点、定义【属性是类私有字段的封装,wpf中使用属性对依赖属性进行封装】、优先级、继承、附件属性、验证和强制、监听)
依赖属性(Dependency Property),就是一种可以自己没有值,但能通过 Binding 从数据源获得值(依赖在别人身上)的属性。拥有依赖属性的对象被称为“依赖对象”
附加属性(Attached Properties)是说一个属性本来不属于某个对象,但由于某种需求而被后来附加上。也就是说把对象放入一个特定环境后对象才具有的属性(表现出来就是被环境赋予的某种属性)

13,prism(一个MVVM框架,依赖IOC容器)

14,dispatcher
跨线程操作

15,资源Resources,StaticResource/DynamicResource,
静态资源在引用对象初始化时一次性设置完毕;对于动态资源、如果发生了改变则会重新应用资源

19,DataSet、DataCommand、DataAdapter
DataAdapter对象是用来读取数据库.可读取写入数据,某条数据超着强,但它占用内存比dataReader大,速度慢,一般和DataSet连用.
DataReader 连接数据库时是面向连接的。读表时,只能向前读取,读完数据后由用户决定是否断开
Dataset表示一个数据集,是数据在内存中的缓存。 可以包括多个表
DataSet 连接数据库时是非面向连接的。把表全部读到Sql中的缓冲池,并断开于数据库的连接
DataAdapter对象是用来读取数据库,可读取写入数据;但他占用内存比DataReader大,速度慢
DataAdapter,使用DataReader从数据源读取数据并Add到DataSet保存起来。实际上我们从数据库获得数据都会通过DataReader,只不过DataAdapter把这一切都封装起来了。

SqlDataReader 高效,功能弱,只读访问
SqlDataAdapter 强大,要求资源也大一点
SqlDataReader 只能在保持跟数据库连接的状态下才可以读取。。。
SqlDataAdapter 大多情况下是一次性读取一个表,然后填充到DataSet中,然后就可以断开跟数据库的连接了。
因为DataSet是离线的,所以SqlDataAdapter这个对象是连接DataSet和数据库的桥梁,所有对DataSet的操作(填充,更新等)都要通过他

ado.net数据访问有两种方式:
1.离线--通过DataSet,然后离线增,删,改,最后通过SqlDataAdapter解 析到数据库中
2.直接对数据库操作SqlCommand (Update,Insert,Delete)

SqlCommand就是是命令了,可以用它来执行SQL命令
SqlDataAdapter就是数据适配器了,它是用于在数据源和数据集之间通讯的一组对象
SqlCommand对应DateReader
SqlDataAdapter对应DataSet
[

](https://blog.csdn.net/jiowei/article/details/8208903)
20,引用传递 ref out
ref和out都可传出参数,out参数可为空,且在函数中必须赋值

21,线程同步、异步、Task
同步和异步主要用于修饰方法。
当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,是同步方法;
当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务,调用者不用等待该方法执行完毕,为异步方法。

异步的好处在于非阻塞(调用线程不会暂停执行去等待子线程完成),因此我们把一些不需要立即使用结果、较耗时的任务设为异步执行,可以提高程序的运行效率。
ThreadPool,Task,async/await

线程同步:解决多个线程同时操作一个资源的问题,多个线程对于资源的使用,必须等所有的线程使用完成之后再进行处理
while(th1.IsAlive); 会大量消耗cpu空转,可以改成th1.Join()就是让当前线程等待th1线程的结束
线程同步:实现:
(1):Join
原理,等待耗时最长的线程执行完成之后在处理公共资源,给耗时最长的线程调用Join方法
(2):lock
改用lock解决多个线程同时操作一个资源。lock是C#中的关键字,他要锁定一个资源
lock的特点是:同时只能有一个线程进入lock的对象的范围,其他lock的线程就要等。
(3):注意事项
注意 lock 要锁定同一个对象,而且必须是引用类型的对象

22, 线程 和 进程
在操作系统中,每运行一个程序都会开启一个进程,一个进程由多个线程构成。
线程是程序执行流中最小的单元

  • 单线程程序是指在一个进程空间中只有一个线程在执行;
  • 多线程程序是指在一个进程空间中有多个线程在执行,并共享同一个进程的大小

23,委托 和 事件
delegate本意就是把函数当做参数来进行传递
delegate除了使用+=或-=来监听和移除方法,还可以用=,这样会不小心把监听列表都覆盖掉的。
event规范化了只能用+=和-=。

委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。
使用委托可以将多个方法绑定到同一个委托变量,当调用此变量时(这里用“调用”这个词,是因为此变量代表一个方法),可以依次调用所有绑定的方法。

事件本身是一个委托,c#编译后是一个私有委托,增加了add和remove方法。事件比委托有更深一层的控制,他规定了只有产生者的内部才可以调用。

22,abstract、virtual、new、override、sealed
一、Virtual方法(虚方法)
virtual 关键字用于在基类中修饰方法。virtual的使用会有两种情况:
1:在基类中定义了virtual方法,但在派生类中没有重写该虚方法。那么在对派生类实例的调用中,该虚方法使用的是基类定义的方法。
2:在基类中定义了virtual方法,然后在派生类中使用override重写该方法。那么在对派生类实例的调用中,该虚方法使用的是派生重写的方法。

二、Abstract方法(抽象方法
abstract关键字只能用在抽象类中修饰方法,并且没有具体的实现。抽象方法的实现必须在派生类中使用override关键字来实现。

三,接口和抽象类:
最本质的区别:抽象类是一个不完全的类,是对对象的抽象,而接口是一种行为规范。

23,动画(Timeline、StoryBoard)

  1. <Border.Triggers>
  2. <EventTrigger RoutedEvent="Window.Loaded">
  3. <BeginStoryboard>
  4. <Storyboard RepeatBehavior="Forever">
  5. <DoubleAnimation Duration="00:0:1" From="0" To="-360"
  6. Storyboard.TargetProperty="(RotateTransform.Angle)"
  7. Storyboard.TargetName="pathRotate"/>
  8. </Storyboard>
  9. </BeginStoryboard>
  10. </EventTrigger>
  11. </Border.Triggers>

WPF动画【线性插值&关键帧&路径】:
1、动画有3种类型:
①Animation线性插值动画:在开始值与结束值之间以逐步增加的方式改变属性的动画。 ②AnimationUsingKeyFrames关键帧动画:从一个值突然变成另一值的动画,所有关键帧动画都使用”类型名 + AnimationUsingKeyFrames”的形式命名。
③AnimationUsingPath路径动画:基于路径而创作的动画。

2、动画的构成:布局控件 - 事件触发器【Trigger】 - 开始播放故事板【BeginStoryboard】 - 故事板【Storyboard】 - 动画【Animation】。

3、Storyboard故事板:动画的基本单元,控制动画的播放、暂停、停止等基础操作,需要指定TargetName&TargetProperty这两个属性。

4、事件触发器:通过事件触发器来播放BeginStoryboard故事板的动画。

5、动画执行的两种方式:①通过设定触发器事件在页面执行动画。②后台托管执行动画。

6、Animation线性插值动画:
① 它提供一种简单的“渐变”动画,我们为一个Animation指定开始值与结束值,并指定由开始值到达结束值 所需时间,便可形成一个简单的动画。
②Animation支持两种属性的动画效果:DoubleAnimation属于Double类型的属性都可以使用它产生的线性插值动画;ColorAnimation作用于属性为Color类型对象的线性插值动画,用于改变对象的填充颜色。
③Animation动画设置属性

23,控件缩放、移动、翻转(Thumb)

24,DPI:单位长度的像素点
“分辨率无关”,WPF的分辨率单位(1/96 英寸)

25,解释什么是依赖属性,它和以前的属性有什么不同?为什么在WPF会使用它?
依赖属性(Dependency Property),就是一种可以自己没有值,但能通过 Binding 从数据源获得值(依赖在别人身上)的属性。拥有依赖属性的对象被称为“依赖对象”
不同:节约内存,依赖属性内部实现使用哈希表存储机制,对多个相同控件的相同属性的值都只保存一份
可支持WPF中的样式设置、数据绑定、继承、动画及默认值,而WPF主要是使用绑定机制,所以使用它

28,绑定(Binding )的基础用法
绑定对象 path

  1. Binding binding = new Binding("BindingPath");
  2. binding.Source = BindingSource;
  3. mButton.SetBinding(Button.ContentProperty, binding);

30,视觉树vs 逻辑树?
1 逻辑树是视觉树的子集,也就是视觉树基本上是逻辑树的一种扩展。
2 WPF通过逻辑树来解决依赖项属性继承和资源的问题,使用视觉树来处理渲染,事件路由,资源定位等问题。
3 逻辑树可以认为是XAML所见的,而视觉树包含了XAML元素内部的结构。
4 逻辑树的查找可以通过LogicalTreeHelper辅助类,视觉树的查找可以通过VisualTreeHelper辅助类,其中需要注意的是对ContentElement元素的查找,无法直接通过VisualTreeHelper进行查找,ContentElement元素并不继承Visual,而ContentElement元素的使用时需要一个ContentElement载体FrameworkContentElement。

31,属性变更通知(INotifyPropertyChange 和ObservableCollection)
INotifyPropertyChanged向客户端发出某一属性值更改的通知。
ObservableCollection类,它是实现 INotifyCollectionChanged 接口的数据集合的内置实现。表示一个 动态数据集合,在添加项、移除项或刷新整个列表时,此集合将提供通知

32,ResourceDictionary 资源字典
作用:一个应用程序中,某个窗口需要使用样式,但是样式非常多,写在一个窗口中代码分类不方便。最好 Style写在专门的xaml文件中,然后引用到窗口中,就像HTML引用外部css文件一样。

  1. 初衷:就在于可以实现多个项目之间的共享资源,资源字典只是一个简单的XAML文档,该文档除了存储希望使用的资源之外,不做任何其它的事情。

34,事件的三种方式(冒泡、直接、隧道)
1.路由事件是那些在可视树层次结构中向上或向下移动的事件。
WPF事件可以分为三种类型:
直接事件:-在本例中,事件在源处引发,并在源处本身处理,如“MouseEnter”事件。
冒泡事件:-它们在可视树层次结构中向上移动。例如,“MouseDown”是一个冒泡事件。
隧道事件:这些事件沿可视树层次结构下行。“PreviewKeyDown”是一个隧道事件。
2.WPF中的路由事件是沿着VisualTree传递的,作用是用来调用应用程序的元素树上的各种监听器上的处理程序。
(1)冒泡,这种事件处理方式是从源元素向上级流过去,直到到达根节点即顶层节点,一般为最外层的控件。
(2)直接,这种处理方式是在源上处理,主要用在源元素上处理。通常setter和trigger中有所体现,我个人认为VisualState可视状态可能也是直接事件处理,也是依赖属性的状态改变。和Trigger有一定的重复,但是VisualState是通过生硬的动画间接实现依赖属性的改变。
(3)隧道,又称作Preview事件,和冒泡事件处理方式相反的。元素树的根位置调用事件处理程序,依次向下直到源元素位置。
隧道事件和冒泡事件一般成对出现。同一事件,执行时首先是隧道事件,然后是冒泡事件。

Routed Events(路由事件) 与 Commands(命令)

Event 和 Command 是程序内部通信基础,Routed Events 能够发起多重控件,并且能有序和用户沟通。
Commands是.NET Framework 提供的核心构架,来激活和去除高级别任务。
由此衍生的Animation是events的更进一步。让你能够以友好互动的方式使用Event架构,来使用多重控件。

在传统的.net中已经有了事件机制了,为什么在WPF中要加入路由事件来取代事件呢,最直观的原因就是典型的WPF应用程序使用很多元素关联和组合起来,从而有了两个概念,LogicalTree) 和 VisualTree),那么它们分别是什么呢,举个例子:

这就是LogicalTree:一个Grid里面镶嵌了其他控件或布局组件,这相当于一棵树中的叶子。
VisualTree:它就是一个树中的叶子里面的结构,用放大镜看一下,其实叶子里面的结构也是一颗树结构。

既然WPF中使用这样的一个设计理念,路由事件就是特别为WPF而生的,它的功能就是可以将一个事件从触发点沿着树向上或者向下传播。而需要对这个事件作出反应的地方添加一个监听器,就会有相应的反应了,当然,它的传递是可以用代码来停止的。

中级工程师

36,绑定详解(包括绑定到单一属性、实体、集合、值转换、触发机制、验证等)

  1. <!-- TextBox绑定属性名为txt2,绑定其Text,双向绑定,属性改变时更新 -->
  2. <TextBox Text="{BindingElementName=txt2,Path=Text,Mode=TwoWay,
  3. UpdateSourceTrigger=PropertyChanged}"/>
  4. <ComboBox ItemsSource="{Binding PerList}" DisplayMemberPath="Name"/>
  5. <!--绑定到集合,数据源必须是ObservableCollection-->

37,怎样布局一个漂亮的UI(你们以前的项目是怎么做的?)
1, 主界面采用无边框设计,左侧加入折叠栏(动画进行折叠),顶部导航栏用RadioButton重写 ControlTemplate, 模拟出TabControl效果
2,右侧内容区域用 ContentControl ,属性Content=”{Binding CurrentModule.Body}” 用集合存储各个页面的内容(UserControl)
3,创建一些用户控件或自定义控件,把他们的控件模板修改成自己想要的风格。
4,使用第三方控件库,MaterialDesign 或 HandyControlH

38,WPF和之前的技术交互(WPF/WinForms)

39,animations 、storyboarding

40,样式、主题和触发器

41,自定义控件

42,怎样才能工作线程更新UI?

  1. //正确的写法:通知主线程去完成更新
  2. new Thread(()=>{
  3. this.Dispatcher.Invoke(new Action(()=>{
  4. progressBar1.Value=20;
  5. }));
  6. }).Start();

高级工程师###
43,什么是attached behavior(附加行为或者附加事件)?

44,PRISM,CAL & CAG等等框架,是否使用过?你们是怎么用的?没有使用的话,解释一下自己的开发模式和框架。

45,怎么开发自定义控件?可以简单介绍一下自己开发的控件。

46,你之前的WPF项目开发流程是怎样的?
1. 提取需求
了解其真正的需求。他们应该完善所有功能,并适当的用用户方案和情景来描述。优先处理哪些有风险的,重复的和重要的工作。

2. 验证用户界面原型
创建一个用户界面原型是用户和工程师之间建立一个互动设计的共识,交流思想的重要步骤。有用的仅仅是一张粗略的用户界面草图,以免过早的讨论实际细节。对此,这里有一些技术和工具:

  • 纸上原形:使用纸和笔来粗略的描画用户界面。不需要任何工具和结构。每个人都可以在纸上描述它的意愿。
  • 线框图:线框图经常用来描述页面的布局。之所以叫线框图,是因为它描画得是空间或图像的轮廓。这个可以借助PowerPoint或Visio完成。

3. 实现业务逻辑和原始的用户界面
4. 整合图形设计
5. 软件测试
47,三种开发模式(MVVM/MVP/MVC)的理解。

48,WPF的性能调整(你是怎么优化WPF性能的?)
(1).减少需要显示的元素数量,去除不需要或者冗余的XAML元素代码.
(2).使用UI虚拟化,只显示当前需要显示的元素.
(3).不要把不要显示的自定义控件隐藏在主界面中,虽然它们不会显示出来,但是程序启动时还是会去计算自定义控件所需的空间和位置.
2. 耗时操作放在放在非UI线程上处理,保持UI的顺畅。处理完成后如果需要在UI上展示,调用Dispatcher.BeginInoke()方法。

  1. 关于Data Binding,根据实际情况对Binding指定不同的Mode,性能OneWay优于TwoWay。

  2. Resources: 系统资源,如果把样式资源都统一到App.xaml中,便于资源的管理。把多次重复用到的资源放到App.xaml中。如页面的资源只会在当前页面中使用到,则把资源定义在当前页面;

5,如非必要,不要使用DynaicResource,使用StaticResource即可;

6, 对Item类型控件重写时,使用VirtualizingStackPanel作为ItemPanel,这样列表资源可以只渲染当前需要的内容。不过如果设置CanContextScrol=”True”会阻止虚拟化,另外使用VirtualizingStackPanel时,可以设置VirtualizingStackPanel.VirtualizationMode=”Recycling”, 这样已经显示过的列表不会被重复创建和释放掉。

  1. 尽量少的使用Animation,尤其在程序启动时,Animation渲染时会占用一些CPU资源。

49,聊聊你做WPF的一些经验和体会。
[

](https://blog.csdn.net/lwwl12/article/details/75276757)