WPF INotifyPropertyChanged without burning base class - c#

I'm trying to find a simple approach for data binding in WPF.
I'm using the INotifyPropertyChanged interface and it works fine if it's implemented on an abstract base class and inherited by objects that have bound members.
public partial class MainWindow : Window
{
public static MainWindow Instance;
private readonly Vm _vm;
public MainWindow ()
{
InitializeComponent();
DataContext = _vm = new Vm
{
Button1 = new Vm.ObservableButton(button1, new List<string> { "Paused", "Logging" }, false),
Button2 = new Vm.ObservableToggleButton(button2, new List<string> { "Log All", "Log VBA" }, false),
};
}
private class Vm
{
public abstract class ObservableObject : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged ([CallerMemberName] string propName = "")
{
var pc = PropertyChanged;
if (pc != null)
pc(this, new PropertyChangedEventArgs(propName));
}
}
public class ObservableButton : ObservableObject
{
private readonly Button _b;
private readonly List<string> _options;
private string _content;
public string Content
{
get { return _content; }
set
{
if (_content == value) return;
_content = value;
OnPropertyChanged();
}
}
public Boolean On { set; private get; }
public ObservableButton (Button b, List<string> options, Boolean on = true)
{
_b = b;
_options = options;
_b.Click += Click;
On = on;
Content = On ? _options[0] : _options[1];
}
public void Click (object sender, RoutedEventArgs e)
{
On = !On;
Content = On ? _options[0] : _options[1];
}
}
public class ObservableToggleButton : ObservableObject
{
private readonly ToggleButton _b;
private readonly List<string> _options;
private string _content;
public string Content
{
get { return _content; }
private set
{
if (_content == value) return;
_content = value;
OnPropertyChanged();
}
}
private Boolean _on;
public Boolean On
{
private get { return _on; }
set
{
if (_on == value) return;
_on = value;
Content = value ? _options[0] : _options[1];
}
}
public ObservableToggleButton (ToggleButton b, List<string> options, Boolean on = true)
{
_b = b;
_options = options;
On = on;
Content = _b.IsChecked ?? false ? _options[0] : _options[1];
}
public void Push ()
{
var peer = new ToggleButtonAutomationPeer(_b);
var toggleProvider = peer.GetPattern(PatternInterface.Toggle) as IToggleProvider;
if (toggleProvider != null) toggleProvider.Toggle();
//On = !On;
}
}
public ObservableButton Button1 { get; set; }
public ObservableToggleButton Button2 { get; set; }
public Vm ()
{
}
}
}
<Grid Margin="0,0,183,134">
<Button x:Name="button1" Content="{Binding Button1.Content}" HorizontalAlignment="Left" Margin="112,134,0,0" VerticalAlignment="Top" Width="75"/>
<ToggleButton x:Name="button2" IsChecked="{Binding Button2.On, Mode=OneWayToSource}" Content="{Binding Button2.Content}" HorizontalAlignment="Left" Margin="206,134,0,0" VerticalAlignment="Top"/>
</Grid>
I wanted to try doing this without burning the base class though, so I implemented INotifyPropertyChanged on the View Model and routed the change events from the bound members, back through the single interface on the View Model. Even though the Binding Object has a reference to the Source and the correct property name, this fails silently.
I figured that it doesn't work because the Binding Object does some type checking, so I made a fake implementation on the bound properties and it works. Here is the code for that scenario...
public partial class MainWindow : Window
{
public static MainWindow Instance;
public MainWindow ()
{
InitializeComponent();
DataContext = new ViewModel
{
Button1 = new ViewModel.ObservableButton(button1, new List<string> { "Paused", "Logging" }, false),
Button2 = new ViewModel.ObservableToggleButton(button2, new List<string> { "Log All", "Log VBA" }, false),
};
}
public class ViewModel : INotifyPropertyChanged
{
private static ViewModel _instance;
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged<T> (T control, [CallerMemberName] string propName = "")
{
var pc = PropertyChanged;
if (pc != null)
pc(control, new PropertyChangedEventArgs(propName));
}
public class ObservableButton : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged () {}
private readonly Button _b;
private readonly List<string> _options;
private string _content;
public string Content
{
get { return _content; }
set
{
if (_content == value) return;
_content = value;
_instance.OnPropertyChanged(this);
}
}
public Boolean On { set; private get; }
public ObservableButton (Button b, List<string> options, Boolean on = true)
{
_b = b;
_options = options;
_b.Click += Click;
On = on;
Content = On ? _options[0] : _options[1];
}
public void Click (object sender, RoutedEventArgs e)
{
On = !On;
Content = On ? _options[0] : _options[1];
}
}
public class ObservableToggleButton : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged () {}
private readonly ToggleButton _b;
private readonly List<string> _options;
private string _content;
public string Content
{
get { return _content; }
private set
{
if (_content == value) return;
_content = value;
_instance.OnPropertyChanged(this);
}
}
private Boolean _on;
public Boolean On
{
private get { return _on; }
set
{
if (_on == value) return;
_on = value;
Content = value ? _options[0] : _options[1];
}
}
public ObservableToggleButton (ToggleButton b, List<string> options, Boolean on = true)
{
_b = b;
_options = options;
On = on;
Content = _b.IsChecked ?? false ? _options[0] : _options[1];
}
}
public ObservableButton Button1 { get; set; }
public ObservableToggleButton Button2 { get; set; }
public ViewModel ()
{
_instance = this;
}
}
}
<Grid Margin="0,0,183,134">
<Button x:Name="button1" Content="{Binding Button1.Content}" HorizontalAlignment="Left" Margin="112,134,0,0" VerticalAlignment="Top" Width="75"/>
<ToggleButton x:Name="button2" IsChecked="{Binding Button2.On, Mode=OneWayToSource}" Content="{Binding Button2.Content}" HorizontalAlignment="Left" Margin="206,134,0,0" VerticalAlignment="Top"/>
</Grid>
So you can see that, even though the interface on the ObservableButton and ObservableToggleButton types are still routing the change notification through their parent, the Binding Object is happy because they toe the line on type.
Is there a good reason why the the child object needs to implement the interface even though there is already everything need to complete the binding without it?

I try to provide a clear example how this should be done in WPF instead of trying to fix the OP question.
XAML
<StackPanel>
<StackPanel.Resources>
<BooleanToVisibilityConverter x:Key="bToV" />
</StackPanel.Resources>
<!--bind the text to the viewmodel content. Use a bool to visibilty converter to convert from true to Visible-->
<TextBlock
Text="{Binding Path=Content}"
Visibility="{Binding Path=IsContentVisible, Converter={StaticResource bToV}}" />
<!--Use a two way binding to sync the IsChecked property with the viewmodel-->
<ToggleButton IsChecked="{Binding Path=IsContentVisible,Mode=TwoWay}"
Content="{Binding Path=ToogleActionName}" />
</StackPanel>
code behind
to keep your project structure clear I warmly suggest to put each class in a separate file. However I put all 3 classes into one single file for easier posting.
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
namespace WpfApplication4
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ContentViewModel() { Content = "foo" };
}
}
public class ContentViewModel : ViewModelBase
{
private string _toogleActionName = "turn it off";
private bool _isContentVisible = true;
private string _content;
public bool IsContentVisible
{
get
{
return _isContentVisible;
}
set
{
_isContentVisible = value;
//switch action name
if (value)
ToogleActionName = "turn it off";
else
ToogleActionName = "turn it on";
OnPropertyChanged();
}
}
public string Content
{
get
{
return _content;
}
set
{
_content = value;
OnPropertyChanged();
}
}
public string ToogleActionName
{
get
{
return _toogleActionName;
}
set
{
_toogleActionName = value;
OnPropertyChanged();
}
}
}
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
I hope this is showing how WPF is supposed to work with the MVVM pattern.

The problem there is that the ViewModel on the first example:
private class Vm
{
...
}
Does not implement INofityPropertyChanged interface, therefore whenever you say that you DataContext is "Vm", the binding would not know that a property has changed because the view model it is not implementing INotifyPropertyChanged...
And on the second example, it is working because you are implementing a INofityPropertyChanged on the view model class
public class ViewModel : INotifyPropertyChanged
{
...
}
Note that it doesn't matter if your child classes implements INotifyPropertyChanged if your base class doesn't implement it too and your base class is observing changes on the children and raises the changes as "its own"...

Related

Changes in Property that implements INotifyPropertyChanged not being reflected in UI

I am trying to implement the MVVM Pattern but unfortunately is taking longer than expected.
I have a ListView populated by an ObservableCollection of ContactsVm, Adding or Removing Contacts works perfectly, the problem comes when trying to change only one Item from this collection by selecting it.
The Xaml where I am setting my bindings:
<ListView ItemsSource="{Binding ContactsToDisplay}"
SelectedItem="{Binding SelectedContact, Mode=TwoWay}"
SeparatorColor="Black"
ItemSelected="OnItemSelected">
<ListView.ItemTemplate>
<DataTemplate>
<TextCell Text="{Binding FirstName}"
Detail="{Binding Id}">
<TextCell.ContextActions>
<MenuItem
Text="Delete"
IsDestructive="true"
Clicked="Delete_OnClicked"
CommandParameter="{Binding .}" />
</TextCell.ContextActions>
</TextCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
Its cs:
public ContactBookApp()
{
InitializeComponent();
MapperConfiguration config = new MapperConfiguration(cfg => {
cfg.CreateMap<Contact, ContactVm>();
cfg.CreateMap<ContactVm, Contact>();
});
BindingContext = new ContactBookViewModel(new ContactService(), new PageService(), new Mapper(config));
}
private void AddButton_OnClicked(object sender, EventArgs e)
{
(BindingContext as ContactBookViewModel)?.AddContact();
}
private void OnItemSelected(object sender, SelectedItemChangedEventArgs e)
{
(BindingContext as ContactBookViewModel)?.SelectContact(e.SelectedItem as ContactVm);
}
private void Delete_OnClicked(object sender, EventArgs e)
{
(BindingContext as ContactBookViewModel)?.DeleteContact((sender as MenuItem)?.CommandParameter as ContactVm);
}
}
My ViewModel, here the "problematic" part is the SelectContact method, I am posting the rest in case it helps:
public class ContactBookViewModel : BaseViewModel
{
private readonly IContactService _contactService;
private readonly IPageService _pageService;
private readonly IMapper _mapper;
private ContactVm _selectedContact;
public ObservableCollection<ContactVm> ContactsToDisplay { get; set; }
public ContactVm SelectedContact
{
get => _selectedContact;
set => SetValue(ref _selectedContact, value);
}
public ContactBookViewModel(IContactService contactService, IPageService pageService, IMapper mapper)
{
_contactService = contactService;
_pageService = pageService;
_mapper = mapper;
LoadContacts();
}
private void LoadContacts()
{
List<Contact> contactsFromService = _contactService.GetContacts();
List<ContactVm> contactsToDisplay = _mapper.Map<List<Contact>, List<ContactVm>>(contactsFromService);
ContactsToDisplay = new ObservableCollection<ContactVm>(contactsToDisplay);
}
public void SelectContact(ContactVm contact)
{
if (contact == null)
return;
//None of this approaches works:
//SelectedContact.FirstName = "Test";
//contact.FirstName = "Test;
}
}
}
My ContactVm class:
public class ContactVm : BaseViewModel
{
private string _firstName;
public int Id { get; set; }
public string FirstName
{
get => _firstName;
set => SetValue(ref _firstName, value);
}
}
The BaseViewModel:
public class BaseViewModel
{
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected void SetValue<T>(ref T backingField, T value, [CallerMemberName]string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(backingField, value))
return;
backingField = value;
OnPropertyChanged(propertyName);
}
}
As you can see, I am trying to update each selected contact setting its FirstName = "Test", the changed are updated but unfortunately they are not getting reflected in the UI, hope you can help me to find what I am doing wrong.
Thanks in advance!
Your BaseViewModel does not implement the INotifyPropertyChanged interface.
Since you had used MVVM , you could handle the logic diretly in your ViewModel when you select item in listview (you don't need to define ItemSelected event any more) .
private ContactVm _selectedContact;
public ContactVm SelectedContact
{
set
{
if (_selectedContact!= value)
{
_selectedContact= value;
SelectedContact.FirstName="Test";
NotifyPropertyChanged("SelectedContact");
}
}
get { return _selectedContact; }
}
And don't forget to implement the INotifyPropertyChanged to your model and viewmodel.
I guess the NotifyPropertyChangedInvocator attribute is not properly notifying the property changes. But I am not sure about that. Because your BaseViewModel does not implement the INotifyPropertyChanged interface.
The below code works fine for me. This is how I use it in my entire project.
I have directly derived the INotifyPropertyChanged interface in my BaseModel and implemented the property changes.
public class BaseModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = "")
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class ContactVm : BaseModel
{
private string _firstName;
public int Id { get; set; }
public string FirstName
{
get { return _firstName; }
set
{
this._firstName = value;
NotifyPropertyChanged();
}
}
}
This is what I have in my callback.
public void SelectContact(ContactVm contact)
{
if (contact == null)
return;
contact.FirstName = "Test";
}
The only difference is I have implemented property changes for the ObservableCollection in ViewModel too.
public ObservableCollection<ContactVm> ContactsToDisplay
{
get { return _contactsToDisplay; }
set
{
this._contactsToDisplay = value;
NotifyPropertyChanged();
}
}
Note that I have not used your SelectedContact binding in my case. May be as you said that binding would be the issue.
I hope it helps you.

How to bind Instanceproperty to viewmodel, view is not updated

Problem:
My Viewmodel creates a instance of a controller mycontroller and the property Busy shall be passed to the View and updated in the View based on the controller state, but my View doesn't get updated. The other property Busy2 is updated based on the current state. Bindablebase implements the I
Question:
Why the property Busy in the ViewModel is not updated? The mycontroller.Busy property is updating, but not the view.
Framework & Tools:
PRISM 6.3.0
Fody.Propertychanged
View:
<TextBlock Text="{Binding Path=Busy}"></TextBlock>
<TextBlock Text="{Binding Path=Busy2}"></TextBlock>
ViewModel:
public class Controller
{
public bool Busy { get; private set; }
public async void GetValue()
{
Busy = true;
await Task.Delay(5000);
Busy = false;
}
}
public class MyViewModel : BindableBase
{
private readonly Controller _mycontroller;
public DelegateCommand<string> RunCommand { get; private set; }
// This property is not updated in the view
public bool Busy
{
get { return _mycontroller.Busy; }
}
// Works as aspected
public bool Busy2 { get; set; }
public MyViewModel()
{
_mycontroller = new Controller();
RunCommand = new DelegateCommand<string>(Run, Canrun).ObservesProperty((() => _mycontroller.Busy));
}
private bool Canrun(string arg)
{
return _mycontroller.Busy != true;
}
private void Run(string obj)
{
Busy2 = true;
_mycontroller.GetValue();
}
}
Update:
I added the Bindablebase from Prism, because is implement the INotifyPropertyChanged, but the view is still not updated.
I refactored the code and I set a breakpoint to set { SetProperty(ref _busy, value); } and the breakpoint is never reached.
I removed the Propertychanged.Fody nuget package too.
ViewModel:
public class Controller : BindableBase
{
private bool _busy;
public bool Busy
{
get { return _busy; }
set { SetProperty(ref _busy, value); }
}
public Controller()
{
}
public void DoWork1()
{
for (var i = 0; i < 10; i++)
{
Thread.Sleep(1000);
_busy = !_busy;
Debug.WriteLine(_busy.ToString());
}
}
public void DoWork2()
{
_busy = !_busy;
}
}
public class MainWindowViewModel : BindableBase
{
private Controller mycontroller;
private string _title = "Prism Unity Application";
public DelegateCommand RunCommand { get; private set; }
public string Title
{
get { return _title; }
set { SetProperty(ref _title, value); }
}
public bool Busy
{
get { return mycontroller.Busy; }
}
public MainWindowViewModel()
{
RunCommand = new DelegateCommand(Execute);
mycontroller = new Controller();
}
private void Execute()
{
mycontroller.DoWork1();
}
}
View:
<Window x:Class="PropertytestPrism.Views.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True"
Title="{Binding Title}" Height="350" Width="525">
<StackPanel>
<Button Command="{Binding RunCommand}" Content="Run"></Button>
<TextBlock Text="{Binding Busy}"></TextBlock>
</StackPanel>
</Window>
Update 2
Failure: Missing INotifypropertychanged for the class Controller
The View is still not updated and the reason for this is my Delegatecommandmethod, which executes mycontroller.DoWork1();
Question:
Why the View is not updated? If I execute the method inside the DelegateCommandmethod?
You should implement INotifyPropertyChanged by your Controller class. Property is changed inside Controller class and this changing should be notified:
public class Controller : INotifyPropertyChanged
{
private bool _busy;
public bool Busy
{
get
{
return _busy;
}
private set
{
SetField(ref _busy, value, "Busy"); }
}
}
public async void GetValue()
{
Busy = true;
await Task.Delay(5000);
Busy = false;
}
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);
return true;
}
}
Try making your bindings work like this.
<TextBlock Text="{Binding Busy,Mode=OneWay}"></TextBlock>.
By default bindings are one time and so they wont be updated even on the property changed notification.

Binding with INotifyPropertyChanged Still Doesn't Work

Tried a lot of stuff, still doesn't work. Binding on the two TextBlocks don't work. Used INotifyPropertyChanged interface much like this code to no avail.
Code:
MainWindow.xaml:
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ClockWatcher" xmlns:System="clr-namespace:System;assembly=mscorlib"
x:Name="clockWatcherWindow"
x:Class="ClockWatcher.MainWindow"
Title="Clock Watcher" Height="554" Width="949"
KeyDown="KeysDown" Focusable="True" Closing="SaveSession"
DataContext="{Binding SM, RelativeSource={RelativeSource Self}}">
<TextBlock x:Name="programStartBlock" Text="{Binding StartTime, BindsDirectlyToSource=True, FallbackValue=Binding sucks so much!!!, StringFormat=ProgramStarted: \{0\}, TargetNullValue=This thing is null}" Padding="{DynamicResource labelPadding}" FontSize="{DynamicResource fontSize}"/>
<TextBlock x:Name="totalTimeLabel" Text="{Binding SM.currentSession.TotalTime, StringFormat=Total Time: \{0\}}" Padding="{DynamicResource labelPadding}" FontSize="{DynamicResource fontSize}"/>
</Window>
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
private const string SESSION_FILENAME = "SessionFiles.xml";
/// <summary>
/// Represents, during selection mode, which TimeEntry is currently selected.
/// </summary>
public SessionManager SM { get; private set; }
public MainWindow()
{
InitializeComponent();
SM = new SessionManager();
SM.newAddedCommentEvent += currentTimeEntry_newComment;
SM.timeEntryDeletedEvent += currentTimeEntry_delete;
SM.commentEntryDeletedEvent += entry_delete;
}
}
SessionManager.cs:
public class SessionManager : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
[NonSerialized]
private DateTime _dtStartTime;
private Session current_session;
#region Properties
public DateTime StartTime
{
get
{
return _dtStartTime;
}
private set
{
if (_dtStartTime != value)
{
_dtStartTime = value;
OnPropertyChanged("StartTime");
}
}
}
public Session CurrentSession
{
get
{
return current_session;
}
set
{
if (current_session != value)
{
OnPropertyChanged("CurrentSession");
current_session = value;
}
}
}
#endregion
public SessionManager()
{
_dtStartTime = DateTime.Now;
}
private void OnPropertyChanged([CallerMemberName] string member_name = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(member_name));
}
}
}
Session.cs:
public class Session : INotifyPropertyChanged
{
private TimeSpan total_time;
public DateTime creationDate { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;
public TimeSpan TotalTime
{
get
{
return total_time;
}
set
{
if (total_time != value)
{
OnPropertyChanged("TotalTime");
total_time = value;
}
}
}
public Session()
{
creationDate = DateTime.Now;
}
private void OnPropertyChanged([CallerMemberName] string member_name = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(member_name));
}
}
}
In first TextBlock, instead of SM.StartTime, write only StartTime.
Remove ElementName from first TB.
Make CurrentSession public property, Your currentSession is private now.
In your SessionManager ctor, current_session = new Session();
Remove DataContext from XAML, use this.DataContext = SM; in your window contructor.
If you want to use DataContext in XAML,
<Window.DataContext>
<local:SessionManager />
</Window.DataContext>
The marked correct answer is definitely the better way to do it, but I just wanted to answer explaining more in detail why what you posted didn't work.
The issue is that when you wrote DataContext={Binding SM, RelativeSource={RelativeSource Self} in your MainWindow.xaml, the binding was evaluted before your line SM = new SessionManager(); was executed in your MainWindow.xaml.cs constructor.
You can see this in effect if you changed your getter for SM to:
public SessionManager SM
{
get { return new SessionManager();}
}
This basically ensures that when WPF evaluates your binding, it'll get an actual object for your SM property instead of null.
Just thought perhaps this will help understanding and reduce frustration next time :). The way you asked your question, you technically needed to implement INotifyPropertyChanged on your MainWindow class, which is a big no-no.

Setting WPF Control from WCF Service

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");
}
}
}

Model changes updating View-Model WPF

I am having an issue getting my model changes updated back into my viewmodel so i can display. In this example i have a label and a button, when i press the button it will execute some business logic, and should update the label on screen. However, when my model changes the view will not. Any Idea on what i am doing wrong here?
View-
<Window.DataContext>
<vm:ViewModel>
</Window.DataContext>
<Grid>
<Label Content="{Binding Path=Name}"/>
<Button Command={Binding UpdateBtnPressed}/>
</Grid>
ViewModel
public ViewModel()
{
_Model = new Model();
}
public string Name
{
get{return _Model.Name;}
set
{
_Model.Name = value;
OnPropertyChanged("Name");
}
}
public ICommand UpdateBtnPressed
{
get{
_UpdateBtn = new RelayCommand(param => UpdateLabelValue());
return _UpdateBtn;
}
private void UpdateLabelValue()
{
_Model.Name = "Value Updated";
}
Model
private string name = "unmodified string";
public string Name
{
get{return name;}
set{name = value;}
}
Try this:
private void UpdateLabelValue()
{
Name = "Value Updated";
}
It seems you've missed to implement the INotifyPropertyChanged interface.
Your model must implement INotifyPropertyChanged such as;
public class Personel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _name;
public string Name
{
get { return _name; }
set { _name = value; OnChanged("Name");}
}
void OnChanged(string pn)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(pn));
}
}
}

Categories