Create Wizard with MVVM Pattern - Databinding and GUI Update not working - mvvm

I am creating a Wizard for automatic Report generating. Therefore the user enters a couple of Issues to the Mainwindow and after he finished, he can create an automatically filled report by clicking a button. Also he need to switch between the entered Issuses (Therefore I put the issues to a list of the Type "Compliant".
The MainWindow contains different controls, in which the user can enter some information.
All controls are binded to the Model like this way (example for a textbox):
Text="{Binding Path=SingleCompliant.Text, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
"SingleCompliant" is an object of the ViewModel of the type "Compliant" (part of the Model), which contains all attributes needed for one Issue.
When I load the Application for the first time, everything works fine.
But when I click on "SaveCompliantAndNext", the binding between "SingleCompliant" Object gets lost and the GUI is not updating. In this Method (raised by a command) I create a new SingleCompliant Object.
What do I need to do for getting a new Object with an empty gui, so the user can continue entereing the next Issue?
This is the ViewModel with implemented "PropertyChanged" Handling:
public class Compliant_ViewModel : ObservableCollection<Compliant> //INotifyPropertyChanged
{
/// <summary>
/// MainWindow Controls are binded to Attributes of this object
/// </summary>
private Compliant _SingleCompliant;
public ObservableCollection<Compliant> CompliantListReport
{
get { return _CompliantListReport; }
private set { _CompliantListReport = value; }
}
/// <summary>
/// Beandstandungs-Objekt
/// </summary>
public Compliant SingleCompliant
{
get { return _SingleCompliant; }
set
{ _SingleCompliant = value;
OnPropertyChanged("SingleCompliant");
}
}
/// <summary>
/// Load next Compliant to the GUI
/// </summary>
public NextCompliant_Command NextCompliant_command { get; private set; }
/// <summary>
/// Load previous Compliant to the GUI
/// </summary>
public PreviousCompliant_Command PreviousCompliant_Command { get; private set; }
/// <summary>
/// Constructor
/// </summary>
public Compliant_ViewModel()
{
SingleCompliant = new Compliant();
CompliantListReport = new ObservableCollection<Compliant>();
NextCompliant_command = new NextCompliant_Command(SaveCompliantAndNext);
PreviousCompliant_Command = new PreviousCompliant_Command(LoadPreviousCompliant);
CreateReport_Command = new CreateReport_Command(CreateReport);
}
public void SaveCompliantAndNext()
{
CompliantListReport.Add(SingleCompliant);
// >>>>> Not working - databinding get lost and gui not updating
SingleCompliant = new Compliant();
}
public void LoadPreviousCompliant()
{
if (this.SingleCompliant.CompliantID > 0)
{ this.SingleCompliant = this.CompliantListReport[this.SingleCompliant.CompliantID - 1]; }
else
{ this.SingleCompliant = this.CompliantListReport[0]; }
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyname)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{ handler(this, new PropertyChangedEventArgs(propertyname)); }
}
}
This is the Model:
public class Compliant : INotifyPropertyChanged
{
public string _Text;
public string Text
{
get { return _Text; }
private set
{
_Text = value;
OnPropertyChanged("Text");
}
}
#region Konstruktoren
public Compliant()
{ }
#region Interface
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyname)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyname));
}
}
#endregion Interface
}
XAML:
Example for one Textbox:
<TextBox Text="{Binding Path=SingleCompliant.Text, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" x:Name="Text" />
XAML Buttons to switch between the Compliants (Issues):
<!--Previous Compliant (Issue)-->
<Button x:Name="BtnBack_Compliant" Content="{DynamicResource Back}" Command="{Binding PreviousCompliant_Command}"/>
<!--Next Compliant (Issue)-->
<Button x:Name="BtnForeward_Compliant" Content="{DynamicResource Foreward}" Command="{Binding NextCompliant_command}" />
What do I make wrong?

Related

Xamarin Forms MVVM Databinding failing when I'm binding to a single object

I'm having an issue with data not binding correctly on a details page when I have clicked through from a ListView via a button. The ListView binds perfectly and the object gets passed through to the details page. The Id of the object is read and a full version of the object is called from an API and set to a new instance of the object. When I add a breakpoint, the full object is available, but Labels on the view aren't populated. Here is the ViewModel:
DetailsViewModel.cs
public class DetailsViewModel
{
public Deal Deal { get; set; }
public int DealId { get; set; }
public DetailsViewModel(int id)
{
Deal = new Deal();
DealId = id;
}
public async void GetDeal()
{
var deal = await Deal.GetDeal(DealId);
if(deal != null)
{
Deal = deal;
}
}
}
The codebehind looks like this:
DetailPage.Xaml.cs
DetailsViewModel viewModel;
int dealId;
public DetailPage(int id)
{
InitializeComponent();
dealId = id;
viewModel = new DetailsViewModel(dealId);
BindingContext = viewModel;
}
protected override void OnAppearing()
{
base.OnAppearing();
viewModel.GetDeal();
}
And the Xaml file is
DetailPage.Xaml
<ContentPage.Content>
<ScrollView>
<StackLayout x:Name="detailsLayout">
<Label Text="{Binding Deal.Name}" />
</StackLayout>
</ScrollView>
</ContentPage.Content>
When I put a breakpoint in Deal = deal on DetailsViewModel, the Deal object exists and has the correct data, but I just get a blank screen. I have tried Labels with Text="{Binding Name}" and Text="{Binding Deal.Name}".
I have also tried manually creating a deal in the GetDeal function of the ViewModel and still nothing is bound.
1) Ensure your property Notifies the UI of a change implementing the INotifyPropertyChanged interface. See https://learn.microsoft.com/en-us/xamarin/xamarin-forms/xaml/xaml-basics/data-bindings-to-mvvm
2) Ensure the set is done on the UI thread using Device.BeginInvokeOnMainThread. https://learn.microsoft.com/fr-fr/dotnet/api/xamarin.forms.device.begininvokeonmainthread?view=xamarin-forms
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Xamarin.Forms;
namespace YourNamespace
{
public class DetailsViewModel : INotifyPropertyChanged
{
private Deal _deal;
public Deal Deal
{
get => _deal;
set
{
if (_deal != value)
{
_deal = value;
OnPropertyChanged();
}
}
}
public int DealId { get; set; }
public DetailsViewModel(int id)
{
//!! useless assignation
//Deal = new Deal();
DealId = id;
}
public async void GetDeal()
{
var deal = await Deal.GetDeal(DealId);
if (deal != null)
{
//Ensure we are on UI thread
Device.BeginInvokeOnMainThread(() => Deal = deal);
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

WPF MVVM Command CanExecute, reevaluates only at focus change

Refactoring an MVVM project in WPF, I'm trying to get rid of what seems a common problem between MVVM pattern users.
I have view, who's DataContext is MyViewModel. Here is a button, bound with a Command that implements both Execute and CanExecute.
XAML:
<Button Command="{Binding ConnectCommand}"/>
MyViewModel exposes ConnectCommand:
public ICommand ConnectCommand
{
get { return new DelegateCommand(() => Connect(), () => IsConnectEnabled); }
}
(at the end the definition of DelegateCommand I'm using)
MyViewModel also exposes the property IsConnectEnabled, used in the CanExecute part of the Command:
public bool IsConnectEnabled
{
get
{
return (isDisconnected && null!=selectedDevice && 0<selectedDevice.Length);
}
}
MyViewModel class implements the INotifyPropertyChanged interface
public class MyViewModel : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
#region rest of the class
}
The CanExecute part of the command is only evaluated on a change of focus in the application (ie, whatever click I do). I know that the UpdateSourceTrigger is by default set to PropertyChanged, therefore my current solution, is to manually raise a PropertyChanged event in a few places in the code. But I want to do better and have this activity done automatically whenever the value of IsConnectEnabled changes.
Does the WPF and the MVVM pattern offer a solution for this issue?
For completeness, follows the complete ICommand implementation I'm using, DelegateCommand:
/// <summary>
/// This class allows delegating the commanding logic to methods passed as parameters,
/// and enables a View to bind commands to objects that are not part of the element tree.
/// </summary>
public class DelegateCommand : ICommand
{
/// <summary>
/// Constructor
/// </summary>
public DelegateCommand(Action executeMethod)
: this(executeMethod, null, false)
{
}
/// <summary>
/// Constructor
/// </summary>
public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod)
: this(executeMethod, canExecuteMethod, false)
{
}
/// <summary>
/// Constructor
/// </summary>
public DelegateCommand(Action executeMethod, Func<bool> canExecuteMethod, bool isAutomaticRequeryDisabled)
{
if (executeMethod == null)
{
throw new ArgumentNullException("executeMethod");
}
_executeMethod = executeMethod;
_canExecuteMethod = canExecuteMethod;
_isAutomaticRequeryDisabled = isAutomaticRequeryDisabled;
}
#region Public Methods
/// <summary>
/// Method to determine if the command can be executed
/// </summary>
public bool CanExecute()
{
if (_canExecuteMethod != null)
{
return _canExecuteMethod();
}
return true;
}
/// <summary>
/// Execution of the command
/// </summary>
public void Execute()
{
if (_executeMethod != null)
{
_executeMethod();
}
}
/// <summary>
/// Property to enable or disable CommandManager's automatic requery on this command
/// </summary>
public bool IsAutomaticRequeryDisabled
{
get
{
return _isAutomaticRequeryDisabled;
}
set
{
if (_isAutomaticRequeryDisabled != value)
{
if (value)
{
CommandManagerHelper.RemoveHandlersFromRequerySuggested(_canExecuteChangedHandlers);
}
else
{
CommandManagerHelper.AddHandlersToRequerySuggested(_canExecuteChangedHandlers);
}
_isAutomaticRequeryDisabled = value;
}
}
}
/// <summary>
/// Raises the CanExecuteChaged event
/// </summary>
public void RaiseCanExecuteChanged()
{
OnCanExecuteChanged();
}
/// <summary>
/// Protected virtual method to raise CanExecuteChanged event
/// </summary>
protected virtual void OnCanExecuteChanged()
{
CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers);
}
#endregion
#region ICommand Members
/// <summary>
/// ICommand.CanExecuteChanged implementation
/// </summary>
public event EventHandler CanExecuteChanged
{
add
{
if (!_isAutomaticRequeryDisabled)
{
CommandManager.RequerySuggested += value;
}
CommandManagerHelper.AddWeakReferenceHandler(ref _canExecuteChangedHandlers, value, 2);
}
remove
{
if (!_isAutomaticRequeryDisabled)
{
CommandManager.RequerySuggested -= value;
}
CommandManagerHelper.RemoveWeakReferenceHandler(_canExecuteChangedHandlers, value);
}
}
bool ICommand.CanExecute(object parameter)
{
return CanExecute();
}
void ICommand.Execute(object parameter)
{
Execute();
}
#endregion
#region Data
private readonly Action _executeMethod = null;
private readonly Func<bool> _canExecuteMethod = null;
private bool _isAutomaticRequeryDisabled = false;
private List<WeakReference> _canExecuteChangedHandlers;
#endregion
}
/// <summary>
/// This class allows delegating the commanding logic to methods passed as parameters,
/// and enables a View to bind commands to objects that are not part of the element tree.
/// </summary>
/// <typeparam name="T">Type of the parameter passed to the delegates</typeparam>
public class DelegateCommand<T> : ICommand
{
#region Constructors
/// <summary>
/// Constructor
/// </summary>
public DelegateCommand(Action<T> executeMethod)
: this(executeMethod, null, false)
{
}
/// <summary>
/// Constructor
/// </summary>
public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod)
: this(executeMethod, canExecuteMethod, false)
{
}
/// <summary>
/// Constructor
/// </summary>
public DelegateCommand(Action<T> executeMethod, Func<T, bool> canExecuteMethod, bool isAutomaticRequeryDisabled)
{
if (executeMethod == null)
{
throw new ArgumentNullException("executeMethod");
}
_executeMethod = executeMethod;
_canExecuteMethod = canExecuteMethod;
_isAutomaticRequeryDisabled = isAutomaticRequeryDisabled;
}
#endregion
#region Public Methods
/// <summary>
/// Method to determine if the command can be executed
/// </summary>
public bool CanExecute(T parameter)
{
if (_canExecuteMethod != null)
{
return _canExecuteMethod(parameter);
}
return true;
}
/// <summary>
/// Execution of the command
/// </summary>
public void Execute(T parameter)
{
if (_executeMethod != null)
{
_executeMethod(parameter);
}
}
/// <summary>
/// Raises the CanExecuteChaged event
/// </summary>
public void RaiseCanExecuteChanged()
{
OnCanExecuteChanged();
}
/// <summary>
/// Protected virtual method to raise CanExecuteChanged event
/// </summary>
protected virtual void OnCanExecuteChanged()
{
CommandManagerHelper.CallWeakReferenceHandlers(_canExecuteChangedHandlers);
}
/// <summary>
/// Property to enable or disable CommandManager's automatic requery on this command
/// </summary>
public bool IsAutomaticRequeryDisabled
{
get
{
return _isAutomaticRequeryDisabled;
}
set
{
if (_isAutomaticRequeryDisabled != value)
{
if (value)
{
CommandManagerHelper.RemoveHandlersFromRequerySuggested(_canExecuteChangedHandlers);
}
else
{
CommandManagerHelper.AddHandlersToRequerySuggested(_canExecuteChangedHandlers);
}
_isAutomaticRequeryDisabled = value;
}
}
}
#endregion
#region ICommand Members
/// <summary>
/// ICommand.CanExecuteChanged implementation
/// </summary>
public event EventHandler CanExecuteChanged
{
add
{
if (!_isAutomaticRequeryDisabled)
{
CommandManager.RequerySuggested += value;
}
CommandManagerHelper.AddWeakReferenceHandler(ref _canExecuteChangedHandlers, value, 2);
}
remove
{
if (!_isAutomaticRequeryDisabled)
{
CommandManager.RequerySuggested -= value;
}
CommandManagerHelper.RemoveWeakReferenceHandler(_canExecuteChangedHandlers, value);
}
}
bool ICommand.CanExecute(object parameter)
{
// if T is of value type and the parameter is not
// set yet, then return false if CanExecute delegate
// exists, else return true
if (parameter == null &&
typeof(T).IsValueType)
{
return (_canExecuteMethod == null);
}
return CanExecute((T)parameter);
}
void ICommand.Execute(object parameter)
{
Execute((T)parameter);
}
#endregion
#region Data
private readonly Action<T> _executeMethod = null;
private readonly Func<T, bool> _canExecuteMethod = null;
private bool _isAutomaticRequeryDisabled = false;
private List<WeakReference> _canExecuteChangedHandlers;
#endregion
}
/// <summary>
/// This class contains methods for the CommandManager that help avoid memory leaks by
/// using weak references.
/// </summary>
internal class CommandManagerHelper
{
internal static void CallWeakReferenceHandlers(List<WeakReference> handlers)
{
if (handlers != null)
{
// Take a snapshot of the handlers before we call out to them since the handlers
// could cause the array to me modified while we are reading it.
EventHandler[] callees = new EventHandler[handlers.Count];
int count = 0;
for (int i = handlers.Count - 1; i >= 0; i--)
{
WeakReference reference = handlers[i];
EventHandler handler = reference.Target as EventHandler;
if (handler == null)
{
// Clean up old handlers that have been collected
handlers.RemoveAt(i);
}
else
{
callees[count] = handler;
count++;
}
}
// Call the handlers that we snapshotted
for (int i = 0; i < count; i++)
{
EventHandler handler = callees[i];
handler(null, EventArgs.Empty);
}
}
}
internal static void AddHandlersToRequerySuggested(List<WeakReference> handlers)
{
if (handlers != null)
{
foreach (WeakReference handlerRef in handlers)
{
EventHandler handler = handlerRef.Target as EventHandler;
if (handler != null)
{
CommandManager.RequerySuggested += handler;
}
}
}
}
internal static void RemoveHandlersFromRequerySuggested(List<WeakReference> handlers)
{
if (handlers != null)
{
foreach (WeakReference handlerRef in handlers)
{
EventHandler handler = handlerRef.Target as EventHandler;
if (handler != null)
{
CommandManager.RequerySuggested -= handler;
}
}
}
}
internal static void AddWeakReferenceHandler(ref List<WeakReference> handlers, EventHandler handler)
{
AddWeakReferenceHandler(ref handlers, handler, -1);
}
internal static void AddWeakReferenceHandler(ref List<WeakReference> handlers, EventHandler handler, int defaultListSize)
{
if (handlers == null)
{
handlers = (defaultListSize > 0 ? new List<WeakReference>(defaultListSize) : new List<WeakReference>());
}
handlers.Add(new WeakReference(handler));
}
internal static void RemoveWeakReferenceHandler(List<WeakReference> handlers, EventHandler handler)
{
if (handlers != null)
{
for (int i = handlers.Count - 1; i >= 0; i--)
{
WeakReference reference = handlers[i];
EventHandler existingHandler = reference.Target as EventHandler;
if ((existingHandler == null) || (existingHandler == handler))
{
// Clean up old handlers that have been collected
// in addition to the handler that is to be removed.
handlers.RemoveAt(i);
}
}
}
}
}
}
This is an old buggy behaviour, at least in my WPF experience.
I have found that the MvvmLight implementation of RelayCommand (GalaSoft.MvvmLight.CommandWpf.RelayCommand) seems to solve this issue.
Notice that they even create a WPF specific implementation (!) that apparently is targeted to correct this weirdness.

Xamarin Forms iOS Validate Texbox and show error

I've customized the Xamarin Forms Entry control for Android,
I'm using MVVM Validation for validating my Inputs via MVVM and generate a message for each form element.
Validator.AddRequiredRule(() => UserName, Messages.FieldCannotBlank);
Validator.AddRequiredRule(() => Password, Messages.FieldCannotBlank);
The customised entry I've used is an extended version of the Extended Entry by Xlabs. Here's the code for the Entry
public class ExtendedEntry : Entry
{
public ExtendedEntry()
{
BorderColor = Color.Default;
BGColorForRounded = Color.Default;
}
#region XAlign
/// <summary>
/// The XAlign property
/// </summary>
public static readonly BindableProperty XAlignProperty =
BindableProperty.Create(nameof(XAlign), typeof(TextAlignment), typeof(ExtendedEntry),
TextAlignment.Start);
/// <summary>
/// Gets or sets the X alignment of the text
/// </summary>
public TextAlignment XAlign
{
get { return (TextAlignment)GetValue(XAlignProperty); }
set { SetValue(XAlignProperty, value); }
}
#endregion
#region BGColorForRounded
public static BindableProperty BGColorForRoundedProperty =
BindableProperty.Create(nameof(BGColorForRounded), typeof(Color), typeof(ExtendedEntry), default(Color));
public Color BGColorForRounded
{
get { return (Color)GetValue(BGColorForRoundedProperty); }
set { SetValue(BGColorForRoundedProperty, value); }
}
#endregion
#region Padding
public static BindableProperty PaddingProperty =
BindableProperty.Create(nameof(Padding), typeof(Thickness), typeof(ExtendedEntry), default(Thickness), defaultBindingMode: BindingMode.OneWay);
public Thickness Padding
{
get { return (Thickness)GetValue(PaddingProperty); }
set { SetValue(PaddingProperty, value); }
}
#endregion Padding
#region IsReadOnly
public static readonly BindableProperty IsReadOnlyProperty =
BindableProperty.Create("IsReadOnly", typeof(bool), typeof(ExtendedEntry), false);
public bool IsReadOnly
{
get { return (bool)GetValue(IsReadOnlyProperty); }
set { SetValue(IsReadOnlyProperty, value); }
}
#endregion
#region IsValid
public static readonly BindableProperty IsValidProperty =
BindableProperty.Create("IsValid", typeof(bool), typeof(ExtendedEntry), true);
public bool IsValid
{
get { return (bool)GetValue(IsValidProperty); }
set { SetValue(IsValidProperty, value); }
}
#endregion
#region ErrorHint
public static readonly BindableProperty ErrorHintProperty =
BindableProperty.Create("ErrorHint", typeof(string), typeof(ExtendedEntry), "");
public string ErrorHint
{
get { return (string)GetValue(ErrorHintProperty); }
set { SetValue(ErrorHintProperty, value); }
}
#endregion
#region PlaceholderTextColor
/// <summary>
/// The PlaceholderTextColor property
/// </summary>
public static readonly BindableProperty PlaceholderTextColorProperty =
BindableProperty.Create("PlaceholderTextColor", typeof(Color), typeof(ExtendedEntry), Color.Default);
/// <summary>
/// Sets color for placeholder text
/// </summary>
public Color PlaceholderTextColor
{
get { return (Color)GetValue(PlaceholderTextColorProperty); }
set { SetValue(PlaceholderTextColorProperty, value); }
}
#endregion
#region HasBorder
/// <summary>
/// The HasBorder property
/// </summary>
public static readonly BindableProperty HasBorderProperty =
BindableProperty.Create("HasBorder", typeof(bool), typeof(ExtendedEntry), true);
/// <summary>
/// Gets or sets if the border should be shown or not
/// </summary>
public bool HasBorder
{
get { return (bool)GetValue(HasBorderProperty); }
set { SetValue(HasBorderProperty, value); }
}
#endregion
#region BorderRadius
public int BorderRadius
{
get { return (int)GetValue(BorderRadiusProperty); }
set { SetValue(BorderRadiusProperty, value); }
}
// Using a DependencyProperty as the backing store for BorderRadius. This enables animation, styling, binding, etc...
public static readonly BindableProperty BorderRadiusProperty =
BindableProperty.Create("BorderRadius", typeof(int), typeof(ExtendedEntry), 0);
#endregion
#region BorderColor
/// <summary>
/// The Border Color Property
/// </summary>
public static readonly BindableProperty BorderColorProperty =
BindableProperty.Create("BorderColor", typeof(Color), typeof(ExtendedEntry), default(Color));
/// <summary>
/// Gets or sets the border color
/// </summary>
public Color BorderColor
{
get { return (Color)GetValue(BorderColorProperty); }
set { SetValue(BorderColorProperty, value); }
}
#endregion
}
Whenever a button is pressed, I call the Validators ValidateAll method to validate all my fields
var result = Validator.ValidateAll();
if (!result.IsValid)
{
App.Current.MainPage.DisplayAlert("Error", "The forms has errors, please review the errors and fix them.", "Ok");
}
Errors = result.AsDictionary();
Errors is another property of the Viewmodel which is basically a dictionary which will hold a list of key-value pairs of property names and errors after validation. I map this value to the ErrorHint property of my ExtendedEntry in Xaml
<controls:ExtendedEntry ErrorHint="{Binding Errors[UserName]}"
IsValid="{Binding Errors[UserName], Converter={StaticResource DictBool},
Mode=TwoWay}"
Placeholder="sallysmith#gmail.com"
Style="{DynamicResource WhiteEntryStandard}"
Text="{Binding UserName}" />
And to finish off, this is the implementation for Android
private void SetValidityAndHint(ExtendedEntry view)
{
if (!view.IsValid)
{
if (!string.IsNullOrEmpty(view.ErrorHint))
{
Control.Error = view.ErrorHint;
}
}
}
This works fine for Android. But I haven't been able to find a solution for iOS. It looks like iOS doesn't have any kind of error property at all. Any pointers anyone??

this.VerifyPropertyName(propertyName); in OnPropertyChanged() always returns null

I have a xaml code as follows:
On MouseLeftButtonDown i redirect it to viewModel where the color GenerateGlowEffect must change. This is not reflected. It always returns null for this.PropertyChanged
XAML:
<UserControl
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:ed="http://schemas.microsoft.com/expression/2010/drawing"
xmlns:telerikDocking="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls.Docking"
xmlns:Telerik_Windows_Controls="clr-namespace:Telerik.Windows.Controls;assembly=Telerik.Windows.Controls"
xmlns:System="clr-namespace:System;assembly=mscorlib" x:Class="TabItemContents.MapDetail"
mc:Ignorable="d" Width="Auto" Height="430.333">
<Grid x:Name="LayoutRoot" Background="{StaticResource TabControlActiveAreaColor}" Height="430.333" VerticalAlignment="Top" HorizontalAlignment="Left">
<Grid.RowDefinitions>
<RowDefinition Height="192.833"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Rectangle IsHitTestVisible="True" x:Name="Rectangle_Generate" Margin="25.334,70.167,0,65.333" Cursor="{DynamicResource HandCursor}" RadiusX="10" RadiusY="10" HorizontalAlignment="Left" Width="88.5" StrokeThickness="1.5" d:LayoutOverrides="HorizontalAlignment" MouseLeftButtonDown="Rectangle_Generate_MouseLeftButtonDown" >
<Rectangle.Fill>
<SolidColorBrush Color="{DynamicResource RectangleColor}"/>
</Rectangle.Fill>
<Rectangle.Effect >
<DropShadowEffect RenderingBias="Quality" BlurRadius="{DynamicResource BlurRadius}" ShadowDepth="0" Color="{Binding GenerateGlowEffect}" />
</Rectangle.Effect>
</Rectangle>
</Grid>
CODEBEHIND:
public partial class MapDetail : UserControl
{
ViewModel ViewModelObject;
public MapDetail()
{
InitializeComponent();
ViewModelObject= new ViewModelObject((IUnityContainer)Application.Current.Properties["UnityContainer"]);
}
private void Rectangle_Generate_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
ViewModelObject.SetColor();
}
}
VIEWMODEL:
public class ViewModel : ViewModelBase
{
private IUnityContainer _unityContainer;
private ILogger _logger;
private Model model;
private string _generateGlowEffect;
public ViewModel(IUnityContainer unityContainer)
{
_unityContainer = unityContainer;
try
{
model = new Model(unityContainer);
}
catch (Exception ex)
{
throw ex;
}
}
public void SetColor()
{
GenerateGlowEffect = "White";
}
public string GenerateGlowEffect
{
get { return _generateGlowEffect; }
set {
_generateGlowEffect = value;
OnPropertyChanged("GenerateGlowEffect");
}
}
}
}
public string GenerateGlowEffect
{
get { return _generateGlowEffect; }
set {
_generateGlowEffect = value;
OnPropertyChanged("GenerateGlowEffect");
}
}
ViewModelBase:
public class ViewModelBase : INotifyPropertyChanged, IDisposable
{
#region Constructor
protected ViewModelBase()
{
}
#endregion // Constructor
#region DisplayName
/// <summary>
/// Returns the user-friendly name of this object.
/// Child classes can set this property to a new value,
/// or override it to determine the value on-demand.
/// </summary>
public virtual string DisplayName { get; protected set; }
#endregion // DisplayName
#region Debugging Aides
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
else
Debug.Fail(msg);
}
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail() is used
/// when an invalid property name is passed to the VerifyPropertyName method.
/// The default value is false, but subclasses used by unit tests might
/// override this property's getter to return true.
/// </summary>
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion // Debugging Aides
#region INotifyPropertyChanged Members
/// <summary>
/// Raised when a property on this object has a new value.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Raises this object's PropertyChanged event.
/// </summary>
/// <param name="propertyName">The property that has a new value.</param>
protected virtual void OnPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
#endregion // INotifyPropertyChanged Members
#region IDisposable Members
/// <summary>
/// Invoked when this object is being removed from the application
/// and will be subject to garbage collection.
/// </summary>
public void Dispose()
{
this.OnDispose();
}
/// <summary>
/// Child classes can override this method to perform
/// clean-up logic, such as removing event handlers.
/// </summary>
protected virtual void OnDispose()
{
}
#endregion // IDisposable Members
}
}
If this.PropertyChanged is null it means that your XAML is not bound against the property.
You need to set the DataContext otherwise the WPF engine doesn't know what to bind against.
public MapDetail()
{
InitializeComponent();
this.ViewModelObject= new VViewModelObject((IUnityContainer)Application.Current.Properties["UnityContainer"]);
this.DataContext = this.ViewModelObject;
}

PropertyChangedEventHandler is null in FirePropertyChanged

I have a viewmodel named EmployeeViewModel which is inherited from ViewModelBase. here is the implementation of ViewModelBase.
public event PropertyChangedEventHandler PropertyChanged;
public void FirePropertyChanged(string propertyname)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyname));
}
public void FirePropertyChanged<TValue>(Expression<Func<TValue>> propertySelector)
{
if (PropertyChanged != null)
{
var memberExpression = propertySelector.Body as MemberExpression;
if (memberExpression != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(memberExpression.Member.Name));
}
}
}
My EmployeeViewModel has a property name GridResults, which is bound to the Grid on View, here is the property definition.
public PagedCollectionView GridResults
{
get { return _gridResults; }
set
{
_gridResults = value;
FirePropertyChanged(()=>GridResults);
}
}
Now when i set value of GridResults somewhere in the code in EmployeeViewModel, it fires the property change event and goes into
FirePropertyChanged(Expression> propertySelector)
but inside that method its PropertyChangedEventHandler always remain null and it prevents the complete execution of method. Ultimately my Grid on View remain unnoticed that its underlying itemsource has been changed.
Am i missing something??
Thankx in advance
-K9
Did your ViewModelBase really implement INotifyPropertyChanged?
if yes, please try the FirePropertyChanged with string parameter.
public PagedCollectionView GridResults
{
get { return _gridResults; }
set
{
_gridResults = value;
FirePropertyChanged("GridResults");
}
}
btw here is the INPCBase class i use:
/// <summary>
/// Basisklasse für INotifyPropertyChanged.
/// </summary>
public class INPCBase : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Notify mittels PropertyInfo. HINWEIS: diese Variante ist ungefähr 3x langsamer wie
/// <see cref="NotifyPropertyChanged(string)"/> bzw. <see cref="NotifyPropertyChanged(System.ComponentModel.PropertyChangedEventArgs)"/>.
/// </summary>
/// <example>
/// <code>
/// public string InfoMessage
/// {
/// get {return this.infomessage;}
/// set
/// {
/// this.infomessage = value;
/// this.NotifyPropertyChanged(()=> this.InfoMessage);
/// }
/// }
/// </code>
/// </example>
/// <typeparam name="T"></typeparam>
/// <param name="property"></param>
protected void NotifyPropertyChanged<T>(Expression<Func<T>> property)
{
var propertyInfo = ((MemberExpression)property.Body).Member as PropertyInfo;
if (propertyInfo == null)
{
throw new ArgumentException("The lambda expression 'property' should point to a valid Property");
}
this.VerifyPropertyName(propertyInfo.Name);
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyInfo.Name));
}
/// <summary>
/// Notify using pre-made PropertyChangedEventArgs
/// </summary>
/// <param name="args"></param>
protected void NotifyPropertyChanged(PropertyChangedEventArgs args)
{
this.VerifyPropertyName(args.PropertyName);
var handler = PropertyChanged;
if (handler != null)
{
handler(this, args);
}
}
/// <summary>
/// Notify using String property name
/// </summary>
protected void NotifyPropertyChanged(String propertyName)
{
this.VerifyPropertyName(propertyName);
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
#region Debugging Aides
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG")]
[DebuggerStepThrough]
public void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] != null)
return;
var msg = "Invalid property name: " + propertyName;
if (this.ThrowOnInvalidPropertyName)
throw new Exception(msg);
Debug.Fail(msg);
}
/// <summary>
/// Returns whether an exception is thrown, or if a Debug.Fail() is used
/// when an invalid property name is passed to the VerifyPropertyName method.
/// The default value is false, but subclasses used by unit tests might
/// override this property's getter to return true.
/// </summary>
protected virtual bool ThrowOnInvalidPropertyName { get; private set; }
#endregion // Debugging Aides
}