2017-02-18 67 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

谢谢!找出答案并用一些示例代码键入答案,以帮助其他人开始使用,但在这里也有一些很好的信息。 –