Change notification without INotifyPropertyChanged? (excerpt from Pro WPF in C# 2010) - c#

While reading Pro WPF in C# 2010 the author writes:
"You can raise an event for each property. In this case, the event must have the name PropertyNameChanged (for example, UnitCostChanged). It’s up to you to fire the event when the property is changed."
Could someone confirm this feature works? I was experimenting and not able to reproduce this behavior (I want to see if this works so then I can do some experimenting with System.Reflection.Emit to create dynamic types)
EDIT: I should clarify the emphasis here is to implement change notification WITHOUT implementing INotifyPropertyChanged, as this is what the book is claiming
Here's the POCO I am testing with:
public class Employee
{
private string _FirstName;
public string FirstName
{
get
{
return _FirstName;
}
set
{
if (_FirstName != value)
{
_FirstName = value;
if (FirstNameChanged != null)
{
FirstNameChanged(this, new PropertyChangedEventArgs("FirstName"));
}
}
}
}
}
I bound it to a DataGrid and have a timer in the background update the FirstName property randomly every few seconds but the DataGrid never fires
<DataGrid x:Name="dgEmployees" ItemsSource="{Binding ElementName=mainWindow, Path=MyEmployees}">
<DataGrid.Columns>
<DataGridTextColumn Header="FirstName" Binding="{Binding Path=FirstName}" />
</DataGrid.Columns>
</DataGrid>
The FirstNameChanged event is always null (I thought the binding engine might automatically subscribe to it if it detected it according to the naming convention). MyEmployees is just an ObservableCollection
Can someone confirm if this feature the author mentions, does indeed work and if I'm making a mistake?
EDIT: for the benefit of anyone who thinks I'm misinterpreting the text:
"You can use three approaches to solve this problem:
You can make each property in the Product class a dependency property using the
syntax you learned about in Chapter 4. (In this case, your class must derive from
DependencyObject.) Although this approach gets WPF to do the work for you
(which is nice), it makes the most sense in elements—classes that have a visual
appearance in a window. It’s not the most natural approach for data classes like
Product.
You can raise an event for each property. In this case, the event must have the
name PropertyNameChanged (for example, UnitCostChanged). It’s up to you to
fire the event when the property is changed.
You can implement the System.ComponentModel.INotifyPropertyChanged
interface, which requires a single event named PropertyChanged. You must then
raise the PropertyChanged event whenever a property changes and indicate which
property has changed by supplying the property name as a string. It’s still up to
you to raise this event when a property changes, but you don’t need to define a
separate event for each property."

I would say it's very possible to implement change notifications in your POCO's without using either INotifyPropertyChanged or dependency properties, like it's being claimed in the book, unless i'm completely missing the point of the question.
If you raise an event called {Propertyname}Changed from your POCO when the value of a property has changed, the WPF binding system will pick that up, and update the relevant bindings.
See this small program for a demonstration - it's the simplest thing I could think of, but I guess it should work in your case as well.
XAML:
<StackPanel>
<TextBlock Text="{Binding Name}" />
<Button Content="Change name" Click="changeNameClick" />
</StackPanel>
Code-behind:
public partial class Window1 : Window
{
private SimpleObject so = new SimpleObject();
public Window1()
{
InitializeComponent();
this.so = new SimpleObject();
so.Name = "Initial value";
this.DataContext = so;
var t = new DispatcherTimer(
TimeSpan.FromSeconds(1),
DispatcherPriority.Normal,
(s, e) => { this.so.Name = "Name changed at: " + DateTime.Now.ToString(); },
Dispatcher);
}
private void changeNameClick(object sender, RoutedEventArgs e)
{
this.so.Name = "New value!!";
}
}
public class SimpleObject
{
private string mName = null;
public string Name
{
get { return mName; }
set
{
if (mName != value)
{
mName = value;
if (NameChanged != null)
NameChanged(this, EventArgs.Empty);
}
}
}
public event EventHandler NameChanged;
}

The pattern you mention applies to WPF as well as to Windows Forms (WinForms).
WPF, see: "Providing Change Notifications" section in Binding Sources Overview on MSDN.
WinForms, see: How to: Apply the PropertyNameChanged Pattern on MSDN.

No, with WPF, you need to use DependencyProperties or INotifyPropertyChanged. For collections, INotifyCollectionChanged will do the trick too.

Related

WPF MVVM Text Block Binding - Text is not visible [duplicate]

Can someone explain me why need to use implementation of INotifyPropertyChanged when using binding in wpf?
I can bind properties without implementation of this interface?
For example i have code
public class StudentData : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
string _firstName = null;
public string StudentFirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
OnPropertyChanged("StudentFirstName");
}
}
}
And binding in .xaml
<TextBox Text="{Binding Path=StudentFirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Row="1"
Grid.Column="2"
VerticalAlignment="Center" />
this code from .xaml.cs
StudentData _studentData = new StudentData { StudentFirstName = "John", StudentGradePointAverage = 3.5};
public MainWindow()
{
InitializeComponent();
this.DataContext = _studentData;
}
why we need to use INotifyPropertyChanged in this case?
It is not my code.
You need INotifyPropertyChanged if you want a wpf form to be automatically updated when a property changes through code. Also some controllers might want to know if edits have been made in order to enable/disable a save-button, for instance. You also might be displaying the same property on different views; in this case INotifyPropertyChanged helps to immediately update the other view when you edit a property.
If you think that your form behaves well without INotifyPropertyChanged, then you can drop it.
Note that binding works even without INotifyPropertyChanged. See: Why does the binding update without implementing INotifyPropertyChanged?
I would implement the properties like this. In some rare cases it can help to avoid endless circular updates. And it is more efficient by the way.
private string _firstName;
public string StudentFirstName
{
get { return _firstName; }
set
{
if (value != _firstName) {
_firstName = value;
OnPropertyChanged("StudentFirstName");
}
}
}
Starting with C#6.0 (VS 2015), you can implement OnPropertyChanged like this:
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
When you bind to a property of StudentData such as the StudentFirstName then the binding class tests to see if the StudentData instance provides the INotifyPropertyChanged interface. If so then it will hook into the PropertyChanged event. When the event fires and it fires because of the StudentFirstName property then it knows it needs to recover the source value again because it has changed. This is how the binding is able to monitor changes in the source and reflect them in the user interface.
If you do not provide the INotifyPropertyChanged interface then the binding has no idea when the source value changes. In which case the user interface will not update when the property is changed. You will only see the initial value that was defined when the binding was first used.
It does need to be implemented in order for binding to work but that doesn't mean you always have to do it yourself. There are other options like Castle Dynamic Proxy (which wraps your classes in a proxy and injects INPC into all virtual properties) and Fody (which adds it to the IL in a post-processing step). It's also possible to implement yourself while at the same time reducing code bloat, as demonstrated in my answer to this question.

WPF Cant find source for binding with reference - xaml

I'm new to WPF and xaml and I'm making a video player. I'm currently trying to bind the movie time slider to the current elapsed time which I store in a TimeSpan variable which is updated every second thru a DispatcherTimer.
This is my xaml:
<customControls:ThumbDragSlider x:Name="sMovieSkipSlider" Height="25" Margin="65,0,65,71" VerticalAlignment="Bottom"
Value="{Binding ElementName=_movieElapsedTime, Path = TotalSeconds, Mode=OneWay}"
DragStarted="SMovieSkipSlider_OnDragStarted"
DragCompleted="SMovieSkipSlider_OnDragCompleted"/>
This is how the variable is declared:
private TimeSpan _movieElapsedTime;
And this is the error I'm getting:
System.Windows.Data Error: 4 : Cannot find source for binding with reference 'ElementName=_movieElapsedTime'. BindingExpression:Path=TotalSeconds; DataItem=null; target element is 'ThumbDragSlider' (Name='sMovieSkipSlider'); target property is 'Value' (type 'Double')
ElementName is used to refer to an element in the XAML. If you had a control such as...
<TextBox x:Name="_movieElapsedTime" />
Then it would make sense the way you have it -- if it happened to have a property named TotalSeconds.
You also can't bind to a field; it has to be either a regular C# property with a get and maybe a set, or else a special kind of property called a dependency property.
But let's do this with a viewmodel. A viewmodel is any random class that implements INotifyPropertyChanged, and raises the PropertyChanged event when its property values change. It keeps track of the internals of your application. It isn't aware that a user interface exists. It exposes data properties like MovieElapsedTime, and may expose commands as well which allow buttons or menu items to send orders to the viewmodel. It may also have methods that its parent viewmodel may call.
We'll write a viewmodel base class that implements INotifyPropertyChanged, and derive a simple viewmodel from it that represents the things that a video player needs to know. Then we'll create a UI in XAML that lets the user interact with it.
You'll probably want the viewmodel to have commands to start and stop the video and so on. That's easy to find on Google. I'd recommend using a RelayCommand/DelegateCommand class; google those and you'll see what they do. There are a lot of examples out there you can steal the code for.
#region ViewModelBase Class
public class ViewModelBase : INotifyPropertyChanged
{
#region INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propName = null) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
#endregion INotifyPropertyChanged
}
#endregion ViewModelBase Class
#region VideopPlayerViewModel Class
public class VideopPlayerViewModel : ViewModelBase
{
#region MovieElapsedTime Property
private TimeSpan _movieElapsedTime = default(TimeSpan);
public TimeSpan MovieElapsedTime
{
get { return _movieElapsedTime; }
set
{
if (value != _movieElapsedTime)
{
_movieElapsedTime = value;
OnPropertyChanged();
}
}
}
#endregion MovieElapsedTime Property
}
#endregion VideopPlayerViewModel Class
MainWindow constructor:
public MainWindow()
{
InitializeComponent();
DataContext = new VideoPlayerViewModel();
}
XAML. Because A VideoPlayerViewModel is the DataContext for our window, that means that when you tell a binding to bind to MovieElapsedTime, with no further information about where to find it, it will go to the DataContext object it inherited from the window.
<customControls:ThumbDragSlider
Value="{Binding MovieElapsedTime.TotalSeconds, Mode=OneWay}"
x:Name="sMovieSkipSlider"
Height="25"
Margin="65,0,65,71"
VerticalAlignment="Bottom"
DragStarted="SMovieSkipSlider_OnDragStarted"
DragCompleted="SMovieSkipSlider_OnDragCompleted"/>
Non-MVVM version
Here's the dependency property version. It's not "the right way to do it" but it's not totally awful.
Next question: What is MovieElapsedTime a member of? The Window? What is the DataContext? If you set DataContext = this and implemented INotifyPropertyChanged on your window, and raise PropertyChanged when MovieElapsedTime changes, that's a bad idea for other reasons, but your binding will work with MovieElapsedTime as a conventional property. If not, you need this:
<customControls:ThumbDragSlider
Value="{Binding MovieElapsedTime.TotalSeconds, Mode=OneWay, RelativeSource={RelativeSource AncestorType=Window}}"
x:Name="sMovieSkipSlider"
Height="25"
Margin="65,0,65,71"
VerticalAlignment="Bottom"
DragStarted="SMovieSkipSlider_OnDragStarted"
DragCompleted="SMovieSkipSlider_OnDragCompleted"/>
Window codebehind:
public TimeSpan MovieElapsedTime
{
get { return (TimeSpan)GetValue(MovieElapsedTimeProperty); }
set { SetValue(MovieElapsedTimeProperty, value); }
}
public static readonly DependencyProperty MovieElapsedTimeProperty =
DependencyProperty.Register(nameof(MovieElapsedTime), typeof(TimeSpan), typeof(MainWindow),
new PropertyMetadata(null));
Define the property that way instead of what you have. With all that stuff, the control will receive notifications automatically when you set the property to a new value.
You should really write a viewmodel which implements INotifyPropertyChanged and make this a property of the viewmodel. We can go through that if you're interested.
I think you'll need to poll more than once a second, though, if you want the update to be smooth. More likely every 500 ms or even 250. Try 500 and see how it looks.

WPF Databinding - Getter/Setter for intermediary object not getting hit

My WPF TextBox uses a class instance (called "SelectedDocument") as its datacontext. This class implements INOtifyPropertyChanged.
The SelectedDocument instance owns another object of type "CellContent" (named "Description"), exposed via a property.
CellContent also implements INotifyPropertyChanged.
The CellContent class has a string property ("TextValue") that can be bound to.
I'm binding the TextBox's Text property to that TextValue property. Like so:
<TextBox DataContext="{Binding SelectedDocument}" Text="{Binding Path=Description.TextValue" />
No issues with the databinding - it works both ways. But for that binding to work, WPF presumably has to hit the Description property in SelectedDocument every time to retreive the CellContent object:
public CellContent Description
{ get; set; }
(This property is more complicated in my code.) Then WPF can reach inside the actual CellContent object and get/set TextValue.
The problem: the Description property is never hit. It looks like WPF is bypassing it, and has created a direct connection to the TextValue property inside of the Description object. I want to hit the Description getter and setters every time so that I can execute some extra code there.
How do I do that?
If you want the Document to be marked dirty whenever a child's property is changed, you could subscribe to the child's PropertyChanged event.
I'm assuming your CurrentDocument looks something like this.
public class Doc
{
public Doc()
{
_description = new CellContent();
// subscribe to changes in child
_description.PropertyChanged += DescriptionChanged;
}
private void DescriptionChanged(object sender, PropertyChangedEventArgs e)
{
Debug.Write($"I'm a dirty dirty document. Property {e.PropertyName} has changed");
}
private CellContent _description;
public CellContent Description
{
get
{
Debug.Write("I assure you this is called every time a getter of the child properties is called");
return _description;
}
// If you have a setter, don't forget to -= unsubscribe and resubscribe += after changing
}
}
Try to to raise PropertyChanged event for Description every time when you change TextValue property.

Showing Results of Calculation while typing c# (something like deepbinding in Java(FX))

i'm looking for a possibility to Update a TextBox TextProperty while typing in an other Textbox.
I got a Couple of Double Properties, all entered via Textboxes and i got 1 ReadOnly Property using the Double Properties for doing some Math and returning the result.
I want that result to be displayed in a ReadOnly TextBox (WPF) and it should update automatically when typing/changing an Input Parameter TextBox.
As i worked with JavaFX the last years i could use a deepbinding to do that:
DoubleBinding calcBind = new DoubleBinding(){
{
super.bind(PropA, PropB, PropC);
}
#Override
protected double computeValue(){
return (PropA * PropB / PropC)
}
};
The Dependencies statet with super.bind(...) are automatically checked if they changed and the result is automatically updated.
Now i'm just lookin for a possibility to realize that in C#
As said i am using a couple of properties. Also INotifyPropertyChanged is implemented. So PropA, B and C stating OnPropertyChanged("PropA") when set. The Calculation is done in a Readonly Property like
public Double MathProp
{
get
{
return PropA*PropB/PropC;
}
}
I am Binding it via Code to a TextBox TextProperty.
Binding mathBinding = new Binding
{
Source = ObjectHolding.MathProp,
Path = new PropertyPath("MathProp"),
UpdateSourceTrigger = UpdateSourceTrigger.Explicit,
Mode = BindingMode.OnyWay
};
TextBox txtMathBox = new TextBox();
txtMathBox.SetBinding(TextBox.TextProperty, mathBinding);
I used the Explicit Trigger to do it the "BruteForce"-Way and updating it manually when a KeyEvent occurs. But its just not working very well and i dont like that way of resolving the problmen. There has to be a more elegant way. I hope u could point me in the right direction.
I thought about using a converter instead of a ReadOnly Property to do the calculations. But i am not converting i am just doing some math and not changing the types.
Would be glad if u got some hints for me.
Thanks
Ok, let me split this into parts:
As i worked with JavaFX the last years
Forget java.
java's approach is tremendously inferior (just as everything else in java), due to the tight coupling that occurs between your UI and the application logic behind it.
If you're working with WPF, it is strongly recommended that you leave behind any and all approaches you might have learned from archaic, legacy technologies and understand and embrace The WPF Mentality (this is getting very repetitive).
Basically, in WPF you don't put "calculations" or other code that doesn't really belong into the UI into UI-related classes. Instead, you create a proper ViewModel which is completely agnostic from the UI and contains whatever data you need to display, and the logic you need to perform on it, and then simply use DataBinding to populate the XAML-defined UI with such data.
So, basically, this is the correct way to do what you're trying to achieve, as per your comments, in WPF:
public class ViewModel : INotifyPropertyChanged
{
private double _value1;
private double _value2;
private double _result;
public double Value1
{
get { return _value1; }
set
{
_value1 = value;
OnPropertyChanged();
OnPropertyChanged("Result");
}
}
public double Value2
{
get { return _value2; }
set
{
_value2 = value;
OnPropertyChanged();
OnPropertyChanged("Result");
}
}
public double Result
{
get { return Value1*Value2; }
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML:
<Window x:Class="WpfApplication4.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>
<TextBox Text="{Binding Value1, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Text="{Binding Value2, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Text="{Binding Result, Mode=OneWay}" IsReadOnly="True"/>
</StackPanel>
</Window>
Code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
Notice how I'm setting UpdateSourceTrigger=PropertyChanged on the first and second bindings. This causes the binding to update the value source immediately when the target changes (which means it's being done at every key press).
Also notice that the ViewModel must Implement INotifyPropertyChanged in order to properly support two-way WPF DataBinding.
Notice how the logic is completely decoupled from the UI and it's really Simple, Simple Properties and INotifyPropertyChanged. No need for bizarre manipulations of the binding framework or stupid #override (whatever that means) boilerplate.
Also see how I'm using XAML rather than procedural code to Define the UI, and then simple DataBinding to connect it to the underlying data. In WPF, manipulating UI elements in procedural code is discouraged and unneeded.
WPF Rocks. C# Rocks. java is legacy.
Let me know if you need further help.

How do I get databound text blocks in a custom control to update?

The setup
So I have a class, ComputerItem, designed to store everything I need to know about a specific computer; these items are stored in an ObservableCollection<ComputerItem>. I then have a custom control ComputerControl, which has (among other things) a few text boxes bound to members of ComputerItem, the bindings made available like so:
<TextBlock Name="tb_computerName"TextWrapping="Wrap" Text="{Binding ElementName=ComputerControl1, Path=computerName}"/>
and in the code behind
public static DependencyProperty computerNameProperty = DependencyProperty.Register("computerName", typeof(string), typeof(ComputerControl), null);
I then create a MultiselectList of ComputerControl objects:
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<toolkit:MultiselectList x:Name="lb_computers" IsSelectionEnabledChanged="lb_computers_IsSelectionEnabledChanged"> <!--SelectionChanged="lb_computers_SelectionChanged" >-->
<toolkit:MultiselectList.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="sp">
<local:ComputerControl computerName="{Binding Name}" MACAddress="{Binding DisplayMAC}" playClicked="playClicked_Handler" editClicked="editClicked_Handler"/>
</StackPanel>
</DataTemplate>
</toolkit:MultiselectList.ItemTemplate>
</toolkit:MultiselectList>
</Grid>
and you can see the data bindings in the ComputerControl definition. In the code behind I bind the ObservableCollection to the MultiselectList:
this.lb_computers.ItemsSource = ComputerListMaintainer.GetList();
and all of this (as well as a few things I'm sure I've forgotten to include here) works beautifully to fill the MultiselectList with ComputerControls representing the ComputerItems in the ObservableCollection.
The problem
My issue is that when the underlying ComputerItem changes, the TextBlocks in the corresponding ComputerControl don't update. I've implemented INotifyPropertyChanged in the ComputerItem class:
public class ComputerItem : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string name;
protected virtual void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
public string Name
{
get { return name; }
set { OnPropertyChanged("Name"); name = value; }
}
}
but that didn't solve the problem. I suspect it's something to do with ComputerControl but I have no idea where to start looking; the closest question I've found suggested INotifyPropertyChanged should have been the solution but they weren't using a custom control in that case, just a custom class, if I remember correctly.
What am I missing?
Well your setter is incorrect for starters; also do look into MvvmLight, as it provides a great API for this kind of plumbing work.
public string Name
{
get { return name; }
set
{
if(value != name)
{
name = value;
OnPropertyChanged("Name");
}
}
}
Update:
You shouldn't be setting lb_computers.ItemsSource in your code behind, because that's one time operation and not a binding. It is better to bind to an ObservableCollection of observable objects (aka INotifyPropertyChanged).
Also I'm not sure if you're properly declaring your dependency property, so below you can find a proper setup on how to define a 'bindable' property.
And also with XAML, the architecture of your code matters, to have a sane experience. I highly recommend that you utilize the Mvvm pattern. I find MvvmLight and MEFedMVVM to be great aids in my development. This require a bit of work at the beginning, but it'll be far easier to debug future issues and maintain your code.
If these tips don't help, then I'd have to see your full code.
Declaring a Bindable Property
#region ReportName
public string ReportName
{
get { return (string)GetValue(ReportNameProperty); }
set { SetValue(ReportNameProperty, value); }
}
public static readonly DependencyProperty ReportNameProperty = DependencyProperty.Register("ReportName",
typeof(string), typeof(ExportableGridView), new PropertyMetadata("Report", new PropertyChangedCallback(OnReportNameChanged)));
public static void OnReportNameChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
ExportableGridView control = sender as ExportableGridView;
control.titleTextBlock.Text = e.NewValue as string;
}
#endregion ReportName

Categories

Resources