2017-02-18 30 views
-1

假設我有一個類通過TCP流接收數據,解析它並相應地更改它的屬性。WPF等待變量更改然後更新UI

public static class SomeClass 
{ 
    static bool myBool; 
    static string myMessage; 

    public static void ToggleBool() 
    { 
     myBool = !myBool; 
     // Do some other stuff here 
    } 

    public static UpdateMessage(string message) 
    { 
     System.Diagnostics.Debug.WriteLine(message); 
     ProcessMessage(message); 
     myMessage = message; 
    } 
} 

現在我想要做的就是擁有一個WPF「調試窗口」,它將直觀顯示設置。我想基本上運行一個循環,相應地更新部分窗口。

喜歡的東西:

public partial class LogWindow : Window 
{ 
    public MainWindow() 
    { 
     InitializeComponent(); 
    } 

    public Async Task UpdateUI() 
    { 
     while(checkForUpdates) 
     { 
      myCheckbox.IsChecked = await SomeClass.UpdatedBoolValue(); 

      string newMessage = await SomeClass.NewMessageRCVD(); 
      txtBox.Append(newMessage); 
     } 
    } 
} 

但是,有2點明顯的問題。其中一個,我不知道如何通過不斷檢查while循環來創建一個不會燒壞CPU的函數。我想我可以使用getter/setter方法。二,我必須更新兩個,以便該循環再次運行。

這是最好的方法是什麼?如何更新需要更新的用戶界面部分?

編輯:類似的問題:Write an Async method that will await a bool

+0

使用定時器更新某個intervall中的所有值。 – CSharpie

+0

如果您使用Caliburn Micro或ReactiveUI,他們都有一些pub-sub實現用於應用程序內消息分派,可以準確解決這些問題。 –

回答

0

做到這一點的最好方法是使用數據綁定。

所以我們需要先定義我們的數據來自哪裏。這被稱爲上下文。這將來自一個MVVM術語的ViewModel。如果您不知道MVVM,不要擔心,這可能來自您擁有的任何課程。在後端的.xaml.cs代碼中,我們需要將該類添加到我們的Windows的DataContext中。下面是一個樣子:

public partial class DebugView : Window 
{ 
    public DebugView() 
    { 
     InitializeComponent(); 
     DataContext = new DebugViewModel(); 
    } 
} 

並在該窗口我們WPF的XAML文件中,我們將有一個標籤和文本框被定義爲這樣的:

<Label Content="{Binding ClientCount, FallbackValue='Clients: 00'}" ... /> 
<TextBox Text="{Binding Port, UpdateSourceTrigger=PropertyChanged}" ... /> 

標籤的文本是它的「內容「,而文本框的文本只是」文本「。我們在那裏添加綁定關鍵字,現在每個文本都將被連接到變量ClientCountPort。因此,我們的DebugViewModel類將是這樣的,首先:

private string _ClientCount; 
    public string ClientCount 
    { 
     get { return _ClientCount; } 
     set { _ClientCount= value; RaisePropertyChanged("ClientCount"); } 
    } 
    private string _Port; 
    public string Port 
    { 
     get { return _Port; } 
     set { _Port= value; RaisePropertyChanged("Port"); } 
    } 

現在你沒有一個函數調用RaisePropertyChanged()所以我做了什麼(我認爲這是常見的做法)是我做的,實現了INotifyPropertyChanged一個基類並處理那裏的所有工作。

所以我們的基類BaseViewModel將繼承INotifyPropertyChanged類,併爲我們設置了一切。這只是看起來像這樣(隨意只是複製粘貼,並使用原樣):

using System.ComponentModel; 

public class BaseViewModel : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 
    internal void RaisePropertyChanged(string prop) 
    { 
     PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop)); 
    } 

    // Other functions we want all ViewModels to have 
} 

等那麼我們DebugViewModel類看起來像這樣:

public class ServerViewModel : BaseViewModel 
{ 
    private string _ClientCount; 
    public string ClientCount 
    { 
     get { return _ClientCount; } 
     set { _ClientCount= value; RaisePropertyChanged("ClientCount"); } 
    } 
    private string _Port; 
    public string Port 
    { 
     get { return _Port; } 
     set { _Port= value; RaisePropertyChanged("Port"); } 
    } 

    public DebugViewModel() 
    { 
     // Initialize to default values 
     ClientCount = $"Clients {server.clientCount}"; 
     Port = $"{server.port}"; 
    } 

    // Rest of code 
} 

然後當你開始你的程序它會自動填充字段,當您更改文本框中的數據時,字符串將會更改,反之亦然。我們的XAML聲明的UpdateSourceTrigger=PropertyChanged部分使得變量在文本框中的數據發生更改時立即更新(默認行爲是當文本框失去焦點時,例如,您選擇下一個文本框或單擊)。

這非常酷,因爲您可以在輸入時動態驗證輸入,也不必擔心切換到UI線程來更新UI,而且只需通過像這樣綁定來讓代碼看起來更簡單。

0

取決於如何複雜的實施/您的需求。

從你的例子,如果你使SomeClass實現INotifyPropertyChanged你可以很容易地附加一個WPF窗口,並通過綁定窗口會自動更新沒有任何形式的循環。

如果您在談論多個課程,並且希望讓他們全都在同一個窗口中顯示屬性信息,那麼您最好的辦法就是創建一個隊列。在你希望跟蹤的每個屬性中,setter寫入隊列。 (全球或單身)然後,您可以輕鬆地在窗口中顯示該信息,或通過Observer pattern輕鬆地顯示該信息。也可以將其設置爲它從不在生產中寫入隊列,或者使用條件編譯語句生成甚至沒有代碼,如果這是您的願望。

+0

謝謝!找出答案並用一些示例代碼鍵入答案,以幫助其他人開始使用,但在這裏也有一些很好的信息。 –