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 }"/>
Related
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.
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 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");
}
}
}
Why does my textbox fail to update when I try to update it from another class?
I've instantiated the MainWindow class in my Email class, but when I try to do
main.trending.Text += emailText;
Am I doing something wrong?
You should bind your data.
Model
public class YourData : INotifyPropertyChanged
{
private string _textBoxData;
public YourData()
{
}
public string TextBoxData
{
get { return _textBoxData; }
set
{
_textBoxData = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("TextBoxData");
}
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
XAML Binding
Set data context in Codebehind
this.DataContext = YourData;
Bind Property
<TextBox Text="{Binding Path=Name2}"/>
See #sa_ddam213 comment. Dont do something like MainWindow main = new MainWindow(); inside Email class. Instead, pass the MainWindow object you already have.
Following codes will work:
public class MainWindow
{
public void MethodWhereYouCreateEmailClass()
{
Email email = new Email;
email.Main = this;
}
}
public class Email
{
public MainWindow main;
public void MethodWhereYouSetTrendingText()
{
main.trending.Text += emailText;
}
}
But I dont say that is best practice. I just try to keep it close to your existing code i guess.
I have a problem with listView initializations. The .xaml part of the listView is as below,
<ListView x:Name="categoryListView" HorizontalAlignment="Left" Width="129" Height="180"
ItemsSource="{Binding Path=RecordModel.CategoryList}"
DisplayMemberPath="RecordModel.CategoryList"
SelectedValue="{Binding Path=RecordModel.RecordTitle}"
VerticalAlignment="Top">
I have a list of String paths in RecordModel.CategoryList but I need to change the list at window initialization. Part of the view-model is below. Where can I add the code to change the list so the listView gets the changed list items at start?
public class MainWindowViewModel : ViewModelBase
{
...
private RecordModel _recordModel;
private ICommand _addCategoryCommand;
...
public MainWindowViewModel()
{
_recordModel = new RecordModel();
}
public RecordModel RecordModel
{
get { return _recordModel; }
set { _recordModel = value; }
}
...
public ICommand AddCategoryCommand
{
get
{
if (_addCategoryCommand == null)
_addCategoryCommand = new AddCat ();
return _addCategoryCommand;
}
}
public class AddCat : ICommand
{
public bool CanExecute(object parameter) { return true; }
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
MainWindowViewModel mainWindowViewModel = (MainWindowViewModel)parameter;
...
//Do things with mainWindowViewModel and the variables it has
}
...
This is the reason that ViewModels exist: so that they can transparently convert values from the Model to values more appropriate for binding.
You should expose a CategoryList property on the MainWindowViewModel and bind directly on that. You can then populate it by processing the values of RecordModel.CategoryList in the RecordModel property setter:
public class MainWindowViewModel : ViewModelBase
{
private RecordModel _recordModel;
public MainWindowViewModel()
{
RecordModel = new RecordModel(); // set the property not the field
}
public RecordModel RecordModel
{
get { return _recordModel; }
set {
_recordModel = value;
// populate CategoryList here from value.CategoryList
}
}
public UnknownType CategoryList { get; }
}