Hello,
I'm stuck with a problem that I can't solve although spending hours of googling for tips on the web. It's a MVVM application where the
viewmodel has a list of entities and an implementation of INotifyPropertyChanged. The view uses RadGridView to display the entities.
The problem occurs when I'm setting a property in one of the entities in the code of the viewmodel. The property gets the value correctly but the GUI doesn't update. I'm thankful for any help I can get, have been spending hours trying to solve this wiht no success. For code excerpts, se below.
Regards, Clas
VIEW
VIEWMODEL
VIEWMODEL BASE CLASS
I'm stuck with a problem that I can't solve although spending hours of googling for tips on the web. It's a MVVM application where the
viewmodel has a list of entities and an implementation of INotifyPropertyChanged. The view uses RadGridView to display the entities.
The problem occurs when I'm setting a property in one of the entities in the code of the viewmodel. The property gets the value correctly but the GUI doesn't update. I'm thankful for any help I can get, have been spending hours trying to solve this wiht no success. For code excerpts, se below.
Regards, Clas
VIEW
<
UserControl
xmlns:telerik
=
"http://schemas.telerik.com/2008/xaml/presentation"
x:Class
=
"X.Y.Z.MonthReport.MonthReportView"
xmlns:toolkit
=
"clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
mc:Ignorable
=
"d"
d:DesignHeight
=
"300"
d:DesignWidth
=
"400"
>
<
Grid
x:Name
=
"LayoutRoot"
Background
=
"White"
>
<
Grid.RowDefinitions
>
<
RowDefinition
Height
=
"Auto"
/>
<
RowDefinition
Height
=
"Auto"
/>
<
RowDefinition
Height
=
"*"
/>
</
Grid.RowDefinitions
>
<
telerik:RadGridView
x:Name
=
"MonthReportGrid"
Grid.Row
=
"1"
ItemsSource
=
"{Binding SelectList}"
SelectedItem
=
"{Binding SelectedItem, Mode=TwoWay}"
AutoGenerateColumns
=
"False"
>
<
telerik:RadGridView.Columns
>
<!-- The other columns have been cut out of this example -->
<
telerik:GridViewDataColumn
DataMemberBinding
=
"{Binding curDate, Mode=TwoWay, TargetNullValue=''}"
DataFormatString
=
"{} {0:d}"
Header
=
"Avläst datum"
UniqueName
=
"curDate"
IsVisible
=
"True"
IsReadOnly
=
"False"
>
<
telerik:GridViewDataColumn.CellEditTemplate
>
<
DataTemplate
>
<
telerik:RadDateTimePicker
SelectedValue
=
"{Binding curDate, Mode=TwoWay, TargetNullValue=''}"
InputMode
=
"DatePicker"
DateTimeWatermarkContent
=
"ÅÅÅÅ-MM-DD"
/>
</
DataTemplate
>
</
telerik:GridViewDataColumn.CellEditTemplate
>
</
telerik:GridViewDataColumn
>
<
telerik:GridViewDataColumn
DataMemberBinding
=
"{Binding curValue, Mode=TwoWay, TargetNullValue=''}"
Header
=
"Avläst värde"
UniqueName
=
"curValue"
IsVisible
=
"True"
IsReadOnly
=
"False"
/>
</
telerik:RadGridView
>
</
Grid
>
</
UserControl
>
using
System;
using
System.Collections.Generic;
using
System.Windows.Data;
using
System.Linq;
using
System.Linq.Expressions;
using
System.Windows.Controls;
using
Telerik.Windows.Controls;
using
Telerik.Windows.Controls.GridView;
namespace
X.Y.Z.MonthReport
{
public
partial
class
MonthReportView : UserControl, IMonthReportView
{
/// <summary>
/// ViewModel attached to the View
/// </summary>
public
IMonthReportViewModel Model
{
get
{
return
this
.DataContext
as
IMonthReportViewModel; }
set
{
this
.DataContext = value; }
}
public
MonthReportView()
{
InitializeComponent();
this
.MonthReportGrid.BeginningEdit +=
new
EventHandler<GridViewBeginningEditRoutedEventArgs>(MonthReportGrid_OnBeginningEdit);
this
.MonthReportGrid.CellEditEnded +=
new
EventHandler<GridViewCellEditEndedEventArgs>(MonthReportGrid_OnCellEditEnded);
this
.MonthReportGrid.SelectionChanged +=
new
EventHandler<SelectionChangeEventArgs>(MonthReportGrid_OnSelectionChanged);
}
void
MonthReportGrid_OnBeginningEdit(
object
sender, GridViewBeginningEditRoutedEventArgs e)
{
// ...
}
public
void
MonthReportGrid_OnCellEditEnded(
object
sender, GridViewCellEditEndedEventArgs e)
{
if
(e.Cell.Column.UniqueName ==
"curValue"
)
{
// ...
this
.Model.SetAutomaticReadingDate();
}
if
(e.Cell.Column.UniqueName ==
"curDate"
)
{
this
.Model.UpdateAutomaticReadingDate();
}
}
public
void
MonthReportGrid_OnSelectionChanged(
object
sender, SelectionChangeEventArgs e)
{
// ...
}
}
}
VIEWMODEL
using
System;
using
Microsoft.Practices.Prism.Events;
using
Microsoft.Practices.Prism.Modularity;
using
Microsoft.Practices.Unity;
using
Microsoft.Practices.Prism.Commands;
namespace
X.Y.Z.MonthReport
{
public
class
MonthReportViewModel : ViewModel<IMonthReportView>, IMonthReportViewModel
{
private
readonly
IEventAggregator eventAggregator;
private
readonly
IMonthReportService dataService;
private
readonly
IMonthReportController dataController;
private
bool
isLoading;
public
bool
IsLoading
{
get
{
return
isLoading; }
set
{
if
(isLoading != value)
{
isLoading = value;
Notify(() =>
this
.IsLoading);
}
}
}
private
DateTime? _newReadingDate;
public
DateTime? NewReadingDate
{
get
{
return
_newReadingDate; }
set
{ _newReadingDate = value; }
}
private
MonthReportEntity _selectedItem;
public
MonthReportEntity SelectedItem
{
get
{
return
_selectedItem; }
set
{
if
(_selectedItem != value)
{
_selectedItem = value;
Notify(() =>
this
.SelectedItem);
}
}
}
private
MonthReportEntityCollection _selectList;
public
MonthReportEntityCollection SelectList
{
get
{
return
_selectList; }
set
{
if
(_selectList != value)
{
_selectList = value;
Notify(() =>
this
.SelectList);
}
}
}
public
MonthReportViewModel(IMonthReportView view,
IEventAggregator eventAggregator, IMonthReportService dataService, IMonthReportController dataController)
{
this
.InitializeCommands();
this
.eventAggregator = eventAggregator;
this
.dataController = dataController;
this
.dataService = dataService;
this
.View = view;
this
.View.Model =
this
;
dataService.onGetMonthReportComplete +=
new
EventHandler<MonthReportEventArgs>(OnGetMonthReportComplete);
dataService.onSaveMonthReportComplete +=
new
EventHandler<MonthReportEventArgs>(OnSaveMonthReportComplete);
InitializeData();
}
public
void
InitializeCommands()
{
// ...
}
public
void
InitializeData()
{
GetMonthReport();
this
.IsLoading =
true
;
}
public
void
GetMonthReport()
{
dataService.GetMonthReport();
}
public
void
SaveMonthReport()
{
dataService.SaveMonthReport(SelectList);
}
public
void
UpdateAutomaticReadingDate()
{
if
(SelectedItem.curDate.HasValue)
NewReadingDate = SelectedItem.curDate;
}
public
void
SetAutomaticReadingDate()
{
if
((NewReadingDate.HasValue) && (!SelectedItem.curDate.HasValue))
{
SelectedItem.curDate = NewReadingDate;
Notify(() =>
this
.SelectedItem.curDate);
}
}
void
OnGetMonthReportComplete(
object
sender, MonthReportEventArgs e)
{
if
(!e.HasError)
this
.SelectList = e.MonthReportData;
else
{
this
.SelectList =
null
;
eventAggregator.GetEvent<ErrorEvent>().Publish(
new
ErrorEvent
{
Message = e.Error.ToString(),
ExceptionObj = e.Error
});
}
this
.IsLoading =
false
;
}
void
OnSaveMonthReportComplete(
object
sender, MonthReportEventArgs e)
{
if
(!e.HasError)
{
// Is it really necessary to re-set the SelectList with the same data again?
//this.SelectList = e.MonthReportData;
eventAggregator.GetEvent<MessageShowEvent>().Publish(
new
MessageShowEvent
{
MessageTitle =
"Bekräftelse"
,
Message =
"Månadsrapporteringen sparades."
});
}
else
{
this
.SelectList =
null
;
eventAggregator.GetEvent<ErrorEvent>().Publish(
new
ErrorEvent
{
Message = e.Error.ToString(),
ExceptionObj = e.Error
});
}
this
.IsLoading =
false
;
}
#region ICleanable
public
override
void
Clean()
{
base
.Clean();
//Remove existing references, for example eventhandlers added in constructor
//Unsubscribe to subscribed events.
}
#endregion
}
}
VIEWMODEL BASE CLASS
using
System;
using
System.ComponentModel;
using
System.Linq.Expressions;
using
System.Windows;
using
System.Windows.Threading;
using
X.Y.Z.Common.Extensions;
namespace
X.Y.Z.Common
{
public
class
ViewModel<TView> : ViewModel
{
public
TView View {
get
;
protected
set
; }
public
override
void
Clean()
{
base
.Clean();
if
(View
is
FrameworkElement)
{
FrameworkElement element = View
as
FrameworkElement;
element.RemoveFromParentPanel();
}
}
}
public
class
ViewModel : INotifyPropertyChanged, IDisposable, ICleanable
{
public
ViewModel() { }
public
void
Notify<TValue>(Expression<Func<TValue>> propertySelector)
{
if
(PropertyChanged !=
null
)
{
var memberExpression = propertySelector.Body
as
MemberExpression;
if
(memberExpression !=
null
)
{
NotifyPropertyChanged(memberExpression.Member.Name);
}
}
}
public
event
PropertyChangedEventHandler PropertyChanged =
delegate
{ };
protected
void
NotifyPropertyChanged(
string
propertyName)
{
PropertyChangedEventHandler handler =
this
.PropertyChanged;
if
(handler !=
null
)
{
var e =
new
PropertyChangedEventArgs(propertyName);
handler(
this
, e);
}
}
public
void
Dispose()
{
this
.Clean();
}
public
virtual
void
Clean()
{
}
#if DEBUG
~ViewModel()
{
string
msg =
string
.Format(
"{0} ({1}) Finalized"
,
this
.GetType().Name,
this
.GetHashCode());
System.Diagnostics.Debug.WriteLine(msg);
}
#endif
}
}