目前我是这么做的,利用TextBlock.Dispatcher.Invoke的方法来更新wpf界面。
但是目前执行以后,整个界面是在输出,但是会没法切换到显示上,假死状态。我试过在递归代码返回以后执行线程沉睡,但是也没有什么用处。
1 private static long CurrentCheckUserId = 0; 2 private void BtnCheckAll_Click(object sender, RoutedEventArgs e) 3 { 4 var dialogResult = MessageBox.Show(this, "全库检索是一个非常耗费资源和时间的操作,是否确认继续,继续请选择“确认”,否则请取消", "重要操作提示!", 5 MessageBoxButton.OKCancel, MessageBoxImage.Asterisk); 6 if (dialogResult == MessageBoxResult.OK) 7 { 8 LbResult.Visibility = Visibility.Collapsed; 9 TbInfo.Visibility= Visibility.Visible; 10 Task.Run(async () => 11 { 12 Members = await _context.Members.ToListAsync(); 13 var totalCount = Members.Count(x => x.CrmId > 1); 14 DispatchUpdateUi($"【{DateTime.Now:MM-dd HH:mm:ss:fff}】有上级推荐人的总用户数为:{totalCount}{Environment.NewLine}",false); 15 foreach (var user in Members) 16 { 17 var userId = user.Id; 18 CurrentCheckUserId = userId; 19 if (DoSearchUser(user) == 0) 20 { 21 string crmIds = string.Empty; 22 if (CrmIds.Count > 0) 23 { 24 crmIds = string.Join(",", CrmIds.ToArray()); 25 } 26 else 27 { 28 crmIds = "0"; 29 } 30 if (DateTime.Now.Second == 30) 31 { 32 Thread.Sleep(1000);//每59秒休息一秒钟 33 } 34 //把获取的推荐人列表存入数据库 35 CrmIds.Clear(); 36 } 37 } 38 }); 39 } 40 } 41 42 private static List<Member> Members=null; 43 private static readonly List<long> CrmIds = new List<long>(); 44 private static readonly ILog Logger = log4net.LogManager.GetLogger("CrmIdsLog"); 45 46 private static string _message = null; 47 48 private void DispatchUpdateUi(string sdr,bool isError=true) 49 { 50 try 51 { 52 if (isError) 53 { 54 Logger.Error(sdr 55 + Environment.NewLine 56 + 57 $"【{DateTime.Now:MM-dd HH:mm:ss:fff}】当前正在检查用户:{CurrentCheckUserId},目前获取的上级推荐人列表为: {string.Join(",", CrmIds.ToArray())}{Environment.NewLine}"); 58 } 59 _message = sdr; 60 TbInfo.Dispatcher.Invoke(new UpdatetbxMessageDelegate(UpdatetbxMessageAction)); 61 } 62 catch (Exception ex) 63 { 64 throw ex; 65 } 66 } 67 private delegate void UpdatetbxMessageDelegate(); 68 private void UpdatetbxMessageAction() 69 { 70 if (!string.IsNullOrWhiteSpace(_message)) 71 { 72 TbInfo.Inlines.Add(_message); 73 TbInfo.Inlines.Add(new LineBreak()); 74 if (CurrentCheckUserId > 0) 75 { 76 TbInfo.Inlines.Add( 77 $"【{DateTime.Now:MM-dd HH:mm:ss:fff}】当前正在检查用户:{CurrentCheckUserId},目前获取的上级推荐人列表为: {string.Join(",", CrmIds.ToArray())}"); 78 TbInfo.Inlines.Add(new LineBreak()); 79 } 80 _message = null; 81 } 82 } 83 84 private long DoSearchUser(Member member) 85 { 86 var userId = member.Id; 87 if (member.CrmId == 0|| member.CrmId == 1) 88 { 89 DispatchUpdateUi($"【{DateTime.Now:MM-dd HH:mm:ss:fff}】检查完成{Environment.NewLine}",false); 90 return 0; 91 } 92 if (CrmIds.Contains(member.CrmId)) 93 { 94 CrmIds.Add(member.CrmId); 95 DispatchUpdateUi($"【{DateTime.Now:MM-dd HH:mm:ss:fff}】上级推荐人列表重复:{string.Join(",", CrmIds.ToArray())}"); 96 return 0; 97 } 98 CrmIds.Add(member.CrmId); 99 //根据上级用户查询用户,然后再查询该上级用户的推荐人 100 var newmember = Members.Find(x => x.Id == member.CrmId); 101 if (newmember == null) 102 { 103 DispatchUpdateUi($"【{DateTime.Now:MM-dd HH:mm:ss:fff}】上级推荐人ID有误:{userId},使用的上级id为{member.CrmId}"); 104 return 0; 105 } 106 return DoSearchUser(newmember); 107 }
。。。。是因为频繁更新的问题么?
不要用控件的inkove,用主线程刷新UI,控件的task.run启动后会锁死UI,鼠标没法点,但是更新看得见
应该怎么处理呢?
@心雨纷扬: 用SynchronizationContext 试试
http://www.cnblogs.com/Kevin-moon/archive/2009/01/13/1374353.html
或者用this.Invoke 别用控件的Invoke 用主线程form的
@寒叶: 用主线程的是可用的。
是的。
大概看了下,你的后台线程不停的去处理前端(这和本身的分线程是相违背的),但是你又想不停的改变前端,你应当适当时机去处理前端。本身wpf就非常庞大,效率低下,就比如你用网页处理 很多 dom一回事 ,如果确实要实时性非常强那么技术线路就不是用他。
补充 —— 你的代码是想写得高级漂亮,却写得还比较啰嗦。
是有个地方啰嗦些,没有优化。
其实这个也准备放弃了,才六万数据,等待遍历时间就几个小时也完成不来,这个递归为何耗时如此之久~~
@心雨纷扬: 那就可能出现回环调用了三。