<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          推薦:WPF開發(fā)的微信客戶端!!!

          共 20193字,需瀏覽 41分鐘

           ·

          2022-06-17 22:44

          本文經(jīng)原作者授權(quán)以原創(chuàng)方式二次分享,歡迎轉(zhuǎn)載、分享。

          原文作者:眾尋

          原文鏈接:https://www.cnblogs.com/ZXdeveloper/p/6058206.html


          公司的同事離職了,接下來的日子可能會忙碌,能完善DEMO的時(shí)間也會少了,因此,把做的簡易DEMO整體先記錄一下,等后續(xù)不斷的完善。

          參考兩位大神的日志:WEB版微信協(xié)議部分功能分析[1]【完全開源】微信客戶端.NET版[2]

          尤其是周見智[3]大神的DEMO,因?yàn)楹枚嗪臀⑿诺姆?wù)端交互,都借鑒了大神的源碼,幫助巨大,可以說我相當(dāng)于做了一個(gè)翻版,只是用WPF開發(fā)的而已,外觀上不同,但是實(shí)際交互上是差不多的。

          微信分為兩個(gè)部分,一個(gè)是登錄,一個(gè)是主體,基于此,WPF也主要是這兩個(gè)窗體來實(shí)現(xiàn)。

          一、登錄模塊

          1、登錄部分分為二維碼和獲取用戶頭像兩個(gè)頁面(因?yàn)槭墙o予WEB的,所以沒有客戶端的登錄按鈕,只能通過掃碼來登錄)

          在程序啟動以后,先通過請求獲取到二維碼,然后,在啟動一個(gè)新的線程,不斷的循環(huán)檢索登錄狀態(tài)。

          private void LoopLoginCheck()
          {
              object login_result = null;
              //循環(huán)判斷手機(jī)掃描二維碼結(jié)果
              while (true)
              {
                  login_result = ls.LoginCheck();
                  //已掃描 未登錄
                  if (login_result is ImageSource)
                  {
                      HeadImageSource = login_result as ImageSource;
                      //廣播,通知到LoginUC頁面,切換
                      Messenger.Default.Send<object>(null"ShowLoginInfoUC");
                  }
                  //已完成登錄
                  if (login_result is string)
                  {
                      //訪問登錄跳轉(zhuǎn)URL
                      ls.GetSidUid(login_result as string);
                                        
                      //廣播,隱藏登錄頁面,打開主頁面
                      Messenger.Default.Send<object>(null"HideLoginUC");

                      thread.Abort();
                      break;
                  }
                  ////超時(shí)
                  if (login_result is int)
                  {
                      //QRCodeImageSource = ls.GetQRCode();
                      //返回二維碼頁面
                      Messenger.Default.Send<object>(null"ShowQRCodeUC");
                  }
              }
          }

          因?yàn)槭荕VVM,所以,需要用廣播來進(jìn)行操作頁面的切換,即填充到登錄窗體中間的控件是二維碼,還是頭像。

          2、大家可以看到我上面的截圖部分包含了一部分的背景,這個(gè)是用Snagit(推薦這個(gè)截圖工具,很好用)截圖時(shí),自動截出的,因?yàn)榇绑w本身的大小就是那么大,多余出來的部分是透明的,用來做二維碼滑動出現(xiàn)的效果部分。

          當(dāng)處于二維碼狀態(tài)時(shí)劃過,則出現(xiàn)動畫,頭像狀態(tài)下則沒有動畫,是設(shè)置了Image的Visibility屬性來控制的,滑動效果可以看我的另一篇博客微信 二維碼鼠標(biāo)滑動 圖像顯隱效果[4]

          3、當(dāng)掃碼成功,并且在手機(jī)端點(diǎn)擊登錄以后,則跳轉(zhuǎn)到主頁面,此處沒有加異步等待處理,所以,用戶量大的朋友,請耐心等待(后期會加上)。

          登錄成功以后,就會出現(xiàn)主窗體和系統(tǒng)托盤,主窗體包含最近聯(lián)系人和通訊錄,系統(tǒng)托盤網(wǎng)上很多解決方案,可以自行查找。

          登錄成功現(xiàn)在發(fā)現(xiàn)了一個(gè)問題,就是我有兩個(gè)微信號,其中一登錄以后是有數(shù)據(jù)的,另一個(gè)則沒有數(shù)據(jù)。

          跟蹤代碼,發(fā)現(xiàn)返回的Json是空的,也就是說沒有返回值,試驗(yàn)了下周大神的代碼,發(fā)現(xiàn)也是空的,不清楚什么情況,我同事的有的也是空的,這個(gè)一直沒有深究,等把功能基本都完善以后再看看問題所在。

          二、主窗體模塊

          1、主窗體的布局部分很簡單,采用了Grid進(jìn)行分隔,三列,上面的控件如圖所示

          大部分到?jīng)]什么,可能大家比較疑惑的是我的聊天窗體為什么是ListBox,這個(gè)東西的話,我認(rèn)為,自己有自己的開發(fā)習(xí)慣,很多控件都可以實(shí)現(xiàn),panel就可以。

          RadioButton的樣式是用path畫的,可以看我另一篇博客微信聊天和通訊錄按鈕樣式[5]

          2、聊天列表里,未讀的消息上會有帶數(shù)字的小紅點(diǎn),這個(gè)是用Button寫的,Item的整體組成是Image(頭像)、Button(未讀數(shù))、TextBlock(昵稱、時(shí)間和聊天內(nèi)容)

          <Style x:Key="ListBoxItemChatStyle" TargetType="{x:Type ListBoxItem}">
              <Setter Property="Template">
                  <Setter.Value>
                      <ControlTemplate TargetType="ListBoxItem">
                          <Border>
                              <StackPanel x:Name="sp" Orientation="Horizontal" Height="{Binding Converter={StaticResource objectToHeight}}" Background="{Binding Converter={StaticResource objectToColor}}">
                                  <Grid>
                                      <Image Source="{Binding Icon}" Width="40" Height="40" Margin="10"/>
                                      <Button Foreground="White" Visibility="{Binding UnReadCount,Converter={StaticResource countToVisibility}}" Content="{Binding UnReadCount}" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,5" Style="{StaticResource CirButtonStyle}"/>
                                  </Grid>
                                  <Grid Width="176">
                                      <Grid.RowDefinitions>
                                          <RowDefinition/>
                                          <RowDefinition/>
                                      </Grid.RowDefinitions>
                                      <TextBlock Grid.Row="0" Text="{Binding ShowName}" FontSize="15" HorizontalAlignment="Left" Margin="5,10,0,0"/>
                                      <TextBlock Grid.Row="0" Text="{Binding LastTime}" FontSize="15" HorizontalAlignment="Right" Margin="0,10,5,0"/>
                                      <TextBlock Grid.Row="1" Text="{Binding LastMsg}" FontSize="12" HorizontalAlignment="Left" Margin="5,0,0,0"/>
                                  </Grid>
                              </StackPanel>
                          </Border>
                          <ControlTemplate.Triggers>
                              <Trigger Property="IsMouseOver" Value="true">
                                  <Setter Property="Background" Value="#FFE2E4E6" TargetName="sp"/>
                              </Trigger>
                              <Trigger Property="IsSelected" Value="true">
                                  <Setter Property="Background" Value="#FFCACDD3" TargetName="sp"/>
                              </Trigger>
                          </ControlTemplate.Triggers>
                      </ControlTemplate>
                  </Setter.Value>
              </Setter>
          </Style>

          3、聊天內(nèi)容部分用的是ScrollingListBox,繼承自ListBox,但是重寫了里面的OnItemsChanged屬性,保證可以時(shí)刻滾動到最后一行

          public class ScrollingListBox : ListBox
          {
              protected override void OnItemsChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
              {
                  if (e.NewItems!=null)
                  {
                      int newItemCount = e.NewItems.Count;

                      if (newItemCount > 0)
                          this.ScrollIntoView(e.NewItems[newItemCount - 1]);

                      base.OnItemsChanged(e);
                  }            
              }
          }

          樣式部分是重寫控件模板用的是Image(頭像),path(三角部分),textbox(內(nèi)容部分)

          <Style x:Key="ChatListBoxStyle" TargetType="{x:Type ListBox}">
              <Setter Property="Background" Value="{DynamicResource {x:Static SystemColors.WindowBrushKey}}"/>
              <Setter Property="BorderBrush" Value="{StaticResource ListBorder}"/>
              <Setter Property="BorderThickness" Value="0"/>
              <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
              <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Hidden"/>
              <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Hidden"/>
              <Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
              <Setter Property="ScrollViewer.PanningMode" Value="Both"/>
              <Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
              <Setter Property="VerticalContentAlignment" Value="Center"/>
              <Setter Property="ItemContainerStyle">
                  <Setter.Value>
                      <Style TargetType="ListBoxItem">
                          <Setter Property="Focusable" Value="False"/>
                          <Setter Property="Template">
                              <Setter.Value>
                                  <ControlTemplate>
                                      <StackPanel Orientation="Horizontal" Grid.Column="1" HorizontalAlignment="Stretch" VerticalAlignment="Top" FlowDirection="{Binding FlowDir}" Margin="15,5">
                                          <Image Grid.Column="1" Source="{Binding Image}" Height="35" Width="35" VerticalAlignment="Top"/>
                                          <Path Grid.Column="2" StrokeThickness="1" Stroke="{Binding TbColor}" Data="M12,13 L5,18 L12,23Z" Fill="{Binding TbColor}" Margin="0" SnapsToDevicePixels="True"/>
                                          <TextBox Grid.Column="3" MaxWidth="355" TextWrapping="Wrap" FontSize="15" BorderBrush="{Binding TbColor}" Background="{Binding TbColor}" IsReadOnly="True" BorderThickness="0" Style="{StaticResource ChatTextBoxStyle}" FlowDirection="LeftToRight" Text="{Binding Message}"/>
                                      </StackPanel>
                                  </ControlTemplate>
                              </Setter.Value>
                          </Setter>
                      
          </Style>
                  </Setter.Value>
              </Setter>
          </Style>

          需要注意的是:此處必須要重寫控件模板,而不能重寫數(shù)據(jù)模板,雖然,很多情況下控件模板和數(shù)據(jù)模板可以得到的效果相同,但是此處,如果寫數(shù)據(jù)模板的話,則自己發(fā)的信息不會在右側(cè),就算設(shè)置FlowDirection也沒有用,大家可以自行嘗試。

          4、如果發(fā)送內(nèi)容是空的情況下,則會有一個(gè)ToolTip出現(xiàn),此處的TooLTipye也是重寫了樣式的Button,好定位,畢竟就算是最大化,位置也是不變的。

          通訊錄部分,和聊天列表差不多,不過,由于需要進(jìn)行分組,也就是A、B……這種組合,所以用的Object類型,在點(diǎn)選過程中,通過is來進(jìn)行判別是不是WeChatUser,如果是,則進(jìn)行轉(zhuǎn)換,來進(jìn)一步處理。

          大家可以看到上面那個(gè)好友是 同程旅游顧問<span …… 其實(shí)它是一個(gè)emoji,只是現(xiàn)在我還沒有做到那一部分,如果做到的話,則進(jìn)行轉(zhuǎn)換,如果誰有好的emoji處理方式希望告知,謝謝了。

          當(dāng)點(diǎn)選列表以后,并且轉(zhuǎn)換成功的情況下,則顯示出用戶的信息,通過內(nèi)容是否未空,來判別是否要顯示

          <Grid Grid.Row="1" Grid.RowSpan="2" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="{Binding ElementName=rb_friend,Path=IsChecked,Converter={StaticResource boolToVisibility}}" Margin="0,50,0,0">
              <Grid.RowDefinitions>
                  <RowDefinition Height="Auto"/>
                  <RowDefinition Height="Auto"/>
                  <RowDefinition Height="Auto"/>
                  <RowDefinition Height="Auto"/>
                  <RowDefinition Height="Auto"/>
                  <RowDefinition Height="Auto"/>
                  <RowDefinition Height="Auto"/>
              </Grid.RowDefinitions>
              <Image Source="{Binding FriendInfo.Icon}" Grid.Row="0" Height="124" Width="124" HorizontalAlignment="Center"/>
              <StackPanel Orientation="Horizontal" Grid.Row="1" HorizontalAlignment="Center">
                  <TextBlock Text="{Binding FriendInfo.NickName}" FontSize="30" Foreground="Black" FontWeight="Bold"/>
                  <Image Visibility="{Binding FriendInfo.Sex,Converter={StaticResource parameterToVisibility},ConverterParameter=2}" Source="/Image/female.png"/>
                  <Image Visibility="{Binding FriendInfo.Sex,Converter={StaticResource parameterToVisibility},ConverterParameter=1}" Source="/Image/male.png"/>
              </StackPanel>
              <TextBlock Text="{Binding FriendInfo.Signature}" Foreground="#FF919191" Grid.Row="2" HorizontalAlignment="Center"/>
              <StackPanel Orientation="Horizontal" Visibility="{Binding FriendInfo.RemarkName,Converter={StaticResource epmtyToVisibility}}" Margin="10" Grid.Row="3" HorizontalAlignment="Center">
                  <TextBlock Text="備  注" Margin="0,0,10,0" FontSize="15"/>
                  <TextBlock Text="{Binding FriendInfo.RemarkName}" FontSize="15"/>
              </StackPanel>
              <StackPanel Orientation="Horizontal" Visibility="{Binding FriendInfo.Province,Converter={StaticResource epmtyToVisibility}}" Margin="10" Grid.Row="4" HorizontalAlignment="Center">
                  <TextBlock Text="地區(qū)" Margin="0,0,10,0" FontSize="15"/>
                  <TextBlock Text="{Binding FriendInfo.Province}" Margin="0,0,2,0" FontSize="15"/>
                  <TextBlock Text="{Binding FriendInfo.City}" FontSize="15"/>
              </StackPanel>
              <Button Content="發(fā)消息" Width="166" Height="37" Grid.Row="5" Command="{Binding FriendSendComamnd}" Margin="0,50,0,0" Style="{StaticResource FriSendButtonStyle}"/>
              <Grid Grid.Row="0" Grid.RowSpan="7" Background="WhiteSmoke" Visibility="{Binding FriendInfo,Converter={StaticResource nullToVisibility}}"/>
          </Grid>

          點(diǎn)擊發(fā)消息按鈕,則跳轉(zhuǎn)回聊天頁面,然后,將當(dāng)前的好友加入到聊天的第一項(xiàng)。

          三、總結(jié)

          做WPF微信DEMO,用到了轉(zhuǎn)換器,轉(zhuǎn)換顏色,轉(zhuǎn)換顯隱;重寫了控件的樣式,例如Button、RadioButton、ListBox;然后MVVM模式下,Bind的用法,感覺這個(gè)DEOM對于初學(xué)者來說應(yīng)該會有很大的幫助。

          不過這個(gè)DEMO的BUG和不完善的地方還有很多,例如系統(tǒng)托盤還沒有做閃爍,現(xiàn)在只能發(fā)送文字,最大化的問題。

          系統(tǒng)托盤閃爍可以用Timer和Opacity來進(jìn)行控制,比如來未讀消息了,則在進(jìn)行時(shí)間間隔的控制顯隱。

          后期會把TextBox換成RichTextBox,這樣可以發(fā)送圖片和emoji。

          最大化問題,是我一直還沒有想到好的解決辦法,最大化的情況下會占據(jù)整個(gè)屏幕,而不把狀態(tài)欄空出來,網(wǎng)上的辦法都是重新設(shè)置Width和Height,但是這樣的話,就要記錄原來的大小和位置,一直沒有找到可以重寫WindowState.Maximized的方法,好像是不能重寫,所以比較糾結(jié),希望哪位大神看完我的代碼以后,能夠給提供一下解決思路,謝謝了。

          參考資料

          [1]

          WEB版微信協(xié)議部分功能分析: http://blog.csdn.net/wonxxx/article/details/51787041

          [2]

          【完全開源】微信客戶端.NET版: http://www.cnblogs.com/xiaozhi_5638/p/4923811.html

          [3]

          周見智: http://home.cnblogs.com/u/xiaozhi_5638/

          [4]

          微信 二維碼鼠標(biāo)滑動 圖像顯隱效果: http://www.cnblogs.com/ZXdeveloper/p/6054278.html

          [5]

          微信聊天和通訊錄按鈕樣式: http://www.cnblogs.com/ZXdeveloper/p/5997372.html

          瀏覽 26
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  四虎成人在线 | 久激情内射婷内射蜜桃 | www.aaa | 亚洲乱码国产乱码午夜 | 欧美一级中文字幕 |