I have a small question regarding databinding and user controls.
I construct (using C# 2010) a user control which is basically a wrapper for a ComboBox, and it has a custom property, which when changed, sets the value of the ComboBox. Conversely, should the selected item in the ComboBox change, the value of the property is changed.
Now, I could do this by trapping the "selected value changed" event on the ComboBox, and setting the property, and I could, in the property setter, set the selected value of the ComboBox, but I surmised I might also be able do this with DataBinding.
And it nearly works, but not quite.
It works at run-time, but not at design time and I was wondering if this could be easilly resolved.
For example, if at design time, I select the instance of my user control, and from the properties window select my control's custom property, and change it, the ComboBox does not reflect the change.
Any pointers to something I have missed would be greatfully received. Obviously, I could set the ComboBox selected value, but it would be nice if DataBinding would do it for me.
Thanks
(Here is my user control. Drop one on a form and use the IDE to change the "Position" property)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
namespace WindowsFormsApplication13
{
public partial class UserControl1 : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public enum enumPosition : byte
{
Unknown, First, Second, Third
}
public UserControl1()
{
InitializeComponent();
var bindingList = new BindingList<KeyValuePair<enumPosition, String>>();
foreach (enumPosition value in Enum.GetValues(typeof(enumPosition)))
{
bindingList.Add(new KeyValuePair<enumPosition, String>(value, value.ToString()));
}
this.comboBox1.DisplayMember = "Value";
this.comboBox1.ValueMember = "Key";
this.comboBox1.DataSource = bindingList;
this.comboBox1.DataBindings.Add("SelectedValue", this, "Position", false, DataSourceUpdateMode.OnPropertyChanged);
}
private enumPosition _position = enumPosition.Unknown;
[DefaultValue(typeof(enumPosition), "Unknown")]
public enumPosition Position
{
get { return _position; }
set
{
if (value != _position)
{
_position = value;
this.OnPropertyChanged(new PropertyChangedEventArgs("Position"));
}
}
}
private void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, e);
}
}
}
Works for me too !
Environment - VS .Net 2008
Only difference I think might be 'Re-Building' the application instead of just 'Build' ?
Related
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 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.)
I was experimenting with Data Binding in Windows Forms and found a glitch that I can't explain. I post the question here in hopes that someone in the community can come up with an answer that makes sense.
I tried to come up with a clever way of binding read-only values that depend on operations on other values, and update it automatically when the dependent values change.
I created a form with 3 textboxes, where I want the sum of the first 2 to appear in the 3rd textbox.
The following code should work, but doesn't, at least not properly:
public class Model : INotifyPropertyChanged
{
private int m_valueA;
private int m_valueB;
public int ValueA
{
get { return m_valueA; }
set { m_valueA = value; RaisePropertyChanged("ValueA"); }
}
public int ValueB
{
get { return m_valueB; }
set { m_valueB = value; RaisePropertyChanged("ValueB"); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class DynamicBindingProperty<T> : INotifyPropertyChanged
{
private Func<T> m_function;
private HashSet<string> m_properties;
public DynamicBindingProperty(Func<T> function, INotifyPropertyChanged container, IEnumerable<string> properties)
{
m_function = function;
m_properties = new HashSet<string>(properties);
container.PropertyChanged += DynamicBindingProperty_PropertyChanged;
}
public T Property { get { return m_function(); } }
void DynamicBindingProperty_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (!m_properties.Contains(e.PropertyName)) return;
if (PropertyChanged == null) return;
PropertyChanged(this, new PropertyChangedEventArgs("Property"));
}
public event PropertyChangedEventHandler PropertyChanged;
}
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
InitializeDataBinding();
}
private void InitializeDataBinding()
{
Model model = new Model();
DynamicBindingProperty<int> tmp = new DynamicBindingProperty<int>(() => model.ValueA + model.ValueB, model, new[] {"ValueA", "ValueB"});
textBox1.DataBindings.Add("Text", model, "ValueA");
textBox2.DataBindings.Add("Text", model, "ValueB");
textBox3.DataBindings.Add("Text", tmp, "Property");
tmp.PropertyChanged += (sender, args) => Console.WriteLine(args.PropertyName);
}
}
After experimenting for a while, I tried renaming DynamicBindingProperty<T>.Property to something else (e.g. DynamicProperty), and everything worked as expected!. Now, I was expecting something to break by renaming Model.ValueA to Property, but it didn't, and still worked flawlessly.
What is going on here?
I did some debugging and it looks like a bug (or requirement "the property must not be named Property" I am not aware of). If you replace
PropertyChanged(this, new PropertyChangedEventArgs("Property"));
with
PropertyChanged(this, new PropertyChangedEventArgs(null));
it still does not work - null or an empty string means any property may have changed. This indicates that problem is not in the handling of the change notification but that the binding has not been correctly established.
If you add a second property Property2 to DynamicBindingProperty<T> that does the same as Property and bind it to a fourth text box, then both text boxes will get update correctly if you perform a change notification with an empty string, null or "Property2". If you perform the change notification with "Property" both text boxes will not get update correctly. This indicates that the binding to Property is not completely broken and also that the change notification is somewhat broken.
Sadly I was unable to pin down the exact location where things go wrong, but if you invest enough time stepping through optimized framework source code you can probably figure it out. The earliest difference between the case with property name Property and the case with property name Property2 I could identify when processing a change notification was in OnValueChanged() in the internal class System.ComponentModel.ReflectPropertyDescriptor. In one case the base implementation gets called while it gets skipped in the other case - at least if the debugger didn't trick me, but this is hard to tell in optimized code.
Well the title may not help to much, but here is my problem. I have problems binding properties between controls in SILVERLIGHT. Here is the structure:
There is a control called "ProjectItemList", this control will recieve a string and display it in a very simple way.
There is a control called "ProjectList", this control will recive an array of string and use the control "ProjectItemList" to represent each of the strings in the array.
There is a window called "ProjectWindow", this window contains a "ProjectList" control, and a dependency property called "Nombres".
The context: The "ProjectWindow" window include a "ProjectList" control, there is a binding between ProjectWindow's dependency property "Nombres" and a dependency property "ListOfNames", here is the code of the "ListOfNames" dependency property:
public static DependencyProperty ListOfNamesProperty =
DependencyProperty.Register("ListOfNames", typeof(string[]), typeof(ProjectList), new PropertyMetadata(null));
public string[] ListOfNames
{
get
{
return (string[])GetValue(ListOfNamesProperty);
}
set
{
SetValue(ListOfNamesProperty, value);
List<ProjectItemList> auxList = new List<ProjectItemList>();
foreach (string s in value)
{
ProjectItemList il = new ProjectItemList();
il.Nombre = s;
this.lb_projects.Items.Add(il);
}
}
}
The problem is that, although the list control in "ProjectList" represent the "Nombres" property, the "Set" of "ListOfNames" is never called, so I can't create each of the "ProjectItemList" objects and represent the data.
I'm fairly new into XAML and Silverlight, so i may not considering something simple.
Thanks!
When you use dependency properties, the set and get are not called through ui binding, this is your problem.
WPF ui controls tald directrly with the dependeny property without going through your getter and setter.
You need to specify a callback to monitor property changes:
public static DependencyProperty ListOfNamesProperty =
DependencyProperty.Register("ListOfNames", typeof(string[]), typeof(ProjectList),
new PropertyMetadata(ListOfNamesChaned));
private static void ListOfNamesChaned(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
List<ProjectItemList> auxList = new List<ProjectItemList>();
foreach (string s in value)
{
ProjectItemList il = new ProjectItemList();
il.Nombre = s;
this.lb_projects.Items.Add(il);
}
}
public string[] ListOfNames
{
get
{
return (string[])GetValue(ListOfNamesProperty);
}
set
{
SetValue(ListOfNamesProperty, value);
}
}
See Chen Kinnrot's answer to know why.
For this to achieve, Raise Property Change event can be used i.e with the propertychanged event handler,Code snippet is like:
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
use OnPropertyChanged to bind the property and if any change in that property.it will notify and your binding properties automatically will be changed and ui will be updated which are bound to these properties.
I'm a total newbie, just learning the basics of DataContext and the MVVM model. I've now got a grid bound to a view model object which implements INotifyPropertyChanged, however it appears that UpdateSourceTrigger (which all the WPF tutorials tell me to use) is not available for WinRT / Metro Style apps!
How do I implement INotifyPropertyChanged then?
I'm at the end of my tether here. I've spend nearly the whole day on the most basic of app examples, simply trying to get a grid to update after I click something. The only way I've managed to do this so far is to create an entirely new instance of the view model and reassign the DataContext which I know is wrong
UPDATE:
I have made some progress, but things have gotten very weird. I have a view model, with a generic list of items. The items list is wired up with a PropertyChangedEventHandler. If I replace the entire collection with a new one, the listview updates.
model.Items = new List<DataItem>{ new DataItem{ Title = "new item" }};
This results in a one item list with the above item. However, if I try adding an item, nothing happens
model.Items.Add(new DataItem{ Title = "added item" });
I also tried creating a method which added an item and specifically fired PropertyChanged, but that also doesn't work
Here's where it gets weird. Next I tried this code.
model.Items.Add(new DataItem { Title = "added item" });
model.Items = new List<DataItem> { new DataItem { Title = "new item" }};
This results in a two item list:
- new item
- added item
How can this be? The code says, "add one item" then "replace the whole list" but it executes in the reverse order?
UPDATE 2:
I've switched to ObservableCollection as suggested, which has actually solved the original problem. I can now add an item and it shows up on the list.
However, the new weird behaviour is still in effect. Items added before the collection is reset are appended to the end of the new collection. Why is my code executing in reverse order?
You need to implement the interface and send out the notification once the given property you care about changes.
public event PropertyChangedEventHandler PropertyChanged;
public string CustomerName
{
get
{
return this.customerNameValue;
}
set
{
if (value != this.customerNameValue)
{
this.customerNameValue = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("CustomerName"));
}
}
}
}
Keep in mind that for a collection, you should use an ObservableCollection as it will take care of the INotifyCollectionChanged being fired when an item is added or removed.
I would suggest to scale your sample back as far as possible. Don't start with a DataGrid but rather a simple TextBoxand Button, where the Button forces a change in your ViewModel which will then reflect on the UI.
Code taken from here.
It's best to implement a parent class which implements it like this:
public class NotifyPropertyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
protected void RaisePropertyChanged(string propertyName)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
And then in your subclass (i.e. ViewModel) in your property do something like this:
public class MyViewModel : NotifyPropertyChangedBase
{
private string _name;
public string Name {
get{ return _name; }
set{
_name = value;
RaisePropertyChanged("Name");
}
}
}