I have a WPF ViewModel
class MainWindowViewModel : INotifyPropertyChanged
{
private string _sql;
public string Sql
{
get { return _sql; }
set
{
if (value == _sql) return;
OnPropertyChanged("Sql");
_sql = value;
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
I also have a XAML view with a TextBox
<Window.Resources>
<HbmSchemaExporter:MainWindowViewModel x:Key="viewModel"/>
</Window.Resources>
....
<TextBox Grid.Row="6" Grid.Column="0" Grid.ColumnSpan="2" Text="{Binding Source={StaticResource ResourceKey=viewModel}, Path=Sql,Mode=OneWay}"/>
Code behind
private MainWindowViewModel ViewModel
{
get { return Resources["viewModel"] as MainWindowViewModel; }
}
The problem is that when in the code I do viewModel.Sql = SOMETHING the text box doesn't get updated. Debugger displays the correct value in the property but the textbox remains blank.
I also tried to change the binding to TwoWay but that only allows me to overwrite the property with a value I type in the textbox, which is something I don't really want (actually I still need to make it readonly, but it's currently out of scope).
How can I update the textbox after programmatically updating the property?
The application is basically a NHibernate DDL generator I'm writing after reading this. I need to press a "Generate SQL" button and it displays the code to run onto DB.
public string Sql
{
get { return _sql; }
set
{
if (value == _sql) return;
OnPropertyChanged("Sql");
_sql = value;
}
}
That does not make sense. At the point that any PropertyChanged event handler is called, reading Sql will still give the old value, because you haven't updated _sql yet. You need to first update the value, and only then raise the PropertyChanged event.
Related
Binding class provides listening for property paths
Example:
<TextBlock Text="{Binding A.B.C.D.E}" />
Reflects any change in the property chain
I want to utilize this mechanism in code behind.
What I want to do:
var binding = new Binding{
Source = ViewModel,
Path = new PropertyPath("A.B.C.D.E")
};
// not available
binding.ResultChanged += OnBindingResultChanged;
But "Binding" doesn't provide any events
I think u should use the Onpropertychanged Event.
You implement it like this
public class YourClass : window, INotifyPropertyChanged
public string A.B.C.D.E {Get ; set ;}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
after this in your XAML you can raise an event when the property is changed.
<TextBlock Text="{Binding A.B.C.D.E}" textchanged="YOURMETHODENAME"/>
When this methode is called you can change the value of the property in the code behind.
public void YOURMETHODENAME (sender e, routeeventargs)
{
//dostuff with your property
OnPropertyChanged ("A.B.C.D.E)
}
this way you can change the value from the property in the code behind and from user input.
I am very new to the concept of data binding and I don't think I understood it completely. I have a class named Project with a LinkedList of type ToDo as one of its properties. When I navigate to one instance of Project, I will display the LinkedList of type ToDo in a ListView. I have created functions that allow me to change the sequences of the nodes in the LinkedList (move up, move down) and to remove the selected node (delete). I want the ListView to refresh whenever there is a change in the LinkedList, (move up, move down or delete). However, I cannot achieve that. Here is my code: (not all parts are included)
XAML of the page:
<ListView x:Name="myListView" ItemsSource="{Binding Source={StaticResource ToDos}, Mode=TwoWay}">
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox x:Name="myCheckBox"
Content="{Binding ToDoTitle, Mode=TwoWay}"
IsChecked="{Binding IsCompleted, Mode=TwoWay}">
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
C# for DataModel:
public class ToDo : INotifyPropertyChanged
{
private string toDoTitle;
private bool isCompleted;
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public string ToDoTitle { get { return this.toDoTitle; } set { this.toDoTitle = value; this.OnPropertyChanged(); } }
public bool IsCompleted { get { return this.isCompleted; } set { this.isCompleted = value; this.OnPropertyChanged(); } }
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
// Raise the PropertyChanged event, passing the name of the property whose value has changed.
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public class Projects : INotifyPropertyChanged
{
private LinkedList<ToDo> toDos;
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public LinkedList<ToDo> ToDos { get { return this.toDos; } set { this.toDos = value; this.OnCollectionChanged(); } }
public Projects()
{
ToDos = new LinkedList<ToDo>();
}
public void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
// Raise the PropertyChanged event, passing the name of the property whose value has changed.
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Thank you.
First I would advise you to read about MVVM, and try to follow some basic tutorials like this one.
You can use MVVM Light to avoid managing the INotifyPropertyChanged by yourself at first (but it's really good to know how MVVM light work under the hood).
To come back to your problem, your current code notifies only if you set the full ToDos list. If you want to be aware of any change in a list (seing when an item is add/remove/update), you are probably looking for an ObservableCollection, not a LinkedList.
Hope it helps.
I'm trying to bind a text block to my variable slctItem. I can see it contain the necessary data I need however my window does not show the data I'm expecting. Here is the code behind for my control. This control is used by a pop up window which will display the values of the control.
When walking the code I see that handler returns null every time in the OnPropertyChanged() method. Why? I must be doing something wrong here. Again slcItemdoes contain the data I'm wanting to use. The OnPropertyChanged() method also fires it just contains null for handler.
public partial class MetaData : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _slctItem;
public MetaData()
{
InitializeComponent();
}
public string slctItem
{
get
{
return _slctItem;
}
set
{
_slctItem = value;
OnPropertyChanged("slctItem");
}
}
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
internal void Refresh()
{
try
{
// If DataContext is Null or a detached DataRow, disable the view
if (DataContext != null && (DataContext is DataRow && ((DataRow)DataContext).RowState != System.Data.DataRowState.Detached))
{
if (DataContext is "Something Here")
{
slctItem = (("Something Here")this.DataContext).NAME;
}
}
}
catch (Exception e)
{
throw new Exception("MetaData -> Refresh(): " + e.Message);
}
}
Here is the XAML code for my control. Here I'm trying to bind to slctItem
<TextBox Grid.Column="2" Grid.Row="0" Text="{Binding Path=slctItem, Mode=OneWay, Converter={StaticResource myFirstCharToUpperConverter}}" Width="150" Height="25" HorizontalAlignment="Left" />
You need to set the DataContext to yourself:
public MetaData()
{
InitializeComponent();
this.DataContext = this;
}
This will allow the binding to find the appropriate property. Right now, if you look at the Debug Output in the Output Window at runtime, you should see binding errors since the data context is unset.
I have a wpf gui page with a textbox that is bound to a property of an innerclass in my window. I have defined the textbox to be bound like so:
XAML:
<TextBox Name="shhh" Text="{Binding Path=derpDerp, Mode=OneWay,
UpdateSourceTrigger=PropertyChanged}" />
CodeBehind:
namespace ...
{
public partial class MainWindow : Window
{
innerclass definition....
public Herp derp;
public MainWindow()
{
...
derp = new Herp();
shhh.DataContext = derp;
...
}
{code that changes derp.derpDerp}
}
}
InnerClass:
public class Herp : INotifyPropertyChanged
{
private secret = "";
public event PropertyChangedEventHandler PropertyChanged;
public Herp(string derp)
{
secret = derp;
}
public string derpDerp
{
get{ return secret; }
set{ secret = value; onPropertyChanged("derpDerp"); }
}
private void OnPropertyChanged(string name)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
What I was wondering is if I can declare the source of the textbox in the xaml. I have seen many examples that say to set the textbox to the datacontext of the parent like the window or a container around the textbox. However i don't find that very intuitive if only 1 control needs the data. It would make sense if I have several textboxes and a stackpanel with a datacontext.
In my implementation I create the object in code and set the datacontext to just the textbox. Is there an equivalent xaml solution?
Something like:
<TextBox Source="something" Path=derpDerp..../>
without setting a datacontext to a container or the window. Also, I didn't know how to set the datacontext of the window to my property correctly because it's an inner class with a namespace of the namespace.the window class or something like that.
What would be the proper way of just giving the textbox a datasource or if not possible how do I reference the innerclass and set the source to the window?
Yes, you can create an instance of a class and set it as DataContext on any control in XAML. The general solution would be like this:
<Window x:Class="MyProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyProject">
<Window.Resources>
<local:Herp DerpDerp="This is Derp!!" x:Key="derp"/>
</Window.Resources>
<Grid>
<TextBox Text="{Binding Source={StaticResource derp}, Path=DerpDerp}"/>
</Grid>
Notice that I defined a new xmlns object called local, which points to the namespace in which the class I'm trying to create resides (in this case, it's Herp).Then, in my Window.Resources, I create an instance of Herp, and set a value for the DerpDerp property. Also notice that I gave the class a key, which is necessary in order for the TextBox to find it and bind to it.
Big note: In order for you to be able to create an instace of a class in XAML, the class needs to have a parameter-less constructor! So I changed Herp a little bit:
namespace MyProject
{
public class Herp : INotifyPropertyChanged
{
private string m_derp;
public Herp()
{
}
public string DerpDerp
{
get { return m_derp; }
set { m_derp = value; OnPropertyChanged("DerpDerp"); }
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Finally, in your TextBox, you use the Source element in your binding to bind to the object:
<TextBox Text="{Binding Source={StaticResource derp}, Path=DerpDerp}"/>
Perhaps I don't understand events fully.
I'm building a Windows Phone 7 app in Silverlight.
I have a UserControl that wraps a ListBox, called EditableListBox. The ListBox has a data template. The items in the list box are wrapped by EditableListItem objects.
The data template is as follows:
<DataTemplate>
<Grid ManipulationCompleted="Grid_ManipulationCompleted">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Image Source="{Binding Path=IconSource}"
Grid.Column="0"
Width="96"
Height="96"
VerticalAlignment="Center"
Visibility="{Binding Path=Editing, Converter={StaticResource visibilityConverter}}"
/>
<TextBlock Text="{Binding Path=Name}" Grid.Column="1" />
</Grid>
</DataTemplate>
I'm binding the Visibility to a property of each EditableListItem, so I need to implement INotifyPropertyChanged so updates to the backing items are reflected in the UI. (Right? Or is there a simpler way to do it?)
EditableListItem:
public class EditableListItem : INotifyPropertyChanged
{
private EditableListBox _parentListBox;
public event PropertyChangedEventHandler PropertyChanged;
public bool Editing
{
get
{
return _parentListBox.Editing;
}
}
public EditableListItem(Section section, EditableListBox parentListBox)
{
_parentListBox = parentListBox;
// after this line, _parentListBox.PropertyChanged is still null.
// why is that?
_parentListBox.PropertyChanged += PropertyChanged;
_parentListBox.PropertyChanged += new PropertyChangedEventHandler(_parentListBox_PropertyChanged);
}
EditableListBox:
public partial class EditableListBox : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// NotifyPropertyChanged will raise the PropertyChanged event,
// passing the source property that is being updated.
public void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public void SetSectionsSource(ObservableCollection<Section> sectionsSource)
{
sectionsSource.CollectionChanged += new NotifyCollectionChangedEventHandler(sectionsSource_CollectionChanged);
ContentListBox.ItemsSource = sectionsSource.Select(section => new EditableListItem(section, this) { Enabled = true });
//ContentListBox.ItemsSource.Add(new EditableListItem(new Section("Section", 3)) { Enabled = true });
}
// ...
private bool _editing;
public bool Editing
{
get
{
return _editing;
}
set
{
_editing = value;
NotifyPropertyChanged("Editing");
}
}
}
The Editing property is stored in EditableListBox - EditableListItem just forwards it. I wanted to attached EditableListItem.PropertyChanged to EditableListBox.PropertyChanged directly, but the following didn't work:
// after this line, _parentListBox.PropertyChanged is still null.
// why is that?
_parentListBox.PropertyChanged += PropertyChanged;
The following did work:
_parentListBox.PropertyChanged += new PropertyChangedEventHandler(_parentListBox_PropertyChanged);
Why is this? Is the first attempt totally invalid (if so, why does the compiler allow it?)?
To begin with, you don't wire up the PropertyChanged to implement it. The idea is that WPF uses that event and it wires it up. The only thing you do is trigger the event when applicable.
And that's a part of the issue here. You have the Editing property, but it is not being fired. I do understand that you have wired the PropertyChanged of the parent listbox to get the event to fire, but that is not going to work.
If I get the idea right, what you want to accomplish is when the Editing property of the listbox gets changed, you want the PropertyChanged of the list item to be forced.
One of the things of PropertyChanged is that the sender has to be the object where the PropertyChanged is located. This means that you should implement it like this:
public partial class EditableListBox : UserControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// You really should make this protected. You do not want the outside world
// to be able to fire PropertyChanged events for your class.
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private bool _editing;
public bool Editing
{
get
{
return _editing;
}
set
{
_editing = value;
NotifyPropertyChanged("Editing");
}
}
}
public class EditableListItem : INotifyPropertyChanged
{
private EditableListBox _parentListBox;
public EditableListItem(EditableListBox parentListBox)
{
_parentListBox = parentListBox;
_parentListBox.PropertyChanged += new PropertyChangedEventHandler(_parentListBox_PropertyChanged);
}
void _parentListBox_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
// Forward the event.
if (e.PropertyName == "Editing")
NotifyPropertyChanged("Editing");
}
public event PropertyChangedEventHandler PropertyChanged;
// You really should make this protected. You do not want the outside world
// to be able to fire PropertyChanged events for your class.
protected void NotifyPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public bool Editing
{
get
{
return _parentListBox.Editing;
}
}
}
I don't know how you get the reference to the editable listbox, but lets say you get it via the constructor. When you get the reference, you attach the the PropertyChanged event handler of the listbox. Because, when the Editing property of that object changes, actually, your Editing property changes too. This is how you simulate that.
One last thing: the reason why the PropertyChanged is still null after the += PropertyChanged is because the PropertyChanged of the object itself is null. You cannot wire the events in this way. The second way is the correct way of wiring up the events, and the above example shows what you do with this.