我正在撰寫一個 MVVM 應用程式,我試圖在底部包含一個狀態欄。我已經設定了視圖和視圖模型,它們應該跟蹤應用程式的 Logger 類的狀態,它是一個易于使用的單例。
看法:
<UserControl x:Class="SynthEBD.UC_StatusBar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:SynthEBD"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=local:VM_StatusBar}"
d:DesignHeight="450" d:DesignWidth="800">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Text="{Binding Path=DispString}" Foreground="{Binding Path=FontColor}" FontSize="18" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
查看型號:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
namespace SynthEBD
{
public class VM_StatusBar : INotifyPropertyChanged
{
public VM_StatusBar()
{
this.DispString = "";
this.FontColor = new SolidColorBrush(Colors.Green);
this.SubscribedLogger = Logger.Instance;
this.SubscribedLogger.PropertyChanged = RefreshDisp;
}
public string DispString { get; set; }
private Logger SubscribedLogger { get; set; }
public SolidColorBrush FontColor { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void RefreshDisp(object sender, PropertyChangedEventArgs e)
{
this.DispString = SubscribedLogger.StatusString;
this.FontColor = SubscribedLogger.StatusColor;
}
}
}
記錄器:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media;
namespace SynthEBD
{
public sealed class Logger : INotifyPropertyChanged
{
private static Logger instance;
private static object lockObj = new Object();
public event PropertyChangedEventHandler PropertyChanged;
public VM_RunButton RunButton { get; set; }
public string StatusString { get; set; }
public string LogString { get; set; }
public SolidColorBrush StatusColor { get; set; }
public SolidColorBrush ReadyColor = new SolidColorBrush(Colors.Green);
public SolidColorBrush WarningColor = new SolidColorBrush(Colors.Yellow);
public SolidColorBrush ErrorColor = new SolidColorBrush(Colors.Red);
public string ReadyString = "Ready To Patch";
private Logger()
{
this.StatusColor = this.ReadyColor;
this.StatusString = this.ReadyString;
}
public static Logger Instance
{
get
{
lock (lockObj)
{
if (instance == null)
{
instance = new Logger();
}
}
return instance;
}
}
public static void LogError(string error)
{
Instance.LogString = error "\n";
}
public static void LogErrorWithStatusUpdate(string error, ErrorType type)
{
Instance.LogString = error "\n";
Instance.StatusString = error;
switch (type)
{
case ErrorType.Warning: Instance.StatusColor = Instance.WarningColor; break;
case ErrorType.Error: Instance.StatusColor = Instance.ErrorColor; break;
}
}
public static void TimedNotifyStatusUpdate(string error, ErrorType type, int durationSec)
{
LogErrorWithStatusUpdate(error, type);
var t = Task.Factory.StartNew(() =>
{
Task.Delay(durationSec * 1000).Wait();
});
t.Wait();
ClearStatusError();
}
public static void ClearStatusError()
{
Instance.StatusString = Instance.ReadyString;
Instance.StatusColor = Instance.ReadyColor;
}
}
public enum ErrorType
{
Warning,
Error
}
}
我故意觸發 Logger.TimedNotifyStatusUpdate() 函式,即使我可以看到 VM_StatusBar.RefreshDisp() 中到達斷點,實際螢屏上的字串和顏色永遠不會改變 ( https://imgur.com/BhizinR ) . 我沒有看到任何失敗的系結,所以我不明白為什么視圖沒有更新。感謝您的任何建議!
編輯:我還嘗試明確觸發 PropertyChanged 事件,而不是依賴 PropertyChanged.Fody 如下,但螢屏上的結果是相同的。
public class VM_StatusBar : INotifyPropertyChanged
{
public VM_StatusBar()
{
this.DispString = "";
this.FontColor = new SolidColorBrush(Colors.Green);
this.SubscribedLogger = Logger.Instance;
this.SubscribedLogger.PropertyChanged = RefreshDisp;
}
public string DispString
{
get { return _dispString; }
set
{
if (value != _dispString)
{
_dispString = value;
OnPropertyChanged("DispString");
}
}
}
private string _dispString;
private Logger SubscribedLogger { get; set; }
public SolidColorBrush FontColor { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, e);
}
protected void OnPropertyChanged(string propertyName)
{
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
public void RefreshDisp(object sender, PropertyChangedEventArgs e)
{
this.DispString = SubscribedLogger.StatusString;
this.FontColor = SubscribedLogger.StatusColor;
string debugBreakHere = "";
}
}
uj5u.com熱心網友回復:
你永遠不應該呼叫Task.Wait一個Task物件。總是await它以允許它異步完成。從您發布的代碼看來,您正在阻塞 UI 執行緒,從而竊取更新表面(渲染)所需的資源。Task.Wait是陷入僵局的門票。
此外,喜歡Task.Run過Task.Factory。
把你的阻塞代碼變成非阻塞代碼應該可以做到:
public static async Task TimedNotifyStatusUpdateAsync(string error, ErrorType type, int durationSec)
{
LogErrorWithStatusUpdate(error, type);
Task t = Task.Run(async () =>
{
// Calling Wait on the Task blocks this thread
//Task.Delay(durationSec * 1000).Wait();
// Instead 'await' the Task to free resources
await Task.Delay(durationSec * 1000);
});
// Await the Task to allow the UI thread to render the view
// in order to show the changes
await t;
ClearStatusError();
}
然后async Task使用await以下方法從另一個方法呼叫該方法:
private async Task CallTimedNotifyStatusUpdateAsync()
=> await TimedNotifyStatusUpdateAsync();
請注意,將異步方法包裝到其中Task.Run通常不是一個好主意。的正確實作TimedNotifyStatusUpdateAsync是:
public static async Task TimedNotifyStatusUpdateAsync(string error, ErrorType type, int durationSec)
{
LogErrorWithStatusUpdate(error, type);
// Await the Task to allow the UI thread to render the view
// in order to show the changes
await Task.Delay(durationSec * 1000);
ClearStatusError();
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/337958.html
