Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I have a bool value that gets updated and the INotifyPropertyChanged event doesnt fire properly. It gets to the IF portion and steps over because it sees a null being passed. I did add using System.ComponentModel at the top of the class
The first portion:
public class ToolTipInortfyPropertyChange : INotifyPropertyChanged
{
private bool form15TooltipShow;
public bool Form15TooltipShow
{
get { return form15TooltipShow; }
set
{
form15TooltipShow = value;
OnPropertyChanged("Form15TooltipShow");
}
}
and then the second portion all in the same class
public void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public event PropertyChangedEventHandler PropertyChanged;
when I step in the code it gets to the "Set" portion then jumps to the OnPropertyChanged and skips the if so my view doesnt get updated with another event needing the bool
Tx
This the case with every event in c#, if no one subscribed to the event then it will be null. In this case it seems like no one subscribed to the PropertyChanged event.
PS: I recommend putting the [CallerMemberName] attribute in the OnPropertyChanged method so you don't have to pass the name along manually every time. Like so:
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
Notice how I made the method protected instead of public, we don't wan't just anyone being able to raise the event
Edit: Here's the full OnPrertyChanged method:
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
When I step through the code I can see that
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
does execute and the value is no longer null so there must be a "listener" I gather. But the view doesnt update. The checkbox is on one form and the control that will receive the event from the checkbox is on another form. Its almost like 2 objects are created and they cant talk to each other because they are 2 different enteties. Is that possible when creating a new object of lets say type person on form 1 and the same on form 2. They will be distinct wouldnt they?
I created a whole new project and made it as simple as possible. Textbox on form1 and textbox on form 2 and try to pass text between these 2 forms. The line
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
excecutes with no Null value but the view doesnt update.
Here is the XAML
<TextBox Text="{Binding Path=CheckBoxState ,Mode=TwoWay}"></TextBox>
and I have tried all kinds of different options like ASYNC etc. The result is always the same. I even removed the code behind from the forms and did the binding on the XAML
<Window.Resources>
<local:CheckBoxClass x:Key="CheckBoxFinder" />
</Window.Resources>
<Grid DataContext="{Binding Source={StaticResource CheckBoxFinder}}">
I believe this worked fine because intelensense automatically comes up after I type PATH=
then the only property listed pops up.
any advice would be appreciated.
tx
Ok just used the EXACT same code and simply added another textbox to form1. So form1 has 2 textboxes. I bound them exactly as previously only difference is the controls sending and receiving are on the same form. And guess what... It worked.
The problem arises when 2 forms need to pass data. Its like 2 objects are created and they cant communicate. I'm sure this is common knowledge to all the seasoned guys / gals. But how do I use one object between these forms so they will be synced? Wish I knew this before could have asked my question more concisely
Related
UWP app ( mvvm architecture ) I have a MainView which has a collection in its ViewModel, used to bind to the GridView on MainView and each item has a TextBox with 2 way databinding with Description property of class Note.
Xaml of the TextBox of each gridviewitem.
<TextBlock Text="{x:Bind Description,Mode=TwoWay}"
Collection property used to bind to ItemSource of gridview.
public ObservableCollection<Note> Notes { get; }
and this is the class Note
public class Note : Observable
{
private string _description;
public string Description
{
get => _description;
set => Set(ref _description, value, nameof(Description));
}
}
the Observable class is for two way data binding help.
public class Observable : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void Set<T>(ref T storage, T value, [CallerMemberName]string propertyName = null)
{
if (Equals(storage, value))
{
return;
}
storage = value;
OnPropertyChanged(propertyName);
}
protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
Now everything uptil this point works perfectly, when I change the text in textbox, it changes the value of Description as well.
Second View
Now I have a feature where each GridViewItem has a button in it which opens the Note in new window. and this new window has nothing but only 1 TextBox, so now the secondary view and the GridViewItem which opened that view are using the same object of Note.
This TextBox in secondary view also has 2 way data binding with the Description of the Note.
The Problem
What I want is that whether the textbox in gridview or the textbox on the secondary view is edited, the value of description must remain synced between these 2 textboxes, that is why I tried to bind them 2 way with same object of Note hence the same Description object is bound to both of them.
Error here was expected to me which was Marshalling threading error, so whenever I try to change value of any textbox, it tried to update UI on other view ( which is another thread ) which is ofcourse not allowed.
I know about CoreDisptcher
I already know about the Dispatcher feature of UWP for safe cross thread communication, I already have it all setup and if I use it from a normal method I can easily use it for cross thread UI update and it totally works. But my issue is the following line :
protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));\
Exception occurs when it tried to invoke the PropertyChanged I tried to wrap following line in my Dispatcher :
OnPropertyChanged(propertyName);
but INotify interface does not allow me to have a Set<> method which returns a Task instead it needs to return just an object, this is the point where I am stuck and I dont know how to make use of Dispatcher in this scenario, please let me know if there is some better way to do this, it seems this way might not be so efficient.
Thanks.
The best solution in this case would be to have a separate set of INotifyPropertyChanged instances for each window and using some kind of messaging solution like EventHub in MvvmLight, which publishes message that the underlying model changed and all interested parties should update their instances.
Another option would be to create a base model class, which maintains a dictionary of INotifyPropertyChanged instances for each UI thread (so it would be a Dictionary<Dispatcher, YourModelClass>. Now the parent would subscribe to PropertyChanged event of each child instance and once it executes would propagate the event to other childs using the appropriate Dispatcher.
Also there is a very interesting utility class ViewSpecificBindableClass by Marian Dolinský on his GitHub which could potentially be a solution that would allow you to have "single" class in multiple views, aware of multiple dispatchers. I haven't tried it yet, but it seems promising.
So I finally had to take a totally different approach centralizing TextChanged events of MainView textbox and the one on the secondaryview.
I essentially passed the textbox on the mainpage through to the secondary page ( secondary view ) and then subscribed to its TextChanged event. I also subscribed to the TextChanged event of textbox on the secondary view, and then with help of reverse dispatchers I was able to sync the text between 2 windows without any problems.
Note : always make sure to unsubscribe to events when the secondary window closes to prevent memory leaks.
private async void PipBox_TextChanged(object sender, TextChangedEventArgs e)
{
string text = PipBox.Text;
await CoreApplication.MainView.Dispatcher.AwaitableRunAsync(() =>
{
if (parentBox.Text != text)
parentBox.Text = text;
});
}
private async void ParentBox_TextChanged(object sender, TextChangedEventArgs e)
{
string text = parentBox.Text;
// the awaitablerunasync extension method comes from "Windows Community Toolkit".
await _viewLifetimeControl.Dispatcher.AwaitableRunAsync(() =>
{
if (ViewModel.MyNote.Description != text)
ViewModel.MyNote.Description = text;
});
}
Notice that I still have 2 way data binding on both textboxes and it does not cause any exceptions because I am using 2 different instances of Note for both views.
<TextBox Text="{x:Bind ViewModel.MyNote.Description, Mode=TwoWay}"
x:Name="PipBox"/>
but because I have twoway data binding on both textboxes, that is how I can easily keep both instances of Note in sync as well on separate threads.
I will keep the github repo in case it can help anyone else : https://github.com/touseefbsb/MultiWindowBindingSync
P.S : A special thanks to Martin Zikmund who helped me a lot in figuring out this solution.
I have a WPF application where I have two windows. One is for listing objects (e.g. Books) and the other one is a form for editing one object (e.g. a Book). I create a new EF context for each of the windows.
Now I wonder, is there a way I can detect in the list window that a context has been edited after I call SaveChanges in the edit window?
What I do at the moment is to have a public Update method on my list window object that I call from the edit window after SaveChanges is called and in that function I call for the context refresh. However that is not scalable as I would need to keep track of all the windows that depend on Book information (e.g. if I add Shelves list I would need to make sure to update that one as well).
Any suggestions on how to solve this in more modular fashion?
P.S.
The funny part is that I seemed to have had an idea of how to improve this 2 years ago when I first wrote this code as I made a comment for myself: "TODO: solve this differently by having events in ArTresorEntities". But I don't remember what I meant.
Using MVMM Light you can send Message. Your List Window registers receiving message and latter notifies it about change. Take a look here.
You can do this by implementing INotifyPropertyChanged on your model class and then binding the value to your label/interface control.
Here's an example model class:
public class Data : INotifyPropertyChanged
{
int random;
public int Random
{
get { return random; }
set { random = value; NotifyPropertyChanged("Random"); }
}
protected void NotifyPropertyChanged(String propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
That's all that's needed to implemented the INotifyPropertyChanged. Notice that you have to call NotifyPropertyChanged during the set method of the property, otherwise this won't work.
On the UI side, you would do something like this:
<Grid>
<Label Content="{Binding Random, UpdateSourceTrigger=PropertyChanged}" />
<Button Width="100" Height="40" Content="Randomize" Click="Button_Click" />
</Grid>
And then lastly, the code-behind for the xaml:
public Data data;
public MainWindow()
{
InitializeComponent();
data = new Data();
this.DataContext = data;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
data.Random = new Random().Next(1000);
}
The DataContext being assigned is just an example, but it binds this particular class to this window's dataContext, allowing me to do the {Binding Random} and know which property I'm talking about and in which class. You can do this in a few ways and split it up throughout a view.
I also added a button that generates a random number and sets it in my Random property on my model. If you run this code, it updates the label each time you click the button.
I am working on a small project..a game, but in the beggining was only console game, so decided to make it with UI using xaml..And here i have this problem - everythink seems to work fine except that it doesnt re-binding the changes i make.I have BaseModelView class which inherits INotifyPropertyChanged
public class BaseViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string prop)
{
if (this.PropertyChanged != null)
{
var propertyChangedArg = new PropertyChangedEventArgs(prop);
this.PropertyChanged(this, propertyChangedArg);
}
}
}
and also for collections im using ObservableCollection, but it doesnt not re-binding the changes..
I think the problem comes from here, because i dont call the OnPropertyChanged on every property for each class, i call it for the whole class but dont know.. like that
public Player Player
{
get { return this.player; }
set
{
this.player = value;
this.OnPropertyChanged("Player");
}
}
here i will link the repo, anyway its not a hidden project its mean to be open source, and maybe can find the problem more easily - http://goo.gl/mFgCXI
I hope someone can help me, because im stucked for days and dont know how to fix it to move on..
Thanks.
Saw your code on Github. You need to implement INotifyPropertyChanged on ALL classes that will be binded to. Also each property that will be bound should raise the OnPropertyChanged notification.
I see that you have binded to the properties of the Monstor & Hero class but its properties (which are bound in the UI) do not raise the OnPropertyChanged Notification.
Try making your binding mode TwoWay and test again.
Mouse, as you know it, is a static class. It has a static property called Mouse.Position. That property, is updated dynamically (binded to mouse, maybe??). (I've read the Mouse documentation in msdn)
I need to build a same class with this scenario.
I am making an application which able to have multiple pages, and each of the pages shared, for example, Margin value.
There're 2 options (I think) to do it :
Make a field in "page" class, and pass the value via constructor (easiest)
Static class, similar to Mouse.Position usage, but, in this case, for example, PageInformation.Margin (PageInformation is a class, not a property nor a field)
I prefer the second option since it's easier to debug (i guess), and make the Page class cleaner, codeless, and easier during update if the Margin somehow changed by the user..
Anyone know how to do it? (mvvm way preferred)
Thanks.
UPDATE :
P.S. I already understand the basic mvvm practice (INPC and such)
This is the general code so far in my application :
public class ViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
...
}
public class SheetPage : ViewModel
{
...
}
Your class will be a Singleton and therefor next to impossible to unit test. That does not seem to be a very practical solution.
In WPF you can bind all margins of all pages to the same ViewModel property. This way, when your ViewModel property changes (assuming you properly implemented INotifyPropertychanged or used DependencyProperty) all your margins will change as well.
I've set-up 2-way binding between my form (it has 32 controls) and an instance of my class but each character entered in a TextBox has that 1/2 second delay which makes the application almost unusable.
When I use DataSourceUpdateMode.Never, the problem does not occur which clearly indicates the 2-way binding is the culprit.
Note that if I set DataSourceUpdateMode.Never for each control but one, the lag exists for that one control so it doesn't seem to be the number of bound controls that causes the issue.
parameterTagRecord = new PETParameterTagRecord(TagID);
baseTagNameTB.DataBindings.Add("Text", parameterTagRecord,
"BaseTagName", true, DataSourceUpdateMode.OnPropertyChanged);
And an extract of my class:
public class PETParameterTagRecord : PETBaseObject, INotifyPropertyChanged
{
private string _baseTagName = Constants.NullString;
public event PropertyChangedEventHandler PropertyChanged;
public string BaseTagName
{
get { return _baseTagName; }
set
{
_baseTagName = value;
NotifyPropertyChanged("BaseTagName");
}
}
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
What am I doing wrong?
It shouldn't be that slow, but there's an option where you can have the textbox change on key press or on lost focus. Try setting it to lost focus. Also in your setter, be sure to check that _baseTagName != value before setting and raising the event. That will slow things up a bunch as well.
So, first try changing your binding like this:
baseTagNameTB.DataBindings.Add("Text", parameterTagRecord,
"BaseTagName", true, DataSourceUpdateMode.OnValidation);
See this MSDN link: http://msdn.microsoft.com/en-us/library/system.windows.forms.datasourceupdatemode.aspx. This means that instead of every keypress causing the new string value to be pushed into the property, it will only do so on Validation (which happens as part of the control losing focus).
Second, change your property implementation to match this:
public string BaseTagName
{
get { return _baseTagName; }
set
{
if (_baseTagName != value) {
_baseTagName = value;
NotifyPropertyChanged("BaseTagName");
}
}
}
Right now you're raising the event whether the property has actually changed or not. That is also detrimental to performance.
I ran into the same exact issue with BindingSource. This has has nothing to do with the update mode or notifications being fired too often (though indirectly, it does). The current implementation causes every single bound element to refresh whenever any property changes. So the reason OnValidation is less of an issue is obvious, it happens less frequently.
Fairly easy to check, too. Add two counters, increase each whenever a getter is accessed or NotifyProperChanged is called. In my case, with roughly 40 elements, I'd be at 1/40 after loading the form. Add a character in a textbox, suddenly at 2/80. Keeping the key pressed, my app stopped being responsive. Once it finally caught up, the count stood at something ridiculous like 50/2000. All from one single element changing.
I might be wrong, but I don't see how this makes sense or could be the desired implementation. Why would I want to update the whole form when one element changes, defeats the point of binding specific elements in the first place.