WPF 微信 MVVM

来源:http://www.cnblogs.com/ZXdeveloper/archive/2016/11/13/6058206.html
-Advertisement-
Play Games

公司的同事離職了,接下來的日子可能會忙碌,能完善DEMO的時間也會少了,因此,把做的簡易DEMO整體先記錄一下,等後續不斷的完善。 參考兩位大神的日誌:WEB版微信協議部分功能分析、【完全開源】微信客戶端.NET版 尤其是周見智大神的DEMO,因為好多和微信的服務端交互,都借鑒了大神的源碼,幫助巨大 ...


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

參考兩位大神的日誌:WEB版微信協議部分功能分析【完全開源】微信客戶端.NET版

尤其是周見智大神的DEMO,因為好多和微信的服務端交互,都借鑒了大神的源碼,幫助巨大,可以說我相當於做了一個翻版,只是用WPF開發的而已,外觀上不同,但是實際交互上是差不多的。

微信分為兩個部分,一個是登錄,一個是主體,基於此,WPF也主要是這兩個窗體來實現。

一、登錄模塊

1、登錄部分分為二維碼和獲取用戶頭像兩個頁面(因為是給予WEB的,所以沒有客戶端的登錄按鈕,只能通過掃碼來登錄)

在程式啟動以後,先通過請求獲取到二維碼,然後,在啟動一個新的線程,不斷的迴圈檢索登錄狀態。

private void LoopLoginCheck()
        {
            object login_result = null;
            //迴圈判斷手機掃描二維碼結果
            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)
                {
                    //訪問登錄跳轉URL
                    ls.GetSidUid(login_result as string);
                                     
                    //廣播,隱藏登錄頁面,打開主頁面
                    Messenger.Default.Send<object>(null, "HideLoginUC");

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

因為是MVVM,所以,需要用廣播來進行操作頁面的切換,即填充到登錄窗體中間的控制項是二維碼,還是頭像。

2、大家可以看到我上面的截圖部分包含了一部分的背景,這個是用Snagit(推薦這個截圖工具,很好用)截圖時,自動截出的,因為窗體本身的大小就是那麼大,多餘出來的部分是透明的,用來做二維碼滑動出現的效果部分。

當處於二維碼狀態時划過,則出現動畫,頭像狀態下則沒有動畫,是設置了Image的Visibility屬性來控制的,滑動效果可以看我的另一篇博客微信 二維碼滑鼠滑動 圖像顯隱效果

3、當掃碼成功,並且在手機端點擊登錄以後,則跳轉到主頁面,此處沒有加非同步等待處理,所以,用戶量大的朋友,請耐心等待(後期會加上)。

登錄成功以後,就會出現主窗體和系統托盤,主窗體包含最近聯繫人和通訊錄,系統托盤網上很多解決方案,可以自行查找。

登錄成功現在發現了一個問題,就是我有兩個微信號,其中一登錄以後是有數據的,另一個則沒有數據。

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

二、主窗體模塊

1、主窗體的佈局部分很簡單,採用了Grid進行分隔,三列,上面的控制項如圖所示

大部分到沒什麼,可能大家比較疑惑的是我的聊天窗體為什麼是ListBox,這個東西的話,我認為,自己有自己的開發習慣,很多控制項都可以實現,panel就可以。

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

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

<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、聊天內容部分用的是ScrollingListBox,繼承自ListBox,但是重寫了裡面的OnItemsChanged屬性,保證可以時刻滾動到最後一行

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);
            }            
        }
    }
ScrollingListBox

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

<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>
聊天窗體樣式

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

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

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

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

<Style x:Key="ListBoxItemFriendStyle" 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}}">
                            <TextBlock Text="{Binding }" FontWeight="Black" Visibility="{Binding Converter={StaticResource modelToVisibility}}" FontSize="15" Margin="10,0,0,0" HorizontalAlignment="Stretch" VerticalAlignment="Center"/>
                            <Image Source="{Binding Icon}" Width="40" Height="40" Margin="10"/>
                            <TextBlock Text="{Binding ShowName}" FontSize="15" HorizontalAlignment="Left" Margin="5,10,0,0"/>
                        </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>
通訊錄列表樣式

當點選列表以後,並且轉換成功的情況下,則顯示出用戶的信息,通過內容是否未空,來判別是否要顯示

<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="地區" 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="發消息" 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>
用戶信息頁面

點擊發消息按鈕,則跳轉回聊天頁面,然後,將當前的好友加入到聊天的第一項。

三、總結

做WPF微信DEMO,用到了轉換器,轉換顏色,轉換顯隱;重寫了控制項的樣式,例如Button、RadioButton、ListBox;然後MVVM模式下,Bing的用法,感覺這個DEOM對於初學者來說應該會有很大的幫助。

不過這個DEMO的BUG和不完善的地方還有很多,例如系統托盤還沒有做閃爍,現在只能發送文字,最大化的問題。

系統托盤閃爍可以用Timer和Opacity來進行控制,比如來未讀消息了,則在進行時間間隔的控制顯隱。

後期會把TextBox換成RichTextBox,這樣可以發送圖片和emoji。

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

 

源碼:GitHub


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Shipyard 是一個基於 Web 的 Docker 管理工具,支持多 host,可以把多個 Docker host 上的 containers 統一管理;可以查看 images,甚至 build images;並提供 RESTful API 等等。 Shipyard 要管理和控制 Docker ...
  • more\less:翻頁命令 more:翻頁的形式查看文件內容。該命令可作為管道命令。 翻頁過程可使用的鍵: 空格(space):向下翻頁; 回車(Enter):向下翻一行; b:往回翻,只限,但管道命令時無效。 q:立刻離開more。 less:比more更方便翻頁,能向上翻頁。可作為管道命令。 ... ...
  • 基本命令的講解 主要內容介紹 1、LINUX操作系統安裝及初始化配置(熟悉);2、LINUX操作系統目錄組成結構及文件級增刪改查操作(重點);3、LINUX操作系統用戶、許可權管理(重點);4、開源軟體及LINUX下軟體包的管理(重點);5、LINUX操作系統磁碟管理(瞭解);6、LINUX操作系統網 ...
  • 一、系統以及軟體的準備 系統及編譯安裝包的下載地址:http://pan.baidu.com/s/1jIjqinc 密碼:ghc2 說明:由於centos6.5是分捲壓縮的,且壓縮為三個壓縮包,所以請下載三個壓縮包,並放於同一文件夾中,解壓CentOS-6.5-x86_64-bin-DVD.zip即 ...
  • docker部署環境:CentOS release 6.5 (Final) Docker配置文件:/etc/sysconfig/docker 重要參數解釋: -H 表示Docker Daemon綁定的地址 -H unix:///var/run/docker.sock -H tcp://0.0.0.0 ...
  • 一、Linux操作系統簡介 1、Linux系統定義:Linux是一套免費使用和自由傳播的類Unix操作系統,是一個基於POSIX和UNIX的多用戶、多任務、支持多線程和多CPU的操作系統 2、Linux系統運行穩定,主要用於伺服器。 3、Linux系統用戶分為: a、系統用戶root:提示符# b、 ...
  • 1.GFS介紹 GFS簡要說明,它有兩種: 1. Google文件系統:GFS是GOOGLE實現的是一個可擴展的分散式文件系統,用於大型的、分散式的、對大量數據進行訪問的應用。它運行於廉價的普通硬體上,但可以提供容錯功能。它可以給大量的用戶提供總體性能較高的服務。欲瞭解更多,可以訪問:http:// ...
  • 題目描述 Description 在小松宿舍樓下的不遠處,有PK大學最不錯的一個食堂——The Farmer’s Canteen(NM食堂)。由於該食堂的菜都很不錯,價格也公道,所以很多人都喜歡來這邊吃飯。The Farmer’s Canteen的點菜方式如同在超市自選商品一樣,人們從一個指定的路口 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...