【发布时间】:2021-10-24 06:10:20
【问题描述】:
我正在编写一个 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 = "";
}
}
【问题讨论】:
-
仅仅设置属性值是不够的。每当调用 setter 时,您都必须触发
PropertyChanged事件。 -
我正在使用 PropertyChanged.Fody,所以我认为这应该为我处理 (github.com/Fody/PropertyChanged)。我的所有其他绑定都可以正确更新,而无需手动触发事件。这个特定的配置有什么需要的吗?
-
你在 UI 线程上调用
TimedNotifyStatusUpdate吗?如果它是从不同的线程调用的,属性更改通知将无处可去 -
如果您明智地测试,删除整个
TaskFactory.StartNew块会发生什么?这是以同步阻塞方式等待的异步延迟...... -
DataContext是如何设置的?你确定绑定解决了吗?您应该启用数据绑定跟踪并检查调试器的输出窗口是否存在绑定错误。
标签: c# wpf data-binding