我的项目如下所示:
TaskClass.cs:
namespace Library.TaskClassDirectory;
public sealed class TaskClass : INotifyPropertyChanged
{
public TaskClass(
TimeSpan plannedLaborCost,
TimeSpan actualLaborCost)
{
Labor = new Labor(plannedLaborCost, actualLaborCost, this);
}
private Labor _labor = null!;
public Labor Labor
{
get => _labor;
set
{
if (Equals(value, _labor))
{
return;
}
_labor = value;
OnPropertyChanged();
}
}
private TimeSpan _theDifferenceBetweenThePlannedAndActualEndOfTask;
public TimeSpan TheDifferenceBetweenThePlannedAndActualEndOfTask
{
get => _theDifferenceBetweenThePlannedAndActualEndOfTask;
set
{
if (value.Equals(_theDifferenceBetweenThePlannedAndActualEndOfTask))
{
return;
}
_theDifferenceBetweenThePlannedAndActualEndOfTask = value;
OnPropertyChanged();
}
}
internal void TheDifferenceBetween()
{
TheDifferenceBetweenThePlannedAndActualEndOfTask = Labor.PlannedCost.Subtract(
Labor.ActualCost
);
OnPropertyChanged(nameof(TheDifferenceBetweenThePlannedAndActualEndOfTask));
}
public event PropertyChangedEventHandler? PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private bool SetField<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value))
{
return false;
}
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
Labor.cs:
namespace Library.TaskClassDirectory;
public class Labor: INotifyPropertyChanged
{
public Labor(TimeSpan plannedCost, TimeSpan actualCost, TaskClass taskClass)
{
PlannedCost = plannedCost;
ActualCost = actualCost;
TaskClass = taskClass;
}
private TimeSpan _plannedCost;
public TimeSpan PlannedCost
{
get => _plannedCost;
set
{
if (value.Equals(_plannedCost))
{
return;
}
if (SetField(ref _plannedCost, value))
{
TaskClass?.TheDifferenceBetween();
}
_plannedCost = value;
OnPropertyChanged();
}
}
private TimeSpan _actualCost;
public TimeSpan ActualCost
{
get => _actualCost;
set
{
if (value.Equals(_actualCost))
{
return;
}
if (SetField(ref _actualCost, value))
{
TaskClass?.TheDifferenceBetween();
}
_actualCost = value;
OnPropertyChanged();
}
}
private TaskClass? _taskClass;
public TaskClass? TaskClass
{
get => _taskClass;
set
{
if (Equals(value, _taskClass))
{
return;
}
_taskClass = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value))
return false;
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
MainViewModel.cs:
namespace WpfAppForExample.ViewModels;
public class MainViewModel:INotifyPropertyChanged
{
public ObservableCollection<TaskClass> TaskClassList { get; set; }
public MainViewModel()
{
TaskClassList = new ObservableCollection<TaskClass>();
GetTaskClassList();
}
private void GetTaskClassList()
{
TaskClass taskClass = new TaskClass(TimeSpan.Zero, TimeSpan.Zero);
TaskClassList.Clear();
TaskClassList.Add(taskClass);
}
public event PropertyChangedEventHandler? PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, [CallerMemberName] string? propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(field, value))
{
return false;
}
field = value;
OnPropertyChanged(propertyName);
return true;
}
}
输出值转换器.cs:
namespace WpfAppForExample.ViewModels;
public class OutputValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is TimeSpan val && parameter != null && parameter.ToString()!.Contains("IsColor"))
{
if (val > TimeSpan.Zero)
{
return "Red";
}
else if (val < TimeSpan.Zero)
{
return "Green";
}
else
{
return "Black";
}
}
return value;
}
public object ConvertBack(object value, Type targetType, object? parameter, CultureInfo culture) =>
value;
}
MainWindow.xaml.cs:
namespace WpfAppForExample
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
MainWindow.xaml:
<Window x:Class="WpfAppForExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfAppForExample"
xmlns:viewModels="clr-namespace:WpfAppForExample.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<viewModels:MainViewModel></viewModels:MainViewModel>
</Window.DataContext>
<Window.Resources>
<viewModels:OutputValueConverter x:Key="OutputValueConverter"></viewModels:OutputValueConverter>
</Window.Resources>
<Grid>
<DataGrid Height="780"
Width="auto"
ItemsSource="{Binding TaskClassList}"
AutoGenerateColumns="False"
HorizontalGridLinesBrush="DarkGray"
RowBackground="LightGray"
AlternatingRowBackground="White">
<DataGrid.Columns>
<DataGridTextColumn Header="Трудозатраты планируемые"
Binding="{Binding Labor.PlannedCost,
Converter={StaticResource OutputValueConverter}}"
IsReadOnly="False"/>
<DataGridTextColumn Header="Трудозатраты фактические"
Binding="{Binding Labor.ActualCost,
Converter={StaticResource OutputValueConverter}}"
IsReadOnly="False"/>
<DataGridTextColumn Header="Сравнение планируемого и фактического окончания работ"
Binding="{Binding TheDifferenceBetweenThePlannedAndActualEndOfTask,
Converter={StaticResource OutputValueConverter}}"
IsReadOnly="True">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="Foreground" Value="Black">
</Setter>
<Style.Triggers>
<DataTrigger Binding="{
Binding TheDifferenceBetweenThePlannedAndActualEndOfTask,
Converter={StaticResource OutputValueConverter},
ConverterParameter=IsColor}"
Value="Black">
<Setter Property="Foreground" Value="Black"></Setter>
</DataTrigger>
<DataTrigger Binding="{
Binding TheDifferenceBetweenThePlannedAndActualEndOfTask,
Converter={StaticResource OutputValueConverter},
ConverterParameter=IsColor}"
Value="Green">
<Setter Property="Foreground" Value="Green"></Setter>
</DataTrigger>
<DataTrigger Binding="{
Binding TheDifferenceBetweenThePlannedAndActualEndOfTask,
Converter={StaticResource OutputValueConverter},
ConverterParameter=IsColor}"
Value="Red">
<Setter Property="Foreground" Value="Red"></Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
在 MainWindow.xaml 行中
<DataTrigger Binding="{Binding TheDifferenceBetweenThePlannedAndActualEndOfTask, Converter={StaticResource OutputValueConverter}, ConverterParameter=IsColor}" Value="Black">
<DataTrigger Binding="{Binding TheDifferenceBetweenThePlannedAndActualEndOfTask, Converter={StaticResource OutputValueConverter}, ConverterParameter=IsColor}" Value="Green">
<DataTrigger Binding="{Binding TheDifferenceBetweenThePlannedAndActualEndOfTask, Converter={StaticResource OutputValueConverter}, ConverterParameter=IsColor}" Value="Red">
IDE 报告 DataContext 指定不正确并写入: 无法解析“WpfAppForExample.ViewModels.MainViewModel”类型的数据上下文中的属性“TheDifferenceBetweenThePlannedAndActualEndOfTask”。
但是,一切正常。这是为什么?
我尝试了以下 DataTrigger Binding 调整,但它们都不起作用:
IDE(XAML 设计器)不够聪明,无法断定 DataContext 实际上是一个 TaskClass 对象。 你可以忽略这个。
如果您对警告不满意:可以选择使用
d:DataContext
属性提供仅供设计器使用的 DataContext。这仅由设计器评估并在运行时被忽略。
示例:
<Style TargetType="TextBlock" d:DataContext="{d:DesignInstance viewModels:TaskClass}">
另请参阅此处:我需要什么来进一步限定 DataContext 的绑定?