This question already has answers here:
Property is not updating in textbox - WPF
(2 answers)
Closed 8 years ago.
Question was answered in other my thread, but i dont get full answer. I made some walk around and its work but im not very happy with this.
So im asking how to do it in MVVM patern.
I got very frustrating problems with updating value in my textbox in View. Proprerty Message is updating from my callback but its not displaying in GUI. Property is correctly binding. There is noway i can do this. The reason is im not raising PropertyChanged on the UI thread, since im calling it from a callback. I'm trying, but nothing works. How to raise PropertyChange event on UI thread?
EDIT AGAIN REFRESH CODE:
namespace test
{
public class MainViewModel : INotifyPropertyChanged, IDataExchangeCallback
{
public MainViewModel()
{
Message = "TEST1";
}
void IDataExchangeCallback.Result(string result)
{
Message += result;
}
public void Register()
{
InstanceContext instanceContext = new InstanceContext(new MainViewModel());
DataExchangeClient client = new DataExchangeClient(instanceContext);
client.RegisterClient(Guid.NewGuid().ToString());
}
private string _message;
public string Message
{
get { return this._message; }
set
{
if (this._message != value)
{
this._message = value;
OnPropertyChanged("Message");
}
}
}
public event PropertyChangedEventHandler PropertyChanged; //ALWAYS NULL!!! BUT I HAVE INotifyPropertyChanged implement
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
Application.Current.Dispatcher.Invoke(() =>
handler(this, new PropertyChangedEventArgs(name)));
}
}
}
}
My PropertChange is always null. WHY? It shoud subscribe to PropertyChanged when i'm using bindings.
EDIT
I Still Need Help
Found solution!
Apparently if you want use INotifyPropertyChanged you need to call it explicitly in code.
this.DataContext = mvm;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MainViewModel mvm = new MainViewModel();
mvm.Register();
this.DataContext = mvm; //this you need
}
}
Declaration in XAML is not sufficient:
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
If someone can explain me why I would be grateful?
Related
I have a UserControl Person_UC and Student_UC. There is a ComboBox in Student_UC which I want to disable it from Person_UC.
But its not working. I want to accomplish this without MVVM.
public partial class Person_UC : UserControl
{
public Person_UC()
{
InitializeComponent();
Student_UC su = new Student_UC();
su.myComboBoxName.IsEnabled = false;
}
}
Without MVVM it would be quite hard to solve. You have to manipulate the same instance of Student_UC which is currently used.
Actually, you're instantiating a new Student_UC and disabling its ComboBox, but you're not doing anything with your variable "su". Did you assign it somewhere?
Basically, you should have one ViewModel per UserControl, so a ViewModel for Person_UC and a ViewModel for Student_UC.
Warning, this solution requires you to use a MVVM Framework like MVVM Light (https://mvvmlight.codeplex.com) for sending messages.
One standard way would be sending a message. Bind your Loaded event of your Person_UC to a method in your code-behind like so:
<UserControl x:Class="YourAssembly.Person_UC"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Name="Person_UC"
Loaded="Loaded">
<Grid Name="RootGrid">
</Grid>
</UserControl>
And:
public partial class Person_UC : UserControl
{
public Person_UC()
{
InitializeComponent();
}
private void Loaded(object sender, RoutedEventArgs e)
{
//Use a Message to notify your Student_UC's ViewModel that you would like to disable its ComboBox
Messenger.Default.Send<ChangeComboBoxEnabilityMessage>(new ChangeComboBoxEnabilityMessage(false));
}
}
Then, when you receive the message within the Student_UC's ViewModel, you have to pass this information to the view. Basically, you can bind IsEnable property of the ComboBox to a property in its ViewModel, that you will set to false when the message is received.
public class Student_UC_ViewModel : INotifyPropertyChanged
{
public Student_UC_ViewModel()
{
//Register your message
Messenger.Default.Register<ChangeComboBoxEnabilityMessage>(message => ComboBoxIsEnabled = message.ComboBoxIsEnabled);
}
private bool _comboBoxIsEnabled;
public bool ComboBoxIsEnabled
{
get
{
return _comboBoxIsEnabled;
}
set
{
_comboBoxIsEnabled = value;
OnPropertyChanged("ComboBoxIsEnabled");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
And your message class:
public class ChangeComboBoxEnabilityMessage : MessageBase
{
public ChangeComboBoxEnabilityMessage(bool comboBoxEnabled)
{
ComboBoxIsEnabled = comboBoxEnabled;
}
public bool ComboBoxIsEnabled
{
get;
set;
}
}
I let you bind your IsEnable property of your ComboBox in your Student_UC xaml to the property of its ViewModel (i.e ComboBoxIsEnabled).
Don't forget to make sure your DataContext is set:
public partial class Student_UC : UserControl
{
public Person_UC()
{
InitializeComponent();
DataContext = new Student_UC_ViewModel();
}
...
}
Also, take care of your binding issues, see your output console in Visual Studio.
I am just starting with WPF and I am trying to setup binding between a local variable and a label. Basicaly I want to update the label when local variable changes. I was searching for solution but they all just use textbox as a source not just class variable and I am not even sure it works this way. So here is my code.
public partial class MainWindow : Window
{
int idCounter;
public MainWindow()
{
InitializeComponent();
Binding b = new Binding();
b.Source = idCounter;
b.Mode = BindingMode.OneWay;
b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
myLabel.SetBinding(Label.ContentProperty,b);
}
private void Button_Click(object sender, RoutedEventArgs e)
{
idCounter++;
}
}
Button does work, idCounter changes value, but it does not update in label so I guess binding is wrong. Can someone tell me what is wrong? Thanks
Your code will work if you change your class to this...
public partial class Window1 : Window, INotifyPropertyChanged
{
private int _idCounter;
public int IdCounter
{
get { return _idCounter; }
set
{
if (value != _idCounter)
{
_idCounter = value;
OnPropertyChanged("IdCounter");
}
}
}
public Window1()
{
InitializeComponent();
myLabel.SetBinding(ContentProperty, new Binding("IdCounter"));
DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
e.Handled = true;
IdCounter++;
}
#region INotifyPropertyChanged Implementation
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string name)
{
var handler = System.Threading.Interlocked.CompareExchange(ref PropertyChanged, null, null);
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
#endregion
}
Some of the issues you were having are...
The window itself should implement INotifyPropertyChanged so that the binding engine can place an ear on it.
the IdCounter needs to be public and have a public getter on it so that the binding engine can 'get' it.
You should set the DataContext to whatever class has declared IdCounter (the MainWindow in this case). Part of the problem was that the binding engine had no DataContext.
The BindingMode setting was a red-herring since a Label binds that way by default.
The UpdateSourceTrigger was a red-herring since the content of the label does not have a mechanism to update the source property. A label's content is not like a text box where the user can type something that the code needs to know about. When you're binding to something that the user cannot change, forget about UpdateSourceTrigger, it's the Target property that counts.
The handler should mark the event. This is good practice and did not affect the binding.
The binding constructor needs only the path.
This code will give you your expected result; i.e., that the label updates when the button is clicked. Checked, compiled, and executed on vs2013, .net 4.5.
The other respondents said you should use a View Model. I agree with this 100%, and overall it's a good thing to consider.
You want to use a property to do this, as well as implementing INotifyPropertyChanged so that the label's content gets updated when the property changes.
Here's an example using a simple ViewModel
xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:converters="clr-namespace:WpfApplication1"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Label Width="200" Height="50" Content="{Binding MyLabel}"/>
<Button Height="30" Width="100" Content="Increment" Click="Button_Click" />
</StackPanel>
</Window>
xaml.cs:
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
MainViewModel vm = new MainViewModel();
public MainWindow()
{
InitializeComponent();
this.DataContext = vm;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
vm.MyLabel += 1;
}
}
}
MainViewModel.cs:
namespace WpfApplication1
{
public class MainViewModel : INotifyPropertyChanged
{
#region Members
private int _myLabel;
#endregion Members
#region Properties
public int MyLabel
{
get
{
return _myLabel;
}
set
{
_myLabel = value;
NotifyPropertyChanged("MyLabel");
}
}
#endregion Properties
public MainViewModel()
{
}
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion
}
}
Note: Ideally, you would want to use a Command for the Button instead of a Click event handler
You cannot bind to something that is private or a field so convert it into public property. You can find more as to what is a valid binding source here
If you want changes to your property be picked up by UI you should implement INotifyPropertyChanged interface and raise event each time value of the property changes. So idCounter should look more like this:
private int _idCounter;
public int idCounter
{
get { return _idCounter; }
set
{
if (_idCounter != value)
{
_idCounter = value;
OnPropertyChanged("idCounter");
}
}
}
When you create binding to property you use Path
Binding works in binding context so you need to specify from where to take this Path. Easiest way to do that is to set DataContext. So in your case initialization should look more like this:
Binding b = new Binding("idCounter");
b.Mode = BindingMode.OneWay;
b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
myLabel.SetBinding(Label.ContentProperty, b);
DataContext = this;
As #d.moncada suggested in his answer you should create dedicated view model
)
Please have a look at the code below:
public class MyClass : INotifyPropertyChanged
{
private string _myName;
public string MyName
{
get { return _myName; }
set
{
if (_myName != value)
{
_myName = value;
OnPropertyChanged("MyName");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Object of this class is used as a DataContext for textBox control on WPF form.
Idea is simple: changes done to MyName property of this object should be automatically displayed in textBox. It works but I do not understand why.
Please follow my way of thinking and point out where I get it wrong...
We set a new value for MyName. Program goes to 'setter' and it calls 'OnPropertyChanged'. It goes to 'OnPropertyChanged' and checks if 'PropertyChanged' is null.
And it is not. And I do not know why since I do not add any event handlers to this event.
So... how does it work?
Thank you!
The Event Handler is added automatically when you bind the object to the DataContext.
I am trying to implement data binding, and to have TextBox's text to be update once I click on some button.
XAML:
<TextBox Text="{Binding Path=Output}" />
Code:
public MainWindow()
{
InitializeComponent();
DataContext = Search;
Search.Output = "111";
}
public SearchClass Search = new SearchClass();
private void button1_Click(object sender, RoutedEventArgs e)
{
Search.Output = "222";
}
public class SearchClass
{
string _output;
public string Output
{
get { return _output; }
set { _output = value; }
}
}
When I execute the program, I see "111", so the binding from MainWindow() works, but if I click a button - the text in the TextBox is not updated (but in the debugger I see that button1_Click is executed and Search.Output is now equal to "222"). What am I doing wrong?
You should implement INotifyPropertyChanged in your SearchClass and then in setter raise the event:
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public string Output
{
get { return _output; }
set
{
_output = value;
PropertyChanged(this, new PropertyChangedEventArgs("Output"));
}
}
If I understood right, SearchClass is the DataContext for your TextBlock. In this case implementing as above would help.
When WPF see some class as the source of Binding - it tries to cast it to INotifyPropertyChanged and subscribe to PropertyChanged event. And when event is raised - WPF updates the binding associated with sender (first argument of PropertyChanged). It is the main mechanism that makes binding work so smoothly.
You have to implement the INotifyPropertyChanged interface on your SearchClass class. This is how binder values are notified their source values have changed. It displays the "111" value because it hasn't been laid out yet (more or less), but will won't update after that until you implement that interface.
Hi i am trying to use the NotifyPropertyChanged to update all the places where i am binding a property. For what i have searched the INotifyPropertyChanged is indicated to this cases.
So i need help because i don't understand what i have wrong in here. And i really don't know what to do with the PropertyChange event. My question about that is, when he changes? What i have to more with him?
Datagrid:
<ListView Name="listView" ItemsSource="{Binding Categories}"/>
An example when i change my Categories property:
DataTest dtTest = new DataTest();
public MainWindow()
{
InitializeComponent();
this.DataContext = dtTest;
}
private void Button1_Click(object sender, RoutedEventArgs e)
{
//Here i pick up a string from a textBox, where i will insert in a Table of my DB,
//Then i will do a query to my Table, and i will get a DataTable for example
//Then i just put the contents into the DataView, so the value have changed.
dtTest.Categories = dtTable.DefaultView;
dtTest = dtTable.defaultView; (this only an example, i don't this for real.)
//What i have to do now, to wherever i am binding (DataGrid, ListView, ComboBox)
//the property to the new values automatically being showed in all places?
}
My Class:
public class DataTest : INotifyPropertyChanged
{
private DataView categories;
public event PropertyChangedEventHandler PropertyChanged; //What i have to do with this?
private void NotifyPropertyChanged(string str)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(str));
}
}
public DataView Categories
{
get { return categories; }
set
{
if (value != categories)
{
categorias = value;
NotifyPropertyChanged("Categories");
}
}
}
}
My Class with INotifyCollectionChanged:
public class DataTest : INotifyCollectionChanged
{
public event NotifyCollectionChangedEventHandler CollectionChanged;
private void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (CollectionChanged != null)
{
CollectionChanged(this, e);
}
}
public DataView Categories
{
get { return categories; }
set
{
if (value != categories)
{
categories = value;
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, Categories));
}
}
}
}
But why the PropertyChanged is always NULL??? I have to do something more, but i don't know what.
Thanks in advance!
the DataTest class needs to be the DataContext for the Binding. You can set this in code-behind (or a myriad of other ways, but for simplicity - just do it in code-behind)
public MainWindow() // constructor
{
this.DataContext = new DataTest();
}
Then, with the 'Source' of the Binding set, you can specify the 'Path', so your xaml looks like this:
<ListBox ItemsSource="{Binding Categories}" />
Now, if the property 'Categories' is changed in code, the NotifyPropertyChanged code you have written will alert the Binding, which in turn will access the public getter for the property and refresh the view.
Getting the handler prevents the event handler from going to null after you check for null and checking for null will prevent you from getting a null exception if there are no event handlers.
The reason that your PropertyChanged is null is that there are no event handlers attached to it. The reason there are not handlers attached to it is you have not bound your object to anything (which will take care of adding a handler) or you haven't added a handler to it (if you wanted to observe it for some other reason). Once your object is created you need to bind it somewhere.
You are doing all you have to do. An event is just a special kind of delegate. You declare it, you invoke it, clients subscribe to it. That's all there is to it.