I am currently trying to find a way to check if a parent property is null before binding to a sub property, like this:
...{Binding Item.Text}
Is there a way to check if Item is null before accessing the Text property? As it stands right now I get a NullReferenceException in PresentationFramework.dll which crashes the app.
This is particularly strange as I set the Item in the ViewModel constructor and have verified that it exists before the rendering step begins:
public MyViewModel()
{
Item = new Foo();
Item.Text = "Bar";
}
I would suggest creating a viewmodel property for the model property you want to expose.
Since the example you've shown works for me..
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new MyViewModel();
}
}
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private Foo item;
public MyViewModel()
{
Item = new Foo();
Item.Item = "Bar";
}
public Foo Item
{
get
{
return this.item;
}
set
{
this.item = value;
RaisePropertyChanged("Item");
}
}
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class Foo
{
public string Item { get; set; }
}
<TextBox Width="200" Height="30" Text="{Binding Item.Item, Mode=TwoWay}" TextAlignment="Center"/>
You can expose the subproperty and perform the checking like this..
public class MyViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private Foo item;
public MyViewModel()
{
//Item = new Foo();
//Item.Item = "Bar";
}
public Foo Item
{
get
{
return this.item;
}
set
{
this.item = value;
RaisePropertyChanged("Item");
}
}
public string Bar
{
get
{
if(Item == null)
{
return "Item is null. OMG!";
}
else
{
return Item.Item;
}
}
set
{
Item.Item = value;
RaisePropertyChanged("Bar");
}
}
private void RaisePropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
<TextBox Width="200" Height="30" Text="{Binding Bar}" TextAlignment="Center"/>
With this, you might want to change your view model design.
Related
I'm using this technique for navigation between views: https://social.technet.microsoft.com/wiki/contents/articles/30898.simple-navigation-technique-in-wpf-using-mvvm.aspx
I have the main ViewModel with menu buttons bound to SelectedViewModel property change commands:
class MainViewModel : INotifyPropertyChanged
{
public ICommand SomeViewCommand { get; set; }
public ICommand OtherViewCommand { get; set; }
private object selectedViewModel;
public event PropertyChangedEventHandler PropertyChanged;
public object SelectedViewModel
{
get { return selectedViewModel; }
set { selectedViewModel = value; OnPropertyChanged("SelectedViewModel"); }
}
public MainViewModel()
{
SomeViewCommand = new RelayCommand<object, object>(null, (object o) => OpenSomeView());
OtherViewCommand = new RelayCommand<object, object>(null, (object o) => OpenOtherView());
}
private void OpenSomeView()
{
SelectedViewModel = new SomeViewModel();
}
private void OpenOtherView(object obj)
{
if(SelectedViewModel != null && SelectedViewModel.GetType() == typeof(SomeViewModel))
{
SomeViewModel s = (SomeViewModel)SelectedViewModel;
// always 0
if (s.NumberOfChanges > 0)
{
MessageBox.Show("test", "Error");
}
// SelectedViewModel = new OtherViewModel(); after confirmation dialog
}
else
SelectedViewModel = new OtherViewModel();
}
private void OnPropertyChanged(string propName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
If I'm in SomeView, I'd like to check its property (number of changes) before switching to OtherView and show a confirmation dialog to the user to confirm their action. I need the current value, but any property seems to have its initialization value. Why?
What would be the cleanest way of doing this? I know it can be done by making the property static, but that seems dirty to me.
In OnPropertyChanged method you can set NumberOfChanges.
I have a problem with PropertyChanged event, because it is always null, I have been trying to set DataContext for many ways, but it always is null, here is some code:
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
Override method OnStartup in App class
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
MainWindow window = new MainWindow();
MainWindowViewModel mwvm = new MainWindowViewModel();
window.DataContext = mwvm;
window.Show();
}
public class MainWindowViewModel : ViewModelBase
{
private ICommand _ChangePageCommand;
private IPageViewModel _CurrentPageViewModel;
private List<IPageViewModel> _PageViewModels;
public List<IPageViewModel> PageViewModels
{
get
{
if (_PageViewModels == null)
_PageViewModels = new List<IPageViewModel>();
return _PageViewModels;
}
}
public IPageViewModel CurrentPageViewModel
{
get
{
return _CurrentPageViewModel;
}
set
{
if (_CurrentPageViewModel != value)
{
_CurrentPageViewModel = value;
OnPropertyChanged("CurrentPageViewModel");
}
}
}
public ICommand ChangePageCommand
{
get
{
if (_ChangePageCommand == null)
{
_ChangePageCommand = new RelayCommand(
p => ChangeViewModel((IPageViewModel)p),
p => p is IPageViewModel);
}
return _ChangePageCommand;
}
}
public MainWindowViewModel()
{
PageViewModels.Add(new FtpSettingsViewModel());
CurrentPageViewModel = PageViewModels[0];
}
private void ChangeViewModel(IPageViewModel p)
{
if (!PageViewModels.Contains(p))
PageViewModels.Add(p);
CurrentPageViewModel = PageViewModels.FirstOrDefault(vm => vm == p);
}
}
I have no idea what's wrong
Are your referring to the "handler" from this code?
PropertyChangedEventHandler handler = this.PropertyChanged;
If so and if my assumption is correct that it is part of your ViewModelBase implementation, please make sure you are also inheriting from INotifyPropertyChanged and in your succeeding setting of CurrentPageViewModel property, you will notice that it will no longer null.
I don't know which class your PropertyChanged is declared, but tack on the INTERFACE reference so the rest of .net knows it can use it..
public class MainWindowViewModel : ViewModelBase, INotifyPropertyChanged
{
// then your declaration of the OnPropertyChanged...
}
Just having the event exposed doesnt mean the rest of the .net framework is hooking into it. You may also have to make sure that the properties in the view are "hooked up" via binding, such as in the xaml...
<Label Content={Binding PropertyOnYourViewModel, NotifyOnSourceUpdated=True}" />
So as the "PropertyOnYourViewModel" gets changed, this label has gets registered to the "OnPropertyChanged" notification process.
I am implementing a cart in Xamarin.Forms. In my cart page there is a ListView with data. Each of the cell contains a button to select the count of item and amount. In the cart view there is a grand total label.
My problem is the grand total is not updating while the number picker changes. The calculation method is called upon item adding view cell. I know that i need to implement INotifyProperty for this, but I'm unsure of how to do it.
I have a base view model which inherits INotifyProperty that contains an event.
public class BaseViewModel : INotifyPropertyChanged
{
private double _price;
public double Price
{
get
{
return _price;
}
set
{
_price = value;
OnPropertyChanged("Price");}
}
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
View model
public BaseViewModel()
{
App.Instance.ViewModel = this;
TempList = TempList ?? new ObservableCollection<cm_items>();
this.Title = AppResources.AppResource.Cart_menu_title;
this.Price = CartCell.price;
}
As a design methodology, its better to implement MVVM as a subclass and implement it to your ViewModel.
Sample Implementation:
public class ObservableProperty : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
I also strongly suggest implementing ICommand as a Dictionary structure like:
public abstract class ViewModelBase : ObservableProperty
{
public Dictionary<string,ICommand> Commands { get; protected set; }
public ViewModelBase()
{
Commands = new Dictionary<string,ICommand>();
}
}
So all todo in your ViewModel is just inherit the ViewModelBase class and use it
class LoginViewModel : ViewModelBase
{
#region fields
string userName;
string password;
#endregion
#region properties
public string UserName
{
get {return userName;}
set
{
userName = value;
OnPropertyChanged("UserName");
}
}
public string Password
{
get{return password;}
set
{
password = value;
OnPropertyChanged("Password");
}
}
#endregion
#region ctor
public LoginViewModel()
{
//Add Commands
Commands.Add("Login", new Command(CmdLogin));
}
#endregion
#region UI methods
private void CmdLogin()
{
// do your login jobs here
}
#endregion
}
Finally: Xaml Usage:
<Entry Placeholder="Username" Text="{Binding UserName}"/>
<Entry Placeholder="Password" Text="{Binding Password}" IsPassword="True"/>
<Button Text="Login" Command="{Binding Commands[Login]}"/>
For example try this view model:
public abstract class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetPropertyValue<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
{
if (value == null ? field != null : !value.Equals(field))
{
field = value;
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
return true;
}
return false;
}
}
and in inherited classes use it like this:
private int myProperty;
public int MyProperty
{
get { return this.myProperty; }
set { this.SetPropertyValue(ref this.myProperty, value); }
}
When I started Xamarin coding, the MVVM was a bit confusing until I discovered that the PropertyChangedEvent on the ViewModel fired off a signal to the View (ContentPage), and updated the Label/textbox/etc.
For those looking for the 'latest and greatest'... Here's some revised code:
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
and on your property Setter:
public string SomeProperty
{
get { return _somProperty; }
set
{
_someProperty= value;
OnPropertyChanged();
}
}
}
Nice? No? Saves having to pass the property name each time!
I am trying to set the name (textbox) value using WCF Service. I am hosting service in WPF application. I used the MVVM Model initially to set textbox value from the MainWindow.cs and it worked. But then I made some properties static in order to access the same through the service contract. It still seems to setting the property of Model attribute but not changing value in the text box. Can anyone please guide me?
Model.cs
public class Model : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, string propertyName)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
MessageBox.Show(field.ToString());
return true;
}
// props
private static string testname;
public static string TestName
{
get { return testname; }
set {
Model m = new Model();
m.SetField(ref testname, value, "TestName");
}
}
}
WCF InameService.cs
public class nameService : InameService
{
public void setMyName(string name)
{
Model.TestName = name;
}
}
MainWindow.xaml
<Grid Name="GridName">
<TextBox Name="TextName" HorizontalAlignment="Left" Height="23" Margin="193,140,0,0" TextWrapping="Wrap" Text="{Binding TestName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalAlignment="Top" Width="120" />
</Grid>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
ServiceHost host = new ServiceHost(typeof(nameService));
InitializeComponent();
host.Open();
Model s = new Model();
//this.DataContext = s.NameValue.TestName;
Model.TestName = "Alicia";
this.TextName.DataContext = s;
}
}
Thanks Nathan for help. Following is the answer:
I changed the ViewModel to Singleton Class and also instantiated the composite Model object while creating the instance.
`class ViewModel
{
private static volatile ViewModel instance;
private static object _mutex = new object();
private ViewModel() { }
private Model model;
public Model NameValue
{
get { return model; }
set { model = value; }
}
public static ViewModel Instance
{
get
{
if (instance == null)
{
lock (_mutex)
{
if (instance == null)
{
instance = new ViewModel();
instance.model = new Model();
}
}
}
return instance;
}
}
}`
then changed the MainWindow.xaml.cs
try
{
ViewModel s = ViewModel.Instance;
s.NameValue.TestName = "Alicia";
this.DataContext = s;
this.TextName.DataContext = s;
}
catch (Exception e)
{
MessageBox.Show("Error" + e.Message);
}
Similar changes was done in the Service Contract Class. I hope this will help some one trying to get the value in
Don't use static properties as you can't bind to them. Use a static object instead or pass the Model object to the service for example in the constructor and use that instance for updates.
public class nameService : InameService
{
private Model model;
public nameService(Model m)
{
model = m;
}
public void setMyName(string name)
{
model.TestName = name;
}
}
public class Model : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetField<T>(ref T field, T value, string propertyName)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(propertyName);
MessageBox.Show(field.ToString());
return true;
}
// props
private string testname;
public string TestName
{
get { return testname; }
set {
Model m = new Model();
m.SetField(ref testname, value, "TestName");
}
}
}
I got a view model like this:
public class BaseViewModelTech : INotifyPropertyChanged
{
static string _TechnicianID;
public string TechnicianID
{
get {
return _TechnicianID;
}
set {
_TechnicianID = TechnicianID;
OnPropertyChanged("TechnicianID");
}
}
static string _DeviceID;
public string DeviceID
{
get
{
return _DeviceID;
}
set
{
_DeviceID = DeviceID;
OnPropertyChanged("DeviceID");
}
}
// In ViewModelBase.cs
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
this.VerifyPropertyName(propertyName);
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
[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;
Debug.Fail(msg);
}
}
}
I send it as parameter to my xaml.cs
public partial class BaseView : Window{
BaseViewModelTech viewModel;
public BaseView (BaseViewModelTech vm)
{
InitializeComponent();
viewModel = vm;
}}
what do I write to access it throught xaml using binding?? I failed to understand multiple examples.
Change your code behind of your view slightly:
public partial class BaseView : Window
{
BaseViewModelTech viewModel;
public BaseView (BaseViewModelTech vm)
{
InitializeComponent();
viewModel = vm;
this.DataContext = vm; // <----------- add this
}
}
And then in your XAML you can have something like this:
<TextBlock Text="{Binding TechnicianID}" />
Also note that in your setters you want to do the notification after the property value is changed, not before:
set
{
_DeviceID = DeviceID;
OnPropertyChanged("DeviceID"); // <------ this goes after the member variable change
}
In your case you can't directly refer your ViewModel directly into xaml due to you vm instance being member of your View. So, you should set the DataContext of your view first in code-behind:
public partial class BaseView : Window{
BaseViewModelTech viewModel;
public BaseView (BaseViewModelTech vm)
{
InitializeComponent();
viewModel = vm;
this.DataContext=viewModel;
}}
then in your my xaml.xaml for example for label :
<Label Content="{Binding TechnicianID }"/>