WPF bindings do not pick up any changes - c#

In my WPF application, I have some properties which I have bound to the XAML counterpart, but for some reason do not get set whenever their values change. I have implemented the INotifyPropertyChanged interface as well as set my DataContext for this View too, and it is still not picking up any changes.
I have this same pattern for other properties within this ViewModel which do work, while others don't.
Here is a snippet of my current code:
ViewModel
public class TestViewModel : INotifyPropertyChanged
{
private string testString;
public TestViewModel()
{
.....
this.RunCommand = new RelayCommand(this.RunAction);
}
public string TestString
{
get
{
return this.testString;
}
set
{
this.testString = value;
this.OnPropertyChanged("TestString");
}
}
private void RunAction()
{
.....
this.testString = "Running.";
}
}
View
<StatusBarItem>
<TextBlock Text="{Binding Path=TestString, UpdateSourceTrigger=PropertyChanged, Mode=OneWay}" />
</StatusBarItem>
DataContext (set in code-behind of another MainWindow class)
var testViewModel = SimpleIoc.Default.GetInstance<TestViewModel>();
var testWindow = new TestWindow() { DataContext = testViewModel };
testingWindow.Show();
If it helps, this is part of a multi-windowed application which uses MVVM-Light to pass properties between classes.

You are not changing the value of the TestString, you are assigning a command to change the value but you do not seem to be executing it.
this.RunCommand = new RelayCommand(this.RunAction);
Bind that command to something or execute it manually from somewhere.
Also you need to assign the property not the field
this.TestString = "Running.";

I found the problem. You are only updating the private property testString. But you do not update the property TestString so the notify is never called.
Try this:
this.TestString = "Running";

Related

C# WPF I cannot get private to update but public will

I am very puzzled about this and have searched but cannot find the answer. I am sure I have not understood something correctly. Can you please explain this to me?
I have a View and a ViewModel. In the view I have a Textblock
<TextBlock
Grid.Row="4"
Grid.Column="5"
Grid.ColumnSpan="3"
IsEnabled="{Binding Enable, Mode=OneWay}"
Margin="5,10,5,10">
<Run Text="File: "/>
<Run Text="{Binding FilePathName}"/>
</TextBlock>
At the top of the View I have
d:DataContext="{d:DesignInstance d:Type=viewModels:MainWindowViewModel}"
I also added in Code behind
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel();
}
I did not want to add the DataContext here but it seemed at first to make it work but now I am not sure I need it and I will try removing it.
I think I understand this part that the data in the View will be refreshed from the item called FilePathName in the ViewModel.
Now in the view model I have created the “get and set” for the FilePathName as:
private string _filepathname;
public string FilePathName
{
get => _filepathname;
set
{
if (_filepathname != value)
{
_filepathname = value;
OnPropertyChanged();
}
}
}
Now this is where my understanding must be going a bit hazy. I think that within the ModelView I will just use the private string. This means when I change its value like this:
_filepathname = “MyNewName”;
then the PropertyChanged will see this change and will update the View via the public FilePathName. However if I use the FileOpenDialog and open a file and then say
if (openFileDialog.FileName != "")
{
_filepathname = openFileDialog.FileName;
ProcessFile();
}
I am using the private here but this will never update. If I put a break point in the “get and set” we never go there so the UI never updates.
However in ProcessFile() I use
StreamReader sr = new StreamReader(_filepathname);
I do actually read the correct file. This I could understand as it is also private. BUT … if I now change the private to public as follows
if (openFileDialog.FileName != "")
{
FilePathName = openFileDialog.FileName;
ProcessFile();
}
I now update on the UI and open the correct file for reading. It seems sometimes I need to use private and sometimes public to make it work. Why?
A second question is why does isEnabled not seem to work on TextBlock. I have it working on buttons and Textbox. I use it in TextBlock as follows and you can see it further above.
IsEnabled="{Binding Enable, Mode=OneWay}"
All help appreciated.
This does not work like that.
If it was, why would we need properties?
This:
myString = "new value"
ONLY assignes value to variable. And that's all. When you do this using property, property setter will be called, so:
if(myString != value)
{
myString = value; //new value will be assigned to variable
OnPropertyChanged(); //property changed will be called
}
When you assign value to variable, then you can just read it and that's what happens.
This variable is called "backing field". It's there because there has to be some variable that stores a value. If you declare property like that:
public string MyString {get; set;}
under the hood, the compiler also creates such backing field and in the end it works like that:
string __myString;
public string MyString
{
get {return __myString;}
set {__myString = value;}
}
so, as you can see, this is just a synthatic sugar. If you want to get more out of properties (like call property changed), you have to create such bakcing field by yourself.
What's more, property doesn't store any value by itself. Think of it as it has been a method. Actually two methods - one that assigns value to variable and the other that returns the value:
public void SetMyString(string value)
{
myString = value;
}
public string GetMyString()
{
return myString;
}
Because property at all is also some kind of synthatic sugar.
So to sum up:
string myString;
public string MyString
{
get {return myString;}
set
{
if(myString != value)
{
myString = value;
OnPropertyChanged();
}
}
}
///
myString = "NewValue"; //<-- only assigns value to variable myString
MyString = "NewValue"; //<-- assings value to variable myString AND calls OnPropertyChanged - because that's how you property setter looks like.
Your view and view model are two different classes.
When you change a private field of your view model, your view has no way to know if it is not notified. You notify the view when you call OnPropertyChanged() in the property setter. The property setter is not called when you modifiy the field. Read Adam's anwser for more details.
To answer your second question, IsEnabled is not the property you are looking for, as it disables the UI element, but does not hide it. You need to change the Visibility property for that (with a converter).
Here is how I would do it (with other simplifications):
<!-- in application or control resources -->
<BooleanToVisibilityConverter x:Key="BooleanToVisibility" />
<TextBlock Grid.Row="4"
Grid.Column="5"
Grid.ColumnSpan="3"
Text="{Binding FilePathName, StringFormat=File: {0}}"
Visibility="{Binding Enable, Mode=OneWay, Converter={StaticResource BooleanToVisibility}}"
Margin="5,10"/>

Order of setting the DataContext in the default constructor in WPF

I experiment with the order of setting the DataContext property in the default constructor in WPF.
<StackPanel>
<ListBox ItemsSource="{Binding MyItems, PresentationTraceSources.TraceLevel=High}"></ListBox>
<TextBlock Text="{Binding SomeText}"></TextBlock>
<TextBlock Text="{Binding SomeNum}"></TextBlock>
<TextBlock Text="{Binding Path=Person.Name}"></TextBlock>
<ListBox ItemsSource="{Binding Path=PersonList}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
1) With DataContext set before the InitializeComponent method
public partial class MainWindow : Window, INotifyPropertyChanged
{
private string someText = "Default text";
public List<string> MyItems { get; set; }
public List<Person> PersonList { get; set; }
public Person Person { get; set; }
public int SomeNum { get; set; }
public string SomeText
{
get
{
return someText;
}
set
{
someText = value;
OnPropertyChanged("SomeText");
}
}
public MainWindow()
{
this.DataContext = this;
MyItems = new List<string>();
PersonList = new List<Person>();
Person = new Person();
InitializeComponent();
/*These changes are not reflected in the UI*/
SomeNum = 7;
Person.Name = "Andy";
/*Changes reflected with a help of INotifyPropertyChanged*/
SomeText = "Modified Text";
/* Changes to the Lists are reflected in the UI */
MyItems.Add("Red");
MyItems.Add("Blue");
MyItems.Add("Green");
MyItems[0] = "Golden";
PersonList.Add(new Person() { Name = "Xavier" });
PersonList.Add(new Person() { Name = "Scott" });
PersonList[0].Name = "Jean";
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
public class Person
{
public string Name { get; set; } = "Default Name";
}
After the call to the InitializeComponent method changes to the values of properties are not reflected in the UI except for those properties which use INotifyPropertyChanged. Everything is clear so far.
However I noticed that changes to the list items are also reflected in the UI. How come?
I always thought that in order to reflect adding/removing from the collection I need ObservableCollection and to implement INotifyPropertyChanged on list object to detect modifications of these objects. What is the meaning of this?
2) With DataContext set after the InitializeComponent method
Why setting a DataContext property after the InitializeComponent is a bad practice with MVVM? Could you describe it more thoroughly or give a simple code example?
I always thought that in order to reflect adding/removing from the collection I need ObservableCollection<T> and to implement INotifyPropertyChanged on list object to detect modifications of these objects.
You do, if you want reliable updating of the UI during changes in the view model.
What is the meaning of this?
The "meaning" is that in your particular scenario, you are making assumptions that aren't valid. WPF components go through a variety of initialization steps, only some of which occur as part of the InitializeComponent() method.
If, for example, you were to move the code for your value updates into a handler for the Loaded event, you'd find some of the updates reflected in the UI, but not all.
If you move that same code into a method invoked via Dispatcher.InvokeAsync() using a priority of DispatcherPriority.SystemIdle, you'd find that none of the updates would be observed, except for the one backed by INotifyPropertyChanged. In that case, you're explicitly waiting until every aspect of initialization has completed, and there are no longer opportunities for the initialization code to observe your updated values.
It's all about timing. Any code that sets a value before the UI winds up observing it, can do so successfully without INotifyPropertyChanged or equivalent. But you're entirely at the mercy of the current implementation of the framework in that case. Different parts of the initialization happen at different times, and these are not all documented, so you're relying on undocumented behavior. It probably won't change, but you have no way to know for sure.
Why setting a DataContext property after the InitializeComponent is a bad practice with MVVM?
It's not. Don't believe everything you read, even (or especially!) on the Internet.
If you want to forego implementation of INotifyPropertyChanged, then it will be important that you initialize all of your view model data before assigning the DataContext. But, even if you assign the DataContext after calling InitializeComponent, that assignment will be observed (because DataContext is a dependency property and so provides property changed notification to the framework), and the UI will retrieve all of the bound data from your view model data.
What's important is that the view model data be initialized before the assignment of DataContext. Where that happens relative to InitializeComponent() is not important.
When a view model property does not fire the PropertyChanged event, its value must of course be set before assigning the view model instance to the view's DataContext.
It does however not matter if you assign the DataContext before or after calling InitializeComponent:
Given a Binding like
<TextBlock Text="{Binding SomeText}"/>
these two sequence will both result in showing the property value in the view:
DataContext = new { SomeText = "Hello, World." };
InitializeComponent();
and
InitializeComponent();
DataContext = new { SomeText = "Hello, World." };

Transferring Textbox data to my ViewModel

I have two textboxes with userinput, of which I need to transfer the data to my ViewModel. I tried looking around how to do this by binding it to a button (as the transfer is supposed to take place upon a buttonclick), but most advice to use bindings. However, to use bindings you have to declare properties in the ViewModel (afaik), but as these strings are used to create a new object, holding properties for them would be all but ideal because the two textboxes might expand to over 10 in the future. I've also tried messing around with CommandParameter but I only seem to be able to declare one.
So for clarification:
How do I transfer the contents of two (or more) textboxes to the corresponding ViewModel so I can create a new Object with them?
Edit:
In addition I'd also like to be able to reset the Text= field to be empty once the method handling the data has succesfully completed.
The View
<TextBox Name="UI1"/>
<TextBox Name="UI2"/>
<Button Source="*ImageSource*" Command="{Binding CallCreateObject}"/>
and the ModelView
private void OnCallCreateObject()
{
Object newObject = new Object(UI1, UI2, false)
}
This is a general example of what I'm trying to achieve
If you want to insert data from UI to ViewModel on Button Click than there is no reason to use binding. Binding is mainly used to sync data between UI and underlying models.
Still if you want only that then on button_click event you can do something like this.
private void button_Click(object sender, RoutedEventArgs e)
{
Model model = new Model();
model.Property1 = textBox1.Text;
model.Property2 = textBox2.Text;
textBox1.Text = string.Empty;
textBox2.Text = string.Empty;
}
That will solve your issue. But this approach is not recommended when you have a better thing that is called 'Binding'
If you want to bind your view with a viewmodel then try this:
Your view model:
public class Person : INotifyPropertyChanged
{
private string name;
// Declare the event
public event PropertyChangedEventHandler PropertyChanged;
public string PersonName
{
get { return name; }
set
{
name = value;
// Call OnPropertyChanged whenever the property is updated
OnPropertyChanged("PersonName");
}
}
// Create the OnPropertyChanged method to raise the event
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
Great, you have set up your view model. Now the view:
XML PersonView.xml:
<Grid Name="MyContainer">
<TextBox Text="{Binding PersonName}" />
<Button Name="SaveInfoButton" OnClick="SaveInfoButton_Click">Save info</Button>
</Grid>
Now that we have indicated with which property the textbox will be bind, lets indicate to the view the model that will use to update the property named PersonName. The idea is that when you click over the button, the property PersonName of our model Person gets updated with the value of the TextBox.
The xml class:
public partial class PersonView : UserControl
{
private readonly Person Model;
public PersonView()
{
//Components initialization, etc. etc...
this.Model = new Person();
this.DataContext = this.Model; // Here we are binding the model with our view.
}
private void SaveInfoButton_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(this.Model.PersonName); // this will print the value of your textbox.
}
}
Dont know if you noticed, but we didnt have the need of creating a new object when the user click the button. We just use our model and update the model properpies. If you add more textbox to your view, you'll have to added to our viewmodel as well as the given example.
Here is some post that can help you a little bit more(dont have enough time)
http://blog.scottlogic.com/2012/04/20/everything-you-wanted-to-know-about-databinding-in-wpf-silverlight-and-wp7-part-two.html
http://www.tutorialspoint.com/wpf/wpf_data_binding.htm
You could use bindings like this:
<TextBox Name="UI1" Text="{Binding Path=Ut1Value}"/>
<TextBox Name="UI2" Text="{Binding Path=Ut2Value}"/>
<Button Source="*ImageSource*" Command="{Binding CreateTheThingCommand}"/>
Then in your viewmodel you'll need to have the properties and command for those:
private string _ut1Value;
private string _ut2Value;
public string Ut1Value
{
get
{
return _ut1Value;
}
set
{
if (_ut1Value!= value)
{
_ut1Value= value;
OnPropertyChanged("Ut1Value");
}
}
}
public string Ut2Value
{
get
{
return _ut2Value;
}
set
{
if (_ut2Value!= value)
{
_ut2Value= value;
OnPropertyChanged("Ut2Value");
}
}
}
public ICommand CreateTheThingCommand
{
get { return new RelayCommand(CreateTheThing); }
}
private void CreateTheThing()
{
Object newObject = new Object(_ut1Value, _ut2Value, false);
// Do whatever with your new object
}
It sounds as if you need at least two ViewModel objects:
One to present the data from an existing object. This would be, essentially, what you have already.
A container ViewModel. This encapsulates the behaviours of the IEnumerable collection of objects, including the functionality required to Add a new object.
The container ViewModel would have the properties that you are struggling with, plus the CreateObject command, along with an IEnumerable (ObservableCollection) property to hold the existing ViewModel objects.
In your View, you would have one control to present the data in an existing ViewModel object, and a second control with a ListView (or similar) control to display the existing view controls and the set of TextBox controls, plus the button to create a new object (and add it to the list).
This would also allow you to add 'remove', 'sort', etc. functionality to the container ViewModel, without having to change the existing ViewModel.
A way to accomplish a scalable solution with minimal lines of code, would be to create hold a list of items you bind to in the view model.
This way you can use an ItemsControl in the UI to display a textbox for each item:
public class ViewModel
{
public List<Item> Items {get;} = new List<Item>
{
new Item { Value = "UI1" },
new Item { Value = "UI2" },
};
public class Item
{
public string Value {get;set;}
}
}
View:
<ItemsControl ItemsSource="{Binding Test}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBox Text="{Binding Value}" Margin="5"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Content="Commit" Margin="5" Click="ButtonBase_OnClick"/>
You can then create the object either from a click event or command:
private void OnCallCreateObject()
{
Object newObject = new Object(Items[0], Items[1], false);
}
The downside is that the order of the items is not explicit, so either you need to assume that the indexed order is correct, or order them manually.

WPF: Binding TextBox Text to a sub-element of a Property with WCF?

I have a TextBox which I'm trying to bind to a element of a table property 'regimeAlias' is a column with the tbRegimes table which I have mapped with Entity Framework:
<TextBox Text="{Binding NewRegime.regimeAlias, Mode=TwoWay}"/>
Exposed property in my ViewModel:
private tbRegime _NewRegime;
public tbRegime NewRegime
{
get { return _NewRegime; }
set
{
_NewRegime = value;
OnPropertyChanged("NewRegime");
}
}
Lastly, here's the WCF Service Reference auto-generated code class:
public partial class tbRegime : object, System.Runtime.Serialization.IExtensibleDataObject, System.ComponentModel.INotifyPropertyChanged {
//blah blah blah
[System.Runtime.Serialization.DataMemberAttribute()]
public string regimeAlias {
get {
return this.regimeAliasField;
}
set {
if ((object.ReferenceEquals(this.regimeAliasField, value) != true)) {
this.regimeAliasField = value;
this.RaisePropertyChanged("regimeAlias");
}
}
}
The setter never gets hit. Is this because each element within the NewRegime object needs to raise PropertyChanged and if so is there an easy workaround without adding a further DTO layer to my code?
Edit3: with the post from your regimeAlias code. i have to say your binding should work. but of course if you wanna debug you have to set the breakpoint in your regimeAlias setter
<TextBox Text="{Binding NewRegime.regimeAlias, Mode=TwoWay}"/>
this code means, you bind to a Public Property regimeAlias in your class tbRegime.
your setter for NewRegime will never hit because you dont bind to it.
so check your tbRegime class property setter for regimeAlias.
EDIT: the DataContext of the TextBox is of course an object with the Public Property NewRegime, but like i said if you use dot notation in your binding the last property is the one you bind to :)
EDIT: you dont have much ways to workaround:) if you let the binding like you did, you need a model with a public property regimeAlias and it should implement INotifyPropertyChanged.
if you wanna wrap the regimeAlias Property then you have the problem the you have to raise OnPropertyChanged("MyRegimeAlias") at the right point.
public string MyRegimeAlias
{
get { return _NewRegime.regimeAlias; }
set
{
_NewRegime.regimeAlias = value;
OnPropertyChanged("MyRegimeAlias");
}
}
xaml
<TextBox Text="{Binding MyRegimeAlias, Mode=TwoWay}"/>

C# WPF Open a window, but checkboxes are not set

i have a problem with my Checkboxes.
I have a MainWindow(), from here i open my second Window to set some searchpropertys for the user.
(in wich fields they want to search ... like Name, Firstname and other)
After the user confirmed his choices, the Window get closed and the values get saved in the app.propertys.
On the next start up of the application the values from the session before are still how they should be, so this part works.
Now the problem:
If i now open the Window all the Checkboxes arnt set like they should be?!
I have them bound to my ViewModel like this :
<CheckBox x:Name="cVersichertenstatus" Content=... **IsChecked="{Binding Versichertenstatuschecked}"** .../>
The code for my bindings lock like this:
private bool versichertenstatusischecked;
public bool Versichertenstatuschecked
{
get
{
return versichertenstatusischecked;
}
set
{
if (Versichertenstatuschecked != value)
{
versichertenstatusischecked = value;
//this.OnPropertyChanged("Versichertenstatuschecked");
}
}
}
After the start up i set all the binding propertys with the values of the app.propertys.
But it change nothing. The Checkboxvalues are always false :-(
I guess its because i dont use OnPropertyChanged, but if i try i get this error:
object reference not set to an instance of the object
I dont get it, i have alot of bindings and all work fine, only the checkbox things do not.
Can someone explain me what i have to do to resolve this error?
Edit:
Set the Datacontext :
public MainWindow()
{
InitializeComponent();
SetupBindings();
var l = new app_config_load();
l.load();
}
private void SetupBindings()
{
pViewModelList viewModel = new pViewModelList();
plistview.DataContext = viewModel;
}
and the load() class:
public void load()
{
Properties.Settings get = new Properties.Settings();
pViewModelList a = new pViewModelList();
a.Nachnamechecked = get.cnachname_app;
a.Versichertenstatuschecked = get.cversichertenstatus_app;
a.Geburtsdatumchecked = get.cgeburtsdatum_app;
a.Versicherungsnrchecked = get.cversichertennummer_app;
a.Vornamechecked = get.cvorname_app;
get.Save();
}
Do i have to initialize somehow the Propertys first? Thought this happens automaticly ?!
Did you set the DataContext?
public MyWindow(MyClass myClass){
InitializeComponent();
this.DataContext = myClass;
}
Before InitializeComponent(); all of your GUI - Elements will be null!
How to get your Element from MainWindow:
public MyClass GetData()
{
MyClass data = this.DataContext as MyClass;
return data;
}
and try:
IsChecked="{Binding Path=Versichertenstatuschecked}"
I think I know what the problem is. You miss the Mode of the binding.
IsChecked="{Binding Versichertenstatuschecked, Mode=TwoWay}"
Using this will trigger changes in the viewModel to be displayed in the view.
i solved the problem by my self.
I just needed to wait till the Window is loaded before i use my Class to set the bindingpropertys.
I used the "loaded" event to call the Class and jeah, no longer any Object Reference errors :)
Such things are realy frustrating.
Cya

Categories