Antoine Charbonneau Antoine Charbonneau - 3 months ago 44
C# Question

Prism MVVM - string property doesn't update in the View when modified in the ViewModel

I have a WPF window in which a display a list of events based on a range of dates. I also have a string property in which I want to display a error message if the user selected a incorrect date range. ("From" date is greater than the "To" date.)

On a date control value changed i set off a command to refresh the events in the datagrid. Before that, i valide that the date range is valid. If not, i display the error message in a label.

The proplem is that even if the ViewModel set the property to the text of the error message, the View never update with the corresponding value.

What I my doing wrong here?

Here is some sample of my code.

<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:prism="http://prismlibrary.com/"
xmlns:igDP="http://infragistics.com/DataPresenter"
xmlns:igEditors="http://infragistics.com/Editors"
x:Class="ADM.Module.Event.Views.EventLogView"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="25"/>
<RowDefinition Height="25"/>
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120"/>
<ColumnDefinition Width="140"/>
<ColumnDefinition Width="30"/>
<ColumnDefinition Width="140"/>
<ColumnDefinition />
</Grid.ColumnDefinitions>

<Label Content="{Binding Header, Mode=OneTime}" FontSize="25" FontWeight="Bold" Grid.ColumnSpan="5"/>

<Label Content="Select a date range : " Grid.Row="1" />

<igEditors:XamDateTimeEditor HorizontalAlignment="Left" Margin="2,2,2,2" Grid.Row="1" Grid.Column="1" VerticalAlignment="Top"
Format="yyyy-MM-dd HH:mm:ss" Mask="yyyy-mm-dd hh:mm:ss" Value="{Binding DateFrom}" Theme="Office2010Blue" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="ValueChanged">
<i:InvokeCommandAction Command="{Binding RefreshEventLogCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</igEditors:XamDateTimeEditor>

<Label Content=" to " Grid.Row="1" Grid.Column="2" />

<igEditors:XamDateTimeEditor HorizontalAlignment="Left" Margin="2,2,2,2" Grid.Row="1" Grid.Column="3" VerticalAlignment="Top"
Format="yyyy-MM-dd HH:mm:ss" Mask="yyyy-mm-dd hh:mm:ss" Value="{Binding DateTo}" Theme="Office2010Blue" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="ValueChanged">
<i:InvokeCommandAction Command="{Binding RefreshEventLogCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</igEditors:XamDateTimeEditor>

<Label Content="{Binding DateRangeError}" Foreground="Red" Grid.Row="2" Grid.ColumnSpan="5" />

<igDP:XamDataGrid HorizontalAlignment="Stretch" Margin="2,2,2,2" Grid.Row="3" Grid.ColumnSpan="5" VerticalAlignment="Stretch"
DataSource="{Binding EventLogRecords}" Theme="Office2010Blue">

<igDP:XamDataGrid.FieldLayoutSettings>
<igDP:FieldLayoutSettings AllowDelete="False" HighlightAlternateRecords="True" AllowAddNew="False" AutoArrangeCells="LeftToRight"
ResizingMode="Immediate" SelectionTypeRecord="Single" AutoGenerateFields="False" />

</igDP:XamDataGrid.FieldLayoutSettings>

<igDP:XamDataGrid.FieldSettings>
<igDP:FieldSettings AllowRecordFiltering="True" />
</igDP:XamDataGrid.FieldSettings>

<igDP:XamDataGrid.FieldLayouts>
<igDP:FieldLayout>
<igDP:FieldLayout.Fields>

<igDP:Field Name="EventDate" Label="Date" AllowEdit="False" Format="yyyy-MM-dd H:mm:ss" >
<igDP:Field.Settings>
<igDP:FieldSettings AllowRecordFiltering="False" />
</igDP:Field.Settings>
</igDP:Field>
<igDP:UnboundField Name="Severities" Label="Severity" BindingPath="Severities.SeverityText" AllowEdit="False" />
<igDP:UnboundField Name="LogTypes" Label="Type" BindingPath="LogTypes.LogTypeName" AllowEdit="False" />
<igDP:Field Name="EventMsg" Label="Message" AllowEdit="False" >
<igDP:Field.Settings>
<igDP:FieldSettings FilterOperandUIType="TextBox" FilterOperatorDefaultValue="Contains" />
</igDP:Field.Settings>
</igDP:Field>

</igDP:FieldLayout.Fields>
</igDP:FieldLayout>
</igDP:XamDataGrid.FieldLayouts>
</igDP:XamDataGrid>

</Grid>




using Prism.Commands;
using ADM.DataModel;
using ADM.Infrastructure.BaseClass;
using ADM.Module.Event.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;

namespace ADM.Module.Event.ViewModels
{
public class EventLogViewModel : TabViewModelBase, IEventLogViewModel
{
private EventLogModel _model;
public EventLogViewModel()
{
Header = "Event Log";

var now = DateTime.Now;
DateFrom = new DateTime(now.Year, now.Month, now.Day, 0, 0, 0);
DateTo = new DateTime(now.Year, now.Month, now.Day, 23, 59, 59);

_model = new EventLogModel();
EventLogRecords = _model.GetEventLogsByDate(DateFrom, DateTo);

RefreshEventLogCommand = new DelegateCommand(RefreshEventLog);
}

#region Properties
private DateTime _dateFrom;
public DateTime DateFrom
{
get { return _dateFrom; }
set
{
_dateFrom = value;
SetProperty(ref _dateFrom, value);
}
}

private DateTime _dateTo;
public DateTime DateTo
{
get { return _dateTo; }
set
{
_dateTo = value;
SetProperty(ref _dateTo, value);
}
}

private string _dateRangeError;
public string DateRangeError
{
get { return _dateRangeError; }
set
{
_dateRangeError = value;
SetProperty(ref _dateRangeError, value);
}
}

private ObservableCollection<EventLogs> _eventLogRecords;
public ObservableCollection<EventLogs> EventLogRecords
{
get {
return _eventLogRecords;
}
private set {
_eventLogRecords = value;
SetProperty(ref _eventLogRecords, value);
}

}
#endregion Properties

#region Commands
public ICommand RefreshEventLogCommand { get; private set; }

public void RefreshEventLog()
{
if (ValidateDateRange(DateFrom, DateTo))
{
EventLogRecords.Clear();
EventLogRecords.AddRange(_model.GetEventLogsByDate(DateFrom, DateTo));
}
}

private bool ValidateDateRange(DateTime fromDate, DateTime toDate)
{
if (DateFrom > DateTo)
{
DateRangeError = "The From Date need to be set to before the To Date.";
return false;
}

return true;
}

#endregion Commands
}
}


using Prism;
using Prism.Mvvm;
using ADM.Infrastructure.DockManager;
using System;

namespace ADM.Infrastructure.BaseClass
{
public class TabViewModelBase : BindableBase, IActiveAware, IDockAware
{
public TabViewModelBase()
{
}

#region IActiveAware

bool _isActive;
public bool IsActive
{
get { return _isActive; }
set
{
_isActive = value;
SetProperty(ref _isActive, value);
}
}

public event EventHandler IsActiveChanged;

#endregion //IActiveAware

#region IDockAware

private string _header;
public string Header
{
get { return _header; }
set
{
_header = value;
SetProperty(ref _header, value);
}
}

#endregion //IDockAware
}
}

Answer

For one, you are setting the backfield of your properties twice. Once manually, and then again by using SetProperty. SetProperty actually checks to see if the values are the same, it they are it will not set the property or call INPC. So you are essentially setting it, and then trying to set it again, which will not happen.

Your property should be defined like this:

    private string _dateRangeError;
    public string DateRangeError
    {
        get { return _dateRangeError; }
        set { SetProperty(ref _dateRangeError, value); }
    }

Then you have the issue that your error message is the same. So once again, INPC will never be called because the value never changes. You might be better off having a different property that controls the visibility of the error instead.

Comments