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");
}
}
}
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 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 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 have a usercontrol that uses several properties on a binded class. I would like to update the control if any of the properties on this class change.
INotifyPropertyChanged only works if I bind to the property directly. (this will work but I would have to bind to several properties for each instance of the control)
Below is a striped down version of the issue I am having. The DirectProperty works as expected. The ParentPropertyChangedCallback() is only fired when the ParentClass is assigned to the user control, not when any of the properties change.
Please tell me I doing something stupid =)
UserControl1.xaml.cs
public partial class UserControl1
{
public static readonly DependencyProperty DirectPropertyProperty =
DependencyProperty.Register(
"DirectProperty",
typeof(bool),
typeof(UserControl1),
new PropertyMetadata(default(bool), DirectPropertyChangedCallBack));
public static readonly DependencyProperty ParentClassProperty =
DependencyProperty.Register(
"ParentClass",
typeof(ParentClass),
typeof(UserControl1),
new PropertyMetadata(default(ParentClass), ParentPropertyChangedCallback));
public UserControl1()
{
InitializeComponent();
}
public bool DirectProperty
{
get { return (bool)GetValue(DirectPropertyProperty); }
set { SetValue(DirectPropertyProperty, value); }
}
public ParentClass ParentClass
{
get { return (ParentClass)GetValue(ParentClassProperty); }
set{ SetValue(ParentClassProperty, value); }
}
// private void parentProperty_Changed(object)
private static void DirectPropertyChangedCallBack(
DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var sourceObject = dependencyObject as UserControl1;
if (sourceObject == null) return;
//set rectangle 1 base on Direct Property
sourceObject.Rectangle1.Fill =
sourceObject.ParentClass.DirectProperty
? new SolidColorBrush(Colors.Green)
: new SolidColorBrush(Colors.Red);
}
private static void ParentPropertyChangedCallback(
DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs dependencyPropertyChangedEventArgs)
{
var sourceObject = dependencyObject as UserControl1;
if (sourceObject == null) return;
//set rectangle 2 base on ParentClass Property
sourceObject.Rectangle2.Fill =
sourceObject.ParentClass.ParentProperty
? new SolidColorBrush(Colors.Green)
: new SolidColorBrush(Colors.Red);
//set rectangle 3 base on ChildClass Property
sourceObject.Rectangle3.Fill =
sourceObject.ParentClass.ChildClass.ChildProperty
? new SolidColorBrush(Colors.Green)
: new SolidColorBrush(Colors.Red);
}
}
ParentClass.cs
public class ParentClass :INotifyPropertyChanged
{
private ChildClass _childClass;
private bool _directProperty;
private bool _parentProperty;
public event PropertyChangedEventHandler PropertyChanged;
public ChildClass ChildClass
{
get { return _childClass; }
set
{
if (Equals(value, _childClass)) return;
_childClass = value;
OnPropertyChanged();
}
}
public bool DirectProperty
{
get { return _directProperty; }
set
{
if (value.Equals(_directProperty)) return;
_directProperty = value;
OnPropertyChanged();
}
}
public bool ParentProperty
{
get { return _parentProperty; }
set
{
if (value.Equals(_parentProperty)) return;
_parentProperty = value;
OnPropertyChanged();
}
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
ChildClass.cs
public class ChildClass : INotifyPropertyChanged
{
private bool _childProperty;
public event PropertyChangedEventHandler PropertyChanged;
public bool ChildProperty
{
get { return _childProperty; }
set
{
if (value.Equals(_childProperty)) return;
_childProperty = value;
OnPropertyChanged();
}
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
**** Working Modified ParentClass
public class ParentClass :INotifyPropertyChanged
{
private ChildClass _childClass;
private bool _directProperty;
private bool _parentProperty;
public event PropertyChangedEventHandler PropertyChanged;
public ParentClass()
{
PropertyChanged += OnPropertyChanged;
}
public ChildClass ChildClass
{
get { return _childClass; }
set
{
if (Equals(value, _childClass)) return;
_childClass = value;
OnPropertyChanged();
_childClass.PropertyChanged += OnPropertyChanged;
DirectProperty = !DirectProperty;
}
}
public bool DirectProperty
{
get { return _directProperty; }
set
{
if (value.Equals(_directProperty)) return;
_directProperty = value;
OnPropertyChanged();
}
}
public bool ParentProperty
{
get { return _parentProperty; }
set
{
if (value.Equals(_parentProperty)) return;
_parentProperty = value;
OnPropertyChanged();
DirectProperty = ! DirectProperty;
}
}
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
if (propertyChangedEventArgs.PropertyName == "DirectProperty") return;
// Properties that are bound to the UI you want to update
DirectProperty = ! DirectProperty;
}
}
Assuming you are interested in the changing values of your view model (DataContext), you can bind multiple properties of your view model to a single property of your user control using a multi-value converter. If any of the properties in the binding changes, the converter will be called and you can use the converter to set a single value on your user control or do nothing. You can learn how to create or use a multi-value converter here
Sample xaml below:
<UserControl1 >
<UserControl1.DirectProperty>
<MultiBinding Converter="{StaticResource myConverterName}">
<Binding Path="ChildClass"/>
<Binding Path="DirectProperty"/>
<Binding Path="ParentProperty"/>
</MultiBinding>
</UserControl1.DirectProperty>
</UserControl1>
Sample converter below:
public class MyConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
//return some value
return null;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
//return some value
return null;
}
}
What you can do on your classes is to subscribe to property changed event. INotifyPropertyChanged gives you the ability to subscribe for PropertyChanges
public ParentClass()
{
this.PropertyChanged += OnPropertyChanged;
}
private void OnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
// Properties that are bound to the UI you want to update
OnPropertyChanged("PROPERTYNAME");
}
Now that'll trigger for any property changed and gets repeated if you just update everything on each property name that was changed. You can customize how it behaves but the idea is there. You can also access properties in PropertyChangedEventArgs to what properties are being changed.
I can't seem to find a simple, concrete explanation of how to bind controls in a WinForms app to nested objects using data binding. For example:
class MyObject : INotifyPropertyChanged
{
private string _Name;
public string Name
{
get { return _Name; }
set
{
_Name = value;
OnPropertyChanged("Name");
}
}
private MyInner _Inner;
public MyInner Inner
{
get { return _Inner; }
set
{
_Inner = value;
OnPropertyChanged("Inner");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
class MyInner : INotifyPropertyChanged
{
private string _SomeValue;
public string SomeValue
{
get { return _SomeValue; }
set
{
_SomeValue = value;
OnPropertyChanged("SomeValue");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Now imagine a form with just two textboxes, the first for Name and the second for Inner.SomeValue. I'm easily able to get binding to work against Name, but Inner.SomeValue is flaky. If I populate the object and then set up the binding, it shows Inner.SomeValue in the textbox but I can't edit it. If I start from a fresh object without initializing Inner, I can't seem to get data to stick in Inner.SomeValue.
I've checked all over MSDN, all over StackOverflow, and dozens of searches with different keywords. Everyone wants to talk about binding to databases or DataGrids, and most examples are written in XAML.
Update: I've tried Marc's full test harness and have partial success. If I hit the "all change!" button, I seem to be able to write back to the inner object. However, starting with MyObject.Inner null, it doesn't know how to create an inner object. I think for now, I can work around it by just making sure my inner references are always set to a valid object. Still, I can't help feeling like I'm missing something :)
Hmm - an excellent question; I've done lots of data-binding to objects, and I would have sworn that what you are doing should work; but indeed it is very reluctant to notice the change to the inner object. I've managed to get it working by:
var outer = new BindingSource { DataSource = myObject };
var inner = new BindingSource(outer, "Inner");
txtName.DataBindings.Add("Text", outer, "Name");
txtSomeValue.DataBindings.Add("Text", inner, "SomeValue");
Not ideal, but it works. Btw; you might find the following utility methods useful:
public static class EventUtils {
public static void SafeInvoke(this EventHandler handler, object sender) {
if(handler != null) handler(sender, EventArgs.Empty);
}
public static void SafeInvoke(this PropertyChangedEventHandler handler,
object sender, string propertyName) {
if(handler != null) handler(sender,
new PropertyChangedEventArgs(propertyName));
}
}
Then you can have:
class MyObject : INotifyPropertyChanged
{
private string _Name;
public string Name { get { return _Name; } set {
_Name = value; PropertyChanged.SafeInvoke(this,"Name"); } }
private MyInner _Inner;
public MyInner Inner { get { return _Inner; } set {
_Inner = value; PropertyChanged.SafeInvoke(this,"Inner"); } }
public event PropertyChangedEventHandler PropertyChanged;
}
class MyInner : INotifyPropertyChanged
{
private string _SomeValue;
public string SomeValue { get { return _SomeValue; } set {
_SomeValue = value; PropertyChanged.SafeInvoke(this, "SomeValue"); } }
public event PropertyChangedEventHandler PropertyChanged;
}
And in the bargain it fixes the (slim) chance of a null-exception (race-condition).
Full test rig, to iron out kinks (from comments):
using System;
using System.ComponentModel;
using System.Windows.Forms;
public static class EventUtils {
public static void SafeInvoke(this PropertyChangedEventHandler handler, object sender, string propertyName) {
if(handler != null) handler(sender, new PropertyChangedEventArgs(propertyName));
}
}
class MyObject : INotifyPropertyChanged
{
private string _Name;
public string Name { get { return _Name; } set { _Name = value; PropertyChanged.SafeInvoke(this,"Name"); } }
private MyInner _Inner;
public MyInner Inner { get { return _Inner; } set { _Inner = value; PropertyChanged.SafeInvoke(this,"Inner"); } }
public event PropertyChangedEventHandler PropertyChanged;
}
class MyInner : INotifyPropertyChanged
{
private string _SomeValue;
public string SomeValue { get { return _SomeValue; } set { _SomeValue = value; PropertyChanged.SafeInvoke(this, "SomeValue"); } }
public event PropertyChangedEventHandler PropertyChanged;
}
static class Program
{
[STAThread]
public static void Main() {
var myObject = new MyObject();
myObject.Name = "old name";
// optionally start with a default
//myObject.Inner = new MyInner();
//myObject.Inner.SomeValue = "old inner value";
Application.EnableVisualStyles();
using (Form form = new Form())
using (TextBox txtName = new TextBox())
using (TextBox txtSomeValue = new TextBox())
using (Button btnInit = new Button())
{
var outer = new BindingSource { DataSource = myObject };
var inner = new BindingSource(outer, "Inner");
txtName.DataBindings.Add("Text", outer, "Name");
txtSomeValue.DataBindings.Add("Text", inner, "SomeValue");
btnInit.Text = "all change!";
btnInit.Click += delegate
{
myObject.Name = "new name";
var newInner = new MyInner();
newInner.SomeValue = "new inner value";
myObject.Inner = newInner;
};
txtName.Dock = txtSomeValue.Dock = btnInit.Dock = DockStyle.Top;
form.Controls.AddRange(new Control[] { btnInit, txtSomeValue, txtName });
Application.Run(form);
}
}
}