string reference binding in WPF - wpf

I have a binding in my view as follows
<TextBox Grid.Row="3" Grid.Column="1" DataContext="{Binding FileStore}" Text="{Binding Path=StoreId}" Foreground="Black"/>
FileStore.StoreId is a string. In my code I changed the StoreId at some point
FileStore.StoreId = "1234";
But the view is not updating the TextBox content. I could think of one possible reason-as string is immutable the assignment allocates a new string.So, the Textbox is still binding to the old instance. Do you agree? How can I tackle the situation?

Your FileStore class should implement INotifyPropertyChanged, so that the binding engine is notified when the value of a property is changed
class FileStore : INotifyPropertyChanged
{
private string _storeId;
public string StoreId
{
get { return _storeId; }
set
{
_storeId = value;
OnPropertyChanged("StoreId");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}

You should realize that FileStore is not changing (it's the same object), you can try by declaring a new FileStore with the new StoreId and then replace the current FileStore.
NewFileStore = new FileStoreType();
NewFileStore.StoreId="1234";
this.FileStore = NewFileStore;
Anyway, your FileStore class should be in a ViewModel which implements INotifyPropertyChanged.

Related

WPF - Binding Textbox Text to a class property

I am doing some changes in my WPF project to make it less deprecated.
One of the things I am trying to do is Binding my Textbox.Text value to a simple Class as shown below.
<TextBox x:Name="txtNCM"
Grid.Column="1"
Margin="5"
MaxLength="8"
Text="{Binding Path=Name}"
</TextBox>
public partial class wCad_NCM : UserControl, INotifyPropertyChanged
{
private string name;
public event PropertyChangedEventHandler PropertyChanged;
public string Name
{
get { return name; }
set
{
name = value;
OnPropertyChanged("Name");
}
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
public wCad_NCM()
{
InitializeComponent();
}
}
Everytime I use the Immediate Window to display the Name's value, it is shown as null. I am really new to this, so I had to search for a similar situation to adapt, but I don't know how to make this work :(
You need to set the DataContext and give Name a value.
To do that, change your constructor to include this:
public wCad_NCM()
{
InitializeComponent();
DataContext = this; // Sets the DataContext
Name = "Test";
}
This should make it work, but is typically bad practice. See http://blog.scottlogic.com/2012/02/06/a-simple-pattern-for-creating-re-useable-usercontrols-in-wpf-silverlight.html for more details.
Additionally, I tried running this and ran into a name hiding problem. Try using a variable name other than Name as FrameworkElement already contains it.

How can I RaisePropertyChanged on property change?

Here I added a model to my viewmodel:
public dal.UserAccount User {
get
{
return _user;
}
set
{
_user = value;
RaisePropertyChanged(String.Empty);
}
}
I handle property change event...
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
This is the binding I use:
<TextBox Text="{Binding User.firstname, Mode=TwoWay, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}" />
Why the propertychange event is not triggered on updating view?
PropertyChanged is used to notify the UI that something has been changed in the Model.
Since you're changing an inner property of the User object - the User property itself is not changed and therefore the PropertyChanged event isn't raised.
Second - your Model should implement the INotifyPropertyChanged interface. - In other words make sure UserAccount implements INotifyPropertyChanged, otherwise changing the firstname will not affect the view either.
Another thing:
The parameter RaisePropertyChanged should receive is the Name of the property that has changed. So in your case:
Change:
RaisePropertyChanged(String.Empty);
To
RaisePropertyChanged("User");
From MSDN:
The PropertyChanged event can indicate all properties on the object have changed by using either null or String.Empty as the property name in the PropertyChangedEventArgs.
(No need to refresh all the Properties in this case)
You can read more on the concept of PropertyChanged here
You can invoke a property changed event from another class. Not particularly useful if you have all the sources. For closed source it might be. Though I consider it experimental and not production ready.
See this console copy paste example:
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
namespace ConsoleApp1
{
public class Program
{
static void Main(string[] args)
{
var a = new A();
a.PropertyChanged += A_PropertyChanged;
var excpl = new Excpl();
excpl.Victim = a;
excpl.Ghost.Do();
}
private static void A_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Console.WriteLine("Event triggered");
}
}
[StructLayout(LayoutKind.Explicit)]
public struct Excpl
{
[FieldOffset(0)]
public A Victim;
[FieldOffset(0)]
public C Ghost;
}
public class A : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
}
public class C : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void Do()
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(""));
}
}
}

Why DataContext for inner object is not updated?

I have question about DataContext changes and I build example for understand this aspect.
I have MainUserControl on MainWindow. MainUserControl consists of number of User Controls.
One of such User Controls is SubUserControl1.
<Window x:Class="WpfApplicationUcBindingQuestion.MainWindow">
<Grid>
.....
<uc:MainUserControl />
</Grid>
</Window>
<UserControl x:Class="WpfApplicationUcBindingQuestion.MainUserControl">
<Grid>
.....
<uc:SubUserControl1 x:Name="subUserControl1" />
</Grid>
</UserControl>
And in MainWindow I have object of class Info. Class Info consists of a few inner classes.
One of them is, lets say, SubInfo. Both Info and SubInfo classes inherits from INotifyPropertyChanged.
And this is the code of them:
public class Info : INotifyPropertyChanged
{
private SubInfo m_subInfo = new SubInfo();
public Info()
{
}
public SubInfo SubInfo
{
get
{
return m_subInfo;
}
set
{
m_subInfo = value;
OnPropertyChanged("SubInfo");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class SubInfo: INotifyPropertyChanged
{
private string m_subString = "subStr";
public SubInfo()
{
}
public string SubString
{
get
{
return m_subString;
}
set
{
m_subString = value;
OnPropertyChanged("SubString");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
I want to set DataContext for MainUserControl to be object of class Info
and for SubUserControl1 DataContext will be Info.SubInfo.
The following code describes this:
<UserControl x:Class="WpfApplicationUcBindingQuestion.SubUserControl1">
<Grid>
.....
<TextBox Text="{Binding Path=SubString}"/>
</Grid>
</UserControl>
public MainUserControl()
{
InitializeComponent();
MainWindow mainWnd = (MainWindow)Application.Current.MainWindow;
Info info = mainWnd.Info;
this.DataContext = info;
this.subUserControl1.DataContext = info.SubInfo;
}
When new subInfo arrived I update inner object subInfo inside info object:
(This is function of MainWindow)
private void OnUpdateData()
{
SubInfo arrivedSubInfo = new SubInfo();
arrivedSubInfo.SubString = "newString";
m_info.SubInfo = arrivedSubInfo;
}
I want to see that DataContext for subUserControl1 is also changed.
But it doesn't happened and TextBox inside SubUserControl1 is not updated
and doesn't show "newString".
(Note: If I write inside OnUpdateData() function the following:
m_info.SubInfo.SubString = arrivedSubInfo.SubString;
(copy field-field and not whole object) it works,
but I dont 'want copy 50 field...)
Where I'm wrong?
Your help will be really appreciated.
Your problem is the following:
In your constructor, when you do that:
this.DataContext = info;
this.subUserControl1.DataContext = info.SubInfo;
You will set the DataContext ONLY once. Which means it will never change unless you write subUserControl1.DataContext = someNewDataContext somewhere.
What you can do to solve this :
The "proper solution":
Use a binding. In your XAML, just write:
<uc:SubUserControl1 x:Name="subUserControl1" DataContext="{Binding
SubInfo, UpdateSourceTrigger=PropertyChanged}" />
This will work, assuming that your SubInfo property fires the OnPropertyChanged event when it is set.
The "ugly solution":
Explicitly set your UserControl's DataContext in code-behind when you need it. Again, I wouldn't advise that and you are better off applying the first solution!

How to bind a Property with a textblock From a Class

public class myClass : INotifyPropertyChanged
{
public string myName(string myNameIs)
{
Name = myNameIs;
return myNameIs;
}
public string My = "Hasan";
public string Name {
get { return My; }
set
{
My = value;
OnPropertyChanged("Name");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
// Raise the PropertyChanged event
this.PropertyChanged( this, new PropertyChangedEventArgs(
propertyName));
}
}
}
.
XAML:
<TextBlock Height="42" Margin="107,245,0,0" TextWrapping="Wrap" Text="{Binding Name}" VerticalAlignment="Top" HorizontalAlignment="Left" Width="159" DataContext="{Binding Source={StaticResource myClassDataSource}}"/>
This is working. But when i update property then it isn`t work?
Your code is rather confusing, you seem to be all over the place with it. I know this isn't the question you asked, but i thought i would point this out anyway:
your member variable is declared as public (public string My = "Hasan";)
your member variable has a totally different name to its property (My and Name)
you have a setter for the public property, and also a setting function (myName(string myNameIs))
you are returning the same value from the setting function as what you passed in
Here is an example of how you could rewrite it:
public class MyClass : INotifyPropertyChanged
{
//normal default constructor
public MyClass()
{
_name = "Hasan";
}
//extra constructor for when you need to set the name to something other than the default
//although this is really only useful if you have no setter on the Name property
public MyClass(string name)
{
_name = name;
}
public string Name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("Name");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
// Raise the PropertyChanged event
this.PropertyChanged(this, new PropertyChangedEventArgs(
propertyName));
}
}
private string _name;
}
You just need to set the TextBlock (or it's parent's) DataContext property to an instance of this class.
Next bind the Text property to the backing property like this
<TextBlock Text="{Binding Name}"/>
Try going through a few tutorials online (or a book) instead of trying to forge your way through. It's easy once you get how DataBinding works.
Update: Once I formatted your question correctly, I could see the XAML you are using...
The mistake here is that you're trying to use the ElementName property (which is used to bind one UI element with another by name). This isn't what you're trying to achieve.

Concept of "UpdateSourceTrigger" Property, How to use it in WPF?

I have a TextBlock, binded with an Object and when i update property of object its not refleting on UI, Why ?
Code:
In Windows1.xaml
<TextBlock Name="txtName" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" Width="100" Height="20" Margin="12,23,166,218" />
and In Windows.xaml.cs
public partial class Window1 : Window
{
Employee obj ;
public Window1()
{
InitializeComponent();
obj = new Employee();
obj.Name = "First";
txtName.DataContext = obj;
}
private void btnUpdate_Click(object sender, RoutedEventArgs e)
{
obj.Name = "changed";
}
}
public class Employee : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _name;
public string Name
{
set
{
this._name = value;
OnPropertyChanged(Name);
}
get { return this._name; }
}
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
OnPropertyChanged(Name);
should be:
OnPropertyChanged("Name");
otherwise if the name is set to "Kent", you're raising a property changed event saying that the "Kent" property has changed, which obviously doesn't exist.
As for UpdateSourceTrigger, that only applies to the source. The property you've bound is the target, not the source. And it doesn't make sense for a TextBlock to update its source, because there's no way for the user to modify the TextBlock text. A TextBox, on the other hand, would make sense. In that case, UpdateSourceTrigger determines the point at which the text in the TextBox is pushed back to the source property (e.g. as the user types each character, or when they move away from the TextBox).
Pass the name of the property as string, instead of the property value, like so:
OnPropertyChanged("Name");
UpdateSourceTrigger is for binding with the source property i.e normal .net property so to it requires to set on options like property change with mode twoway for dynamic change ex.text of textbox being change and the which updtaes a label to change. Where as if you want the change event to fire at the end i.e lost focus or click use explicit option with updatesourcetrigger.

Resources