I want to change a value (textBlock) according to an event. Then, I want to refresh my window, but I couldn't. I used invalidateVisual as well as solutions of other posts, but nothing worked.
Thank you in advance
Several solutions (the first and second one does not make use of databinding).
txtMyControl.text = "New value";
If not on the main thread, you could use the dispatcher to update the value.
Application.Current.Dispatcher.BeginInvoke(() => txtMyControl.text == "New Value")
However, the most WPF friendly way to do it is to use the databinding.
Any change made to the value in code will be instantly reflected in the UI.
XAML
<TextBox x:Name="txtExample" Text="{Binding MyTextProperty,Mode=TwoWay}" Height="24" Width="120"/>
In your code, you have to declare a variable that will be persistent.
private ExampleModel _ExampleModel = new ExmampleModel();
When you load your code, you associate that variable to your textbox data context.
txtExample.DataContext = _ExampleModel
Then, you have the class that will contains all the editable properties on screen (textboxes, radio boxes, etc...)
public class ExampleModel : INotifyPropertyChanged
{
private string _MyTextProperty = "test";
public string MyTextProperty {
get { return _MyTextProperty; }
set {
if (string.Compare(_MyTextProperty, value) != 0) {
_MyTextProperty = value;
RaisePropertyChanged("MyTextProperty");
}
}
}
public void RaisePropertyChanged(string PropertyName)
{
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
public event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged;
public delegate void PropertyChangedEventHandler(object sender, PropertyChangedEventArgs e);
}
Whenever you handle your event, you just have to change the value of the property containing the information and the UI will refresh accordingly. Also, since we use a two-way binding, the value from your textbox will always be the same than the one contained by MyTextProperty property in ExampleModel class, which make value retrieval very easy.
ex:
_ExampleModel.MyTextProperty = "New value";
If you were already using databinding, make sure the class used implements INotifyPropertyChanged and that the propertyChanged event is called when the property value change or otherwise it won't update the UI.
The best approach to what you're trying to do would be to use Data Binding.
You need to have a string object that will always hold the value of your textblock. Next you need to bind that object to your textblock and then use the event provided by the INotifyPropertyChanged interface and each time the value changes its representation (the textblock) will change to, no need to refresh the window.
More information here
If your event updates the textblock and the textblock you are using is bound to a string property and that property issues a NotifyPropertyChanged() in it's set method, that will cause the display to refresh as you desire.
There are other ways, but this is the easiest given my understanding of your question.
(this is similar to the other answer, but I tried to word so it is easier to understand/implement.)
Related
Under certain conditions if the user selects an item in a combobox, it automatically must be changed to another item
ViewModel
public class VM : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string property)
{
if (PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
private string selected;
public string Selected
{
get { return selected; }
set
{
if (selected != value)
{
selected = value;
OnPropertyChanged("Selected");
}
}
}
private ObservableCollection<string> collection;
public ObservableCollection<string> Collection
{
get { return collection; }
set
{
collection = value;
OnPropertyChanged("Collection");
}
}
public VM()
{
this.Collection = new ObservableCollection<string>(new string[] { "A", "B", "C" });
this.Selected = "A";
this.PropertyChanged += VM_PropertyChanged;
}
void VM_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
this.Selected = "C";
}
}
View
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<StackPanel>
<Grid>
<ComboBox ItemsSource="{Binding Collection}" SelectedValue="{Binding Selected}"/>
</Grid>
<Label Content="{Binding Selected, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
</Window>
So, in this example, no matter what do I select, it should show "C" both, on the combobox and Label, but "C" only shows on Label, it means that the ViewModel is updated but not the view.
It seems the problem here is to try to change the property from the PropertyChanged method.
What could be wrong?
Here's how I would most likely do it, but the BeginInvoke() call that does the magic could just as easily be called from your PropertyChanged handler.
What it's doing is essentially queueing the action to happen after the entire property-set business has fully completed. The DispatcherPriority.ApplicationIdle flag is a key point.
As you've found, it's useless for the PropertyChanged handler and the property setter to raise PropertyChanged while the ComboBox is still in the process of changing its selection. This code lets that whole thing finish, and then immediately changes Selected to something else. At that point, the ComboBox will be at leisure to take notice of your PropertyChanged event and update its own selection.
private string selected;
public string Selected
{
get { return selected; }
set
{
if (selected != value)
{
// Don't let them select "B".
if (value == "B")
{
Dispatcher.CurrentDispatcher.
BeginInvoke(new Action(() => this.Selected = "C"),
DispatcherPriority.ApplicationIdle);
return;
}
selected = value;
OnPropertyChanged("Selected");
}
}
}
Under certain conditions if the user selects an item in a combobox, it automatically must be changed to another item
For what it's worth, I think it would be a good idea to revisit that design choice. It is likely to be confusing to users, and there is probably a better way to present that state of affairs to the user, than to ignore input they give the program. There's not enough context in your question to fully understand how you got into this situation in the first place, so I can't offer anything more than to suggest it's likely better to fix the design, than to finagle the code into doing what you want.
That said…
The issue you are running into is that WPF ignores property-changed events for the source of a binding it is currently already updating. In your scenario, the binding is updating the Selected value from its binding, and so changes to that property will be ignored until that binding update is completed.
There are a variety of ways to get the code to work the way you want. Probably the easiest is to simply defer the update of the source property until the handling of the user input has completed. You can do that by using the Dispatcher.InvokeAsync() method:
void VM_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
Dispatcher.CurrentDispatcher.InvokeAsync(() => this.Selected = "C");
}
I'm not a big fan of the above, because of the fact that it takes what ideally should be a view-agnostic object, the view model, and injects knowledge of your specific view API, i.e. use of the Dispatcher object. That said, there are other mechanisms you could use which are similar, and which don't rely on the Dispatcher object (e.g. using an asynchronous timer).
There are a variety of other examples of ways to address this on Stack Overflow. For example, you might look at this answer for inspiration. I don't think it will do exactly what you want "straight out of the box", but the attached property approach might be something you find more appropriate, by moving the logic from view model to view code, and thus a place where it's more appropriate to use Dispatcher. (And arguably, if you are going to do something like this, the logic probably belongs in the view anyway…it's weird enough there, but I see no compelling reason this should be inherent in the view model.)
Another approach can be seen in the question Coerce a WPF TextBox not working anymore in .NET 4.0. I.e. manually force the view's state to be updated after the fact.
Here is my model class, the column that I am interested in this question:
public class Cell : INotifyPropertyChanged
{
public string TestImageAspect
{
get { return testImageAspect; }
set
{
testImageAspect = value;
Console.WriteLine("OnPropertyChanged => testImageAspect");
this.OnPropertyChanged("OperationResult");
}
}
private string testImageAspect;
}
ImageList is prepared with required images. In the ObjectListView I set appropriate column's ImageAspectName to the property name:
Then on button click I run the following code to change the
Cell c = ...;
c.TestImageAspect = "success"; // the name exist in ImageList
After above code I see that OnPropertyChanged has been called, however UI is not updating, unless I hover to the row where it has to change, then I see new icon. I am not looking for dirty workaround, since I know few, but rather want to understand whether ObjectListView has to update UI itself. If yes, what am I doing wrong?
The ObjectListView property UseNotifyPropertyChanged has to be set true.
From the official documentation
If you set UseNotifyPropertyChanged, then ObjectListView will listen for changes on your model classes, and automatically update the rows when properties on the model classes changed. Obviously, your model objects have to implement INotifyPropertyChanged.
Could you post the XAML for the binding - that might help debug this. Also, it's bit confusing that your property is called TestImageAspect but you're passing "OperationResult" to OnPropertyChanged. I'm not sure if OnPropertyChanged would work either. The more usual way would be to do:-
public class Cell : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public string TestImageAspect
{
get { return testImageAspect; }
set
{
testImageAspect = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("TestImageAspect"));
}
}
}
private string testImageAspect;
}
I have a ListView and a GridView that lists users in an application by names. Whenever the user selects an user to edit, I add a new tab to a TabControl, and bind all editable properties to the WPF controls.
However, when the user is editing in the Edit Tab, the information in the List (specifically, the name field) is also being updated.
Currently I'm making a copy of the object to be edited and leaving the original so it doesn't update the ListView, but isn't there a better/easier way to do this?
I've tried setting the Binding Mode=OneWay, didn't work, and also UpdateSourceTrigger=Explicit in the GridView but also didn't work.
Is there any easier way to do this?
Edit: The way I implemented my INotifyPropertyChanged class is part of the issue, since I have this:
public partial class MyTabControl : UserControl
{
public MyTabControl()
{
InitializeComponent();
//Here, DataContext is a List<Users>
//Users being my Model from the Database
//Some of it's properties are bound to a GridView
//User doesn't implement INPC
}
public void OpenTab(object sender, RoutedEventArgs e)
{
User original = (sender as Button).DataContext as User;
// - This will create a new ViewModel below with the User I'm sending
MyTabControl.AddTab(original);
}
}
And my ViewModel of Users is:
public class UserViewModel : INotifyPropertyChanged
{
public User Original { get; private set; }
public string Name { get { return Original.Name; } set { Original.Name = value; OnPropertyChanged("Name"); } }
public UserViewModel(User original)
{
Original = original ?? new User();
}
// - INPC implementation
}
Since my ViewModel is the one reporting the property changes, I didn't expect my original User to report it as well to the GridView.
The Mode=OneWay causes the information flow to go from the bound data entity to the target UI property only, any change to the UI property will not be bound back.
The reason why the UI content is changing is because the underlying property is read/write (i.e. has a getter and a setter) and is notifying any value change (due to the implementation of the INPC interface).
Presuming that it is a list of User objects you've bound to the GridView, you have two simple options to fix this. Which one is best depends on how much scope for change you have:
change the current Name property on the User object, remove the setter for it. Replace the setter with a method to set the property (i.e. SetUserName(string name)) which then sets the private member variable. Or pass the name as an argument to the constructor of the User entity.
create a new property with only a getter which returns the name and set your binding to that; i.e. public string UserName { get { return Name; }}. As there is only a getter there will be no notification of this property, so if the name does change it won't be propagated via this new property.
So I have three things:
A ListBox in a Window
A DataBase class granting me access to an ObservableList
Contact implements INotifyChanged
In my main Window, I have three Buttons (One for new List Entry, one for editing, one for deleting an item)
I fill the list like this:
lbKontakte.ItemsSource = DB.GetInstance().Kontakte;
whereas Kontakte is a ObservableCollection
I can create a new Entry using
DB.GetInstance().Kontakte.Add(New Kontakt(...));
or remove an entry using
DB.GetInstance().Kontakte.Remove(...);
Boh actions are immediately visible in the ListBox.
If I modify a value however, I'm not using any Code. I have a TextBox which is bound to the Name field of a contact. If I make changes to it, the changes should theoretically be carried out immediately to the bound Contact Object.
However, if I do modify the text, the changes do not become visible in the ListBox. If I pause the code and take a look at the object, I can see its Name Field has correctly been changed.
How come my ListBox is not updated?
PS:
Contact does implement INotifyChanged using following Code:
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(PropertyChangedEventArgs e)
{
if (PropertyChanged != null)
{
PropertyChanged(this, e);
}
}
and
public String Name
{
get { return _name; }
set { _name = value; OnPropertyChanged(new PropertyChangedEventArgs("Name")); }
}
Edit: The Textbox is NOT part of the main Window but a Window showed as a dialog if the user clicks the edit button. The Window is then given the selectedItem casted as Kontakt in the Constructor. Bound to the Field like this:
<TextBox Name="txtName" Grid.Column="1" Grid.Row="0" Margin="4" Text="{Binding Path=Name}"></TextBox>
and
public KontaktAddUI(Kontakt kontaktToEdit)
{
InitializeComponent();
this.kontaktToEdit = kontaktToEdit;
this.MainGrid.DataContext = kontaktToEdit;
}
Correct Answer in the Comments, thanks again!
Your problem is ObservableCollection doesn't get notified if your Item Property Changed that is a known issue To fix this problem you need to wire up your INotifyPropertyChanged event to the CollectionChanged event from your ObservableCollection
Here you can see an example how you could do it.
I was looking at this question, but I don't understand how to actually USE the created AttachedProperty. The problem is trying to have a binding on the source of the WebBrowser control.
The code there looks like:
public static class WebBrowserUtility
{
public static readonly DependencyProperty BindableSourceProperty =
DependencyProperty.RegisterAttached("BindableSource", typeof(string), typeof(WebBrowserUtility), new UIPropertyMetadata(null, BindableSourcePropertyChanged));
public static string GetBindableSource(DependencyObject obj)
{
return (string) obj.GetValue(BindableSourceProperty);
}
public static void SetBindableSource(DependencyObject obj, string value)
{
obj.SetValue(BindableSourceProperty, value);
}
public static void BindableSourcePropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
WebBrowser browser = o as WebBrowser;
if (browser != null)
{
string uri = e.NewValue as string;
browser.Source = uri != null ? new Uri(uri) : null;
}
}
}
and
<WebBrowser ns:WebBrowserUtility.BindableSource="{Binding WebAddress}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled"
Width="300"
Height="200" />
The WebAddress, what is that exactly? This is my understanding (which is probably wrong):
There's an AttachedProperty that can be attached to any object, and in this particular case, it is basically just attaching a property called BindableSource which is of type String.
When we have the "{Binding WebAddress}" it means that in some c# code somewhere that handles this .xaml file there's something that looks like:
public String WebAddress
{
// get and set here? not sure
}
And to take advantage of the property changed, I can called RaisedPropertyChanged and it will fire that static method up there?
Even when I look at it, it doesn't seem right, but I can't find anything online to help me.
There's an AttachedProperty that can be attached to any object, and in this particular case, it is basically just attaching a property called BindableSource which is of type String.
You might want to read the MSDN article on attached properties.
It is rather simple: Dependency properties work with dictionaries in which controls are associated with their values for a property, this makes it quite easy to add something like attached properties which can extend a control.
In the RegisterAttached method of the attached property a PropertyChangedCallback is hooked up which will be executed if the value changes. Using a dependency property enables binding which is the point of doing this in the first place. All the property really does is call the relevant code to navigate the browser if the value changes.
When we have the "{Binding WebAddress}" it means that in some c# code somewhere that handles this .xaml file there's something that looks like [...]
The binding references some public property or depedency property (not a field) called WebAddress inside the DataContext of the WebBrowser. For general information on data-binding see the Data Binding Overview.
So if you want to create a property which should be a binding source you either implement INotifyPropertyChanged or you create a DependencyProperty (they fire change notifications on their own and you normally do only create those on controls and UI-related classes)
Your property could look like this:
public class MyModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
private string _webAddress;
public string WebAddress
{
get { return _webAddress; }
set
{
if (value != _webAddress)
{
_webAddress = value;
NotifyPropertyChanged("WebAddress");
}
}
}
}
Here you have to raise the PropertyChanged event in the setter as you suspected. How to actually declare working bindings in XAML is a rather broad topic sp i would like to direct you to the aforementioned Data Binding Overview again which should explain that.
And to take advantage of the property changed, I can called RaisedPropertyChanged and it will fire that static method up there?
The event is fired to trigger the binding to update, this in turn changes the value of the attached property which in turn causes the PropertyChangedCallback to be executed which eventually navigates the browser.