Reputation: 13
My project looks like this:
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;
}
}
OutputValueConverter.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>
In the MainWindow.xaml lines
<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">
The IDE reports that the DataContext is specified incorrectly and writes: Unable to resolve property 'TheDifferenceBetweenThePlannedAndActualEndOfTask' in data context of type 'WpfAppForExample.ViewModels.MainViewModel'.
However, everything works correctly. Why is that?
I've tried the following DataTrigger Binding adjustments, but they all don't work:
Upvotes: 0
Views: 56
Reputation: 11977
The IDE (XAML designer) is not smart enough to conclude that the DataContext is in fact a TaskClass object. You can just ignore this.
If you are unhappy with the warning: there is the option to provide a DataContext just for use by the Designer using the d:DataContext
attribute. This is evaluated only by the Designer and ignored at runtime.
Example:
<Style TargetType="TextBlock" d:DataContext="{d:DesignInstance viewModels:TaskClass}">
See also here: What do I need to further qualify the DataContext for a binding?
Upvotes: 0