I'm having a hard time binding a simple static string property to a TextBox.
Here's the class with the static property:
public class VersionManager
{
private static string filterString;
public static string FilterString
{
get { return filterString; }
set { filterString = value; }
}
}
In my xaml, I just want to bind this static property to a TextBox:
<TextBox>
<TextBox.Text>
<Binding Source="{x:Static local:VersionManager.FilterString}"/>
</TextBox.Text>
</TextBox>
Everything compiles, but at run time, I get the following exception:
Cannot convert the value in attribute
'Source' to object of type
'System.Windows.Markup.StaticExtension'.
Error at object
'System.Windows.Data.Binding' in
markup file
'BurnDisk;component/selectversionpagefunction.xaml'
Line 57 Position 29.
Any idea what I'm doing wrong?
If the binding needs to be two-way, you must supply a path.
There's a trick to do two-way binding on a static property, provided the class is not static : declare a dummy instance of the class in the resources, and use it as the source of the binding.
<Window.Resources>
<local:VersionManager x:Key="versionManager"/>
</Window.Resources>
...
<TextBox Text="{Binding Source={StaticResource versionManager}, Path=FilterString}"/>
You can't bind to a static like that. There's no way for the binding infrastructure to get notified of updates since there's no DependencyObject (or object instance that implement INotifyPropertyChanged) involved.
If that value doesn't change, just ditch the binding and use x:Static directly inside the Text property. Define app below to be the namespace (and assembly) location of the VersionManager class.
<TextBox Text="{x:Static app:VersionManager.FilterString}" />
If the value does change, I'd suggest creating a singleton to contain the value and bind to that.
An example of the singleton:
public class VersionManager : DependencyObject {
public static readonly DependencyProperty FilterStringProperty =
DependencyProperty.Register( "FilterString", typeof( string ),
typeof( VersionManager ), new UIPropertyMetadata( "no version!" ) );
public string FilterString {
get { return (string) GetValue( FilterStringProperty ); }
set { SetValue( FilterStringProperty, value ); }
}
public static VersionManager Instance { get; private set; }
static VersionManager() {
Instance = new VersionManager();
}
}
<TextBox Text="{Binding Source={x:Static local:VersionManager.Instance},
Path=FilterString}"/>
In .NET 4.5 it's possible to bind to static properties, read more
You can use static properties as the source of a data binding. The
data binding engine recognizes when the property's value changes if a
static event is raised. For example, if the class SomeClass defines a
static property called MyProperty, SomeClass can define a static event
that is raised when the value of MyProperty changes. The static event
can use either of the following signatures:
public static event EventHandler MyPropertyChanged;
public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;
Note that in the first case, the class exposes a static event named
PropertyNameChanged that passes EventArgs to the event handler.
In the second case, the class exposes a static event named
StaticPropertyChanged that passes PropertyChangedEventArgs to the
event handler. A class that implements the static property can choose
to raise property-change notifications using either method.
As of WPF 4.5 you can bind directly to static properties and have the binding automatically update when your property is changed. You do need to manually wire up a change event to trigger the binding updates.
public class VersionManager
{
private static String _filterString;
/// <summary>
/// A static property which you'd like to bind to
/// </summary>
public static String FilterString
{
get
{
return _filterString;
}
set
{
_filterString = value;
// Raise a change event
OnFilterStringChanged(EventArgs.Empty);
}
}
// Declare a static event representing changes to your static property
public static event EventHandler FilterStringChanged;
// Raise the change event through this static method
protected static void OnFilterStringChanged(EventArgs e)
{
EventHandler handler = FilterStringChanged;
if (handler != null)
{
handler(null, e);
}
}
static VersionManager()
{
// Set up an empty event handler
FilterStringChanged += (sender, e) => { return; };
}
}
You can now bind your static property just like any other:
<TextBox Text="{Binding Path=(local:VersionManager.FilterString)}"/>
There could be two ways/syntax to bind a static property. If p is a static property in class MainWindow, then binding for textbox will be:
1.
<TextBox Text="{x:Static local:MainWindow.p}" />
2.
<TextBox Text="{Binding Source={x:Static local:MainWindow.p},Mode=OneTime}" />
You can use ObjectDataProvider class and it's MethodName property. It can look like this:
<Window.Resources>
<ObjectDataProvider x:Key="versionManager" ObjectType="{x:Type VersionManager}" MethodName="get_FilterString"></ObjectDataProvider>
</Window.Resources>
Declared object data provider can be used like this:
<TextBox Text="{Binding Source={StaticResource versionManager}}" />
If you are using local resources you can refer to them as below:
<TextBlock Text="{Binding Source={x:Static prop:Resources.PerUnitOfMeasure}}" TextWrapping="Wrap" TextAlignment="Center"/>
Right variant for .NET 4.5 +
C# code
public class VersionManager
{
private static string filterString;
public static string FilterString
{
get => filterString;
set
{
if (filterString == value)
return;
filterString = value;
StaticPropertyChanged?.Invoke(null, FilterStringPropertyEventArgs);
}
}
private static readonly PropertyChangedEventArgs FilterStringPropertyEventArgs = new PropertyChangedEventArgs (nameof(FilterString));
public static event PropertyChangedEventHandler StaticPropertyChanged;
}
XAML binding (attention to braces they are (), not {})
<TextBox Text="{Binding Path=(yournamespace:VersionManager.FilterString)}" />
Leanest answer (.net 4.5 and later):
static public event EventHandler FilterStringChanged;
static string _filterString;
static public string FilterString
{
get { return _filterString; }
set
{
_filterString= value;
FilterStringChanged?.Invoke(null, EventArgs.Empty);
}
}
and XAML:
<TextBox Text="{Binding Path=(local:VersionManager.FilterString)}"/>
Don't neglect the brackets
Look at my project CalcBinding, which provides to you writing complex expressions in Path property value, including static properties, source properties, Math and other. So, you can write this:
<TextBox Text="{c:Binding local:VersionManager.FilterString}"/>
Goodluck!
Another solution is to create a normal class which implements PropertyChanger like this
public class ViewProps : PropertyChanger
{
private string _MyValue = string.Empty;
public string MyValue
{
get {
return _MyValue
}
set
{
if (_MyValue == value)
{
return;
}
SetProperty(ref _MyValue, value);
}
}
}
Then create a static instance of the class somewhere you wont
public class MyClass
{
private static ViewProps _ViewProps = null;
public static ViewProps ViewProps
{
get
{
if (_ViewProps == null)
{
_ViewProps = new ViewProps();
}
return _ViewProps;
}
}
}
And now use it as static property
<TextBlock Text="{x:Bind local:MyClass.ViewProps.MyValue, Mode=OneWay}" />
And here is PropertyChanger implementation if necessary
public abstract class PropertyChanger : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Suppose you have a class as follow:
public static class VersionManager
{
public static string FilterString;
}
You can bind your static variable in this way:
<TextBox Text = {x:Static local:VersionManager.FilterString }/>
Related
I have dependency property in SliderViewModel where this view model implements DependencyObject and is set as the data context for BRSliderUserControl. How can I bind to the dependency property in the view model from AmplitudeOptionsUserControl. Is it possible to do so. My guess is I need to create an other dependency property in BRSliderUserControl and then send the update value to the view model. Is this the right way though?
SliderViewModel.cs
public Class SliderViewModel:DependencyObject
{
public AnalysisViewType AnalysisTypeValue
{
get { return (AnalysisViewType)GetValue(AnalysisTypeDependencyProperty); }
set { SetValue(AnalysisTypeDependencyProperty, value); }
}
public static readonly DependencyProperty AnalysisTypeDependencyProperty =
DependencyProperty.Register("AnalysisTypeValue", typeof(AnalysisViewType), typeof(SliderViewModel),
new PropertyMetadata(AnalysisViewType.Unassigned, OnAnalysisTypeChanged));
private static void OnAnalysisTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//Do something here
}
}
BRSliderUserControl.xaml.cs
public BRSliderUserControl()
{
InitializeComponent();
SliderViewModel sliderViewModel = new SliderViewModel();
this.DataContext = sliderViewModel;
}
Now how can I bind to that dependency property from another user control?
AmplitudeOptionsControl.xaml
//This does not work..
<lib:BRSliderUserControl
Grid.Row="5"
Grid.Column="0"
Grid.ColumnSpan="3"
AnalysisTypeValue="{Binding AmplitudeOptionsVM.AnalysisType,RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"
/>
You don't need dependency property in your View Models. Your ViewModel should implement INotifyPropertyChanged interface, and your properties should raise NotifyPropertyChanged event when the value changes. There are many helpers around which makes this a bit easier.
You can use Dependency property if you want, but it makes your view models dependent on WPF, although binding to Dependency properties seems to be much faster (see here: https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/optimizing-performance-data-binding)
You can see a discussion here: INotifyPropertyChanged vs. DependencyProperty in ViewModel
Also, since your DataContext is of type SliderViewModel, which has a public property named AnalysisTypeValue, in your XAML you should bind like this
... AnalysisTypeValue = {Binding AnalysisTypeValue}
Move the dependency property to the code-behind of the UserControl class:
public class BRSliderUserControl
{
public AnalysisViewType AnalysisTypeValue
{
get { return (AnalysisViewType)GetValue(AnalysisTypeDependencyProperty); }
set { SetValue(AnalysisTypeDependencyProperty, value); }
}
public static readonly DependencyProperty AnalysisTypeDependencyProperty =
DependencyProperty.Register("AnalysisTypeValue", typeof(AnalysisViewType), typeof(BRSliderUserControl),
new PropertyMetadata(AnalysisViewType.Unassigned, OnAnalysisTypeChanged));
private static void OnAnalysisTypeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//Do something here
}
public BRSliderUserControl()
{
InitializeComponent();
SliderViewModel sliderViewModel = new SliderViewModel();
this.DataContext = sliderViewModel;
}
}
Add a plain CLR property to the view model:
public class SliderViewModel : INotifyPropertyChanged
{
private AnalysisViewType _analysisTypeValue;
public AnalysisViewType AnalysisTypeValue
{
get { return _analysisTypeValue; }
set { _analysisTypeValue = value; NotifyPropertyChanged(); }
}
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Bind the dependency property in the view to the source property of the view model:
<lib:BRSliderUserControl
Grid.Row="5"
Grid.Column="0"
Grid.ColumnSpan="3"
AnalysisTypeValue="{Binding AnalysisTypeValue}" />
This question already has answers here:
Binding to static property
(12 answers)
Closed 2 years ago.
I had a variable which was not static and INotifyPropertyChanged implemented succesfully. Then I tried to make it global, so turned it a static variable. But this time, INotifyPropertyChanged does not work. Any solution?
INotifyPropertyChanged works on instance properties. One solution is to use a singleton pattern and keep INotifyPropertyChanged, the other is to use your own event to notify listeners.
Singleton example
public sealed class MyClass: INotifyPropertyChanged
{
private static readonly MyClass instance = new MyClass();
private MyClass() {}
public static MyClass Instance
{
get
{
return instance;
}
}
// notifying property
private string privMyProp;
public string MyProp
{
get { return this.privMyProp; }
set
{
if (value != this.privMyProp)
{
this.privMyProp = value;
NotifyPropertyChanged("MyProp");
}
}
}
// INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(info));
}
}
}
EDIT: In WPF 4.5, they introduced property changed mechanic for static properties:
You can use static properties as the source of a data binding. The
data binding engine recognizes when the property's value changes if a
static event is raised. For example, if the class SomeClass defines a
static property called MyProperty, SomeClass can define a static event
that is raised when the value of MyProperty changes. The static event
can use either of the following signatures.
public static event EventHandler MyPropertyChanged;
public static event EventHandler<PropertyChangedEventArgs> StaticPropertyChanged;
Very good example, I used it for some general settings in application, when I want to bind some property online to components
public sealed class DataGridClass:INotifyPropertyChanged
{
private static readonly DataGridClass instance = new DataGridClass();
private DataGridClass() { }
public static DataGridClass Instance
{
get
{
return instance;
}
}
private int _DataGridFontSize {get;set;}
public int DataGridFontSize
{
get { return _DataGridFontSize; }
set { _DataGridFontSize = value;
RaisePropertyChanged("DataGridFontSize");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
Set startup properties:
DataGridClass.Instance.DataGridFontSize = 14(or read from xml)
Bind this to components properties
xmlns:static="clr-namespace:MyProject.Static"
<extgrid:ExtendedDataGrid x:Name="testGrid" ClipboardCopyMode="IncludeHeader" AutoGenerateColumns="False">
<extgrid:ExtendedDataGrid.Resources>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="FontSize"
Value="{Binding Source={x:Static static:DataGridClass.Instance},
Path=DataGridFontSize, Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"/>
</Style>
</extgrid:ExtendedDataGrid.Resources>
When you change this value somewhere in application like "Preferences"->DataGrid FontSize - to it automatically update this property for bindings with UpdateSourceTrigger
private void comboBoxFontSize_DropDownClosed(object sender, EventArgs e)
{
DataGridClass.Instance.DataGridFontSize = Convert.ToInt32(comboBoxFontSize.Text);
}
<ComboBox Grid.Column="1" Grid.Row="0" Height="21" Width="75" Name="comboBoxFontSize" HorizontalAlignment="Left"
VerticalAlignment="Center" DropDownClosed="comboBoxFontSize_DropDownClosed"
ItemsSource="{Binding Source={x:Static commands:ConstClass.ListOfFontSize},Mode=OneWay}"
SelectedItem="{Binding Source={x:Static static:DataGridClass.Instance},Path=DataGridFontSize,
Mode=OneWay,UpdateSourceTrigger=PropertyChanged}"/>
I am unable to understand a thing which is as follows. My project is in Prism 4.1 Sliverlight 5. I'm Using MVVM pattern.
I've a static class like this
{
public static class RegionNames
{
public static string AUTH_LOGIN_REGION = "AuthRegion";
public static string TAB_TEST_REGION = "TabRegion";
public static string USER_TAB_REGION="UserTabRegion";
}
}
I tried to use this class in Shell.xmal like below.
<Grid.Resources>
<inf:RegionNames x:Key="rName"></inf:RegionNames>
</Grid.Resources>
Now this Resource I used in textblock
Result :No text appeared.
<TextBlock Text="{Binding Source={StaticResource rNamee}, Path=USER_TAB_REGION}" Margin="20"></TextBlock>
Now I changed this class like below:
{
public class RegionNames : INotifyPropertyChanged
{
public static string AUTH_LOGIN_REGION = "AuthRegion";
public static string TAB_TEST_REGION = "TabRegion";
public static string USER_TAB_REGION="UserTabRegion";
public RegionNames() {
AuthReginName = "HOLY COW POW POW !!";
}
private string _authReginName;
public string AuthReginName {
get {
return _authReginName;
}
set {
_authReginName = value;
OnPropertyChanged("AuthReginName");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
And used it like this
<TextBlock Text="{Binding Source={StaticResource rNamee}, Path=AuthReginName}"></TextBlock>
Result : Text Appeared
this time it worked. Why? My static defied string values are not coming?.
Is there any relation with Object creation of class & Setting Properties values?
First of all for instance objects you can bind only with properties, that's why second solution works for you. (You can't bind with fields)
And for static properties you can bind with fields but you need to use x:Static markup extension. (same goes for properties as well)
<TextBlock Text="{x:Static inf:RegionNames.USER_TAB_REGION}" Margin="20"/>
My Second Question with this thread About x:Static
For Silverlight 5 who want to Use x:Static in xaml.
http://brianlagunas.com/creating-a-silverlight-5-static-markup-extension/
is Helpful & Worked for me.
I want to bind a TextBox in the window to a property contained within a class that is a variable of the viewmodel and ensure that INotifyPropertyChanged's PropertyChanged event propagates from the class to the parent.
Let me illustrate with an example:
(Window's DataContext is set to an instance of ViewModel)
public class ViewModel {
private OtherClass classInstance = new OtherClass();
public int Attribute {
get { return classInstance.Attribute; }
}
}
public class OtherClass : INotifyPropertyChanged {
private int _attribute;
public int Attribute {
get { return _attribute; }
set {
_attribute = value;
PropertyChanged("Attribute");
}
}
...
}
The problem in this example is that, when Attribute changes, the bound Textbox does not update the binding since I assume it's listening to the ViewModel's PropertyChanged event and not that of the instance of OtherClass.
Any ideas on how to remedy this situation? I was thinking about chaining OtherClass's INotifyPropertyChanged to that of its parent, but there has to be a better way.
Why not bind directly to the class property instead of using a proxy?
public class ViewModel {
private OtherClass classInstance = new OtherClass();
public OtherClass MyOtherClass {
get { return classInstance; }
}
}
Then in your binding you can simply reference the property via the SubClass
{Binding MyOtherClass.Attribute}
A drop dead simple example, but it works!
The Code Behind:
public partial class MainWindow : Window {
private readonly SomeClass _someClass = new SomeClass();
public MainWindow() {
InitializeComponent();
DataContext = _someClass;
}
}
public class SomeClass: INotifyPropertyChanged {
private readonly SomeSubClass _mySubClass = new SomeSubClass();
public SomeSubClass MySubClass {
get { return _mySubClass; }
}
private String _name;
public String Name {
get { return _name; }
set {
_name = value;
OnPropertyChanged("Name");
}
}
//Property Change Stuff
}
public class SomeSubClass : INotifyPropertyChanged {
private String _name;
public String Name {
get {
return _name;
}
set {
_name = value;
OnPropertyChanged("Name");
}
}
//Property Change Stuff
}
The XAML:
<Window x:Class="JWC.Examples.WPF.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow">
<StackPanel>
<Label Content="Name" VerticalAlignment="Top" />
<TextBox Text="{Binding Name}" />
<Label Content="SubClass.Name" />
<TextBox Text="{Binding MySubClass.Name}" />
<Label Content="Bound to Name" />
<TextBlock Text="{Binding Name}" />
<Label Content="Bound to MySubClass.Name" />
<TextBlock Text="{Binding MySubClass.Name}" />
</StackPanel>
</Window>
You will need to do something like this:
public class ViewModel {
public ViewModel() {
classInstance = new OtherClass();
classInstance.PropertyChanged += HandleAttributeChanged;
}
private void HandleAttributeChanged(object Sender, NotifyPropertyChangedEventArgs args) {
PropertyChanged("Attribute");
}
}
I don't show it here, but you should also implement IDisposable and unsubscribe from the PropertyChanged event on your child, otherwise you will leak memory.
Alternatively you can expose the classInstance as a public property and set your binding to: ViewModel.classInstance. Note this needs to be a property and not the field itself.
I think the parent class should subscribe to the child propertyCahnged event..something like:
public class ViewModel
{
private OtherClass classInstance = new OtherClass();
public ViewModel()
{
classInstance.PropertyChanged += NotifyChildAttributeChanged;
}
public int Attribute
{
get { return classInstance.Attribute; }
}
}
NotifyChildAttributeChanged is basically a method that listens only to the "Attribute" property and fires a PropertyChanged notification of its own..
Of course our parent class must implement INotifyPropertyChanged as well as will all ViewModels (preferably) and your DataContext will detect the change.
To get around this you need to implement INotifyPropertyChanged on your view model as well. Just add the interface and the event and the rest will take care of itself - no need to chain the events / calls together.
Check out this for using reflection to get the property as well.
http://tsells.wordpress.com/2011/02/08/using-reflection-with-wpf-and-the-inotifypropertychanged-interface/
I am trying to create a composite DataContext for a UserControl. Basically I have a control which has Order and Package properties and I wanted to create the composite object representing this datasource in XAML rather than in code.
This is how I am trying to display the UserControl (and create the DataContext):
<views:PackageDetailsControl>
<views:PackageDetailsControl.DataContext>
<vm:OrderPackagePair Package="{Binding Package, Mode=OneWay}"
Order="{Binding Order, Mode=OneWay}"/>
</views:PackageDetailsControl.DataContext>
</views:PackageDetailsControl>
The OrderPackagePair object is a simple dependency object that is created in XAML :
public class OrderPackagePair : DependencyObject
{
public OrderDetails Order
{
get { return (OrderDetails)GetValue(OrderProperty); }
set { SetValue(OrderProperty, value); }
}
public static readonly DependencyProperty OrderProperty =
DependencyProperty.Register("Order", typeof(OrderDetails), typeof(OrderPackagePair), new UIPropertyMetadata(null));
public PackageInfo Package
{
get { return (PackageInfo)GetValue(PackageProperty); }
set { SetValue(PackageProperty, value); }
}
public static readonly DependencyProperty PackageProperty =
DependencyProperty.Register("Package", typeof(PackageInfo), typeof(OrderPackagePair), new UIPropertyMetadata(null));
}
Order and Package are not bound correctly and are just null.
Yes I know there's probably a better way of doing this - but I cannot understand why this isn't working. Occasionally in Blend it'll work and then go blank again.
This will not work because DependencyObject(OrderPackagePair class) doesn't monitor internal changes of its dependency properties. As OrderPackagePair object remains the same, DataContext considered as unchanged.
On the opposite site, class Freezable is intented to notify subscribers that instance was changed when one of its dependency properties changed.
So, try to declare Freezable instead of DependencyObject as base class of OrderPackagePair.
------------- UPDATE --------
Yes, it works. In order to prove it I've implemented simple example.
Code of OrderPackagePairClass:
public class OrderPackagePair : Freezable
{
public OrderDetails Order
{
get { return (OrderDetails)GetValue(OrderProperty); }
set { SetValue(OrderProperty, value); }
}
public static readonly DependencyProperty OrderProperty =
DependencyProperty.Register("Order", typeof(OrderDetails), typeof(OrderPackagePair), new UIPropertyMetadata(null));
public PackageInfo Package
{
get { return (PackageInfo)GetValue(PackageProperty); }
set { SetValue(PackageProperty, value); }
}
public static readonly DependencyProperty PackageProperty =
DependencyProperty.Register("Package", typeof(PackageInfo), typeof(OrderPackagePair), new UIPropertyMetadata(null));
protected override Freezable CreateInstanceCore()
{
throw new NotImplementedException();
}
}
XAML:
<Window x:Class="WindowTest.MainWindow"
xmlns:self="clr-namespace:WindowTest"
Name="RootControl">
<StackPanel Margin="10" DataContextChanged="StackPanel_DataContextChanged">
<StackPanel.DataContext>
<self:OrderPackagePair Package="{Binding Path=DataContext.PackageInfo, Mode=OneWay, ElementName=RootControl}"
Order="{Binding Path=DataContext.OrderDetails, Mode=OneWay, ElementName=RootControl}"/>
</StackPanel.DataContext>
<Button Margin="10" Content="Change Package" Click="Button_Click"/>
</StackPanel>
</Window>
And code behind:
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private OrderDetails _orderDetails;
public OrderDetails OrderDetails
{
get
{
return this._orderDetails;
}
set
{
this._orderDetails = value;
this.OnPropertyChanged("OrderDetails");
}
}
private PackageInfo _packageInfo;
public PackageInfo PackageInfo
{
get
{
return this._packageInfo;
}
set
{
this._packageInfo = value;
this.OnPropertyChanged("PackageInfo");
}
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.PackageInfo = new PackageInfo(DateTime.Now.ToString());
}
private void StackPanel_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
Trace.WriteLine("StackPanel.DataContext changed");
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string name)
{
var safeEvent = this.PropertyChanged;
if (safeEvent != null)
{
safeEvent(this, new PropertyChangedEventArgs(name));
}
}
}
When you click the button, model changes PackageInfo property (for simplicity model and view are implemented in the same class). Dependency property OrderPackagePair.Package reacts on new value and overwrites its value. Due to Freezable nature, OrderPackagePair notifies all subscribers that it was changed and handler StackPanel_DataContextChanged is called. If you get back to DependencyObject as base class of OrderPackagePair - handler will be never called.
So, I suppose your code doesn't work because of other mistakes. You should carefully work with DataContext. For example, you wrote:
<views:PackageDetailsControl>
<views:PackageDetailsControl.DataContext>
<vm:OrderPackagePair Package="{Binding Package, Mode=OneWay}"
Order="{Binding Order, Mode=OneWay}"/>
</views:PackageDetailsControl.DataContext>
</views:PackageDetailsControl>
and certainly this is one of the problems. Binding expression is oriented on current DataContext. But you set DataContext as OrderPackagePair instance. So you binded OrderPackagePair.Package to OrderPackagePair.Package (I suppose, that your goal is to bind OrderPackagePair.Package to Model.Package). And that's why nothing happened.
In my example in binding expression I explicitly tell to which DataContext I want to bind:
Package="{Binding Path=DataContext.PackageInfo, Mode=OneWay, ElementName=RootControl}"