Binding to dependency property in a ViewModel - wpf

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}" />

Related

Correct way to update property in ViewModel from Model

I'm fairly novice with WPF. It's my understanding that data changes in the model, and it should notify the viewmodel, and the view will bind to properties and things alike in the viewmodel. Is this correct? If so, I've been reading that the model should implement INotifyPropertyChanged, and look something like this
public class LoginModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
public bool Authenticated { get; set; }
}
and in my ViewModel, I have a property "AuthResult", that should get the update from the Model property "Authenticated"
public partial class view1 : UserControl, INotifyPropertyChanged{
public bool AuthResult
{
get
{
return _authVal;
}
set
{
_authVal = value;
NotifyPropertyChanged("AuthResult");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
I know this current implementation is incorrect. I've found that I should be subscribing to the PropertyChanged notification from my model like so:
LoginModel.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(LoginModel_PropertyChanged);
void LoginModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if(e.PropertyName == "Authenticated")
{
//do something
}
}
I don't see where the "AuthResult" property should be updated. Would I do something in the If statement like AuthResult = _model.Authenticated;?
EDITED:
and in my constructor?
LoginModel _model;
public view1(LoginModel model)
{
_model = model;
InitializeComponent();
}
If the model implements the INotifyPropertyChanged interface you can bind directly to it from the view:
<Button Content="Button" IsEnabled="{Binding Authenticated}" />
Note that the LoginModel class must raise the PropertyChanged event whenever the Authenticated property is set to a new value.
You could also expose the entire model entity through the view model class:
public class ViewModel
{
public ViewModel(LoginModel model)
{
Model = model;
}
public LoginModel Model { get; }
}
...and bind to it like this:
<Button Content="Button" IsEnabled="{Binding Model.Authenticated}" />
It is still the model class that must implement the INotifyPropertyChanged interface and raise change notifications.
Another option is for the view model to wrap any property of the model class that you want to be able to bind to from the view. Then you bind to a property of the view model class that in turn wraps a property of the model class something like this:
public class ViewModel
{
private readonly LoginModel _model;
public ViewModel(LoginModel model)
{
_model = model;
}
public bool AuthResult
{
get
{
return _model.Authenticated;
}
set
{
_model.Authenticated = value;
NotifyPropertyChanged("AuthResult");
}
}
}
<Button Content="Button" IsEnabled="{Binding AuthResult}" />
The benefit of using this latter approach is that view has no dependency upon the model class. It binds to the view model class only and this is how the MVVM design pattern typically is meant to be implemented.
But if you do bind to a (wrapper) property of the view model and want the view to be updated whenever a property of the model class is set, the model has to notify the view model that it has changed one way or another, i.e. it has to raise some kind of event or similar. And this typically means implementing the INotifyPropertyChanged interface. The view model can then subscribe to the PropertyChanged event of the model and raise its own PropertyChanged event for the data bound property whenever the model is updated, e.g.:
public class ViewModel
{
private readonly LoginModel _model;
public ViewModel(LoginModel model)
{
if (model == null)
throw new ArgumentNullException("model");
_model = model;
_model.PropertyChanged += OnModelChanged;
}
private void OnModelChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Authenticated")
NotifyPropertyChanged("AuthResult");
}
public bool AuthResult
{
get
{
return _model.Authenticated;
}
set
{
_model.Authenticated = value;
NotifyPropertyChanged("AuthResult");
}
}
}
Just use Model as member in the ViewModel
public class ViewModel
{
private Model _myModel;
public Model MyModel
{
get { return _myModel; }
set
{
if (Equals(_myModel, value)) return;
_myModel = value;
NotifyPropertyChanged(nameof(MyModel));
}
}
}
Then in xaml you can bind properties of Model
<CheckBox IsChecked="{Binding MyModel.Authenticated}" />
With this approach your ViewModel will be "build" around your Model.
In case you don't want that models implement INotifyPropertyChanged than create a "Facade" class of model in use it in same way as previous example.
public class ModelFacade : INotifyPropertyChanged
{
private Model _myModel;
public bool Authenticated
{
get { return _myModel.Authenticated; }
set
{
_myModel.Authenticated = value;
NotifyPropertyChanged(nameof(Authenticated));
}
}
}
public class ViewModel
{
private ModelFacade _myModel;
public ModelFacade MyModel
{
get { return _myModel; }
set
{
if (Equals(_myModel, value)) return;
_myModel = value;
NotifyPropertyChanged(nameof(MyModel));
}
}
}

Create composite DataContext in XAML for a Usercontrol

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}"

WPF Binding Not Working

I am pretty sure I am doing something dreadfully wrong, but can't figure it out.
I created a simple wrapper around a class and added a dependency property so I could bind to it. However, the binding gives no errors, but does nothing.
In order to simplify things I changed the class to TextBox, and got the same results.
public class TextEditor : TextBox
{
#region Public Properties
#region EditorText
/// <summary>
/// Gets or sets the text of the editor
/// </summary>
public string EditorText
{
get
{
return (string)GetValue(EditorTextProperty);
}
set
{
//if (ValidateEditorText(value) == false) return;
if (EditorText != value)
{
SetValue(EditorTextProperty, value);
base.Text = value;
//if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("EditorText"));
}
}
}
public static readonly DependencyProperty EditorTextProperty =
DependencyProperty.Register("EditorText", typeof(string), typeof(TextEditor));
#endregion
#endregion
#region Constructors
public TextEditor()
{
//Attach to the text changed event
//TextChanged += new EventHandler(TextEditor_TextChanged);
}
#endregion
#region Event Handlers
private void TextEditor_TextChanged(object sender, EventArgs e)
{
EditorText = base.Text;
}
#endregion
}
When I run the following XAML the first gives results, but the second one (EditorText) doesn't even hit the EditorText property.
<local:TextEditor IsReadOnly="True" Text="{Binding Path=RuleValue, Mode=TwoWay}" WordWrap="True" />
<local:TextEditor IsReadOnly="True" EditorText="{Binding Path=RuleValue, Mode=TwoWay}" WordWrap="True" />
You're doing extra work in your CLR property. There is no guarantee that your CLR property will be used by WPF so you shouldn't be doing this. Instead, use metadata on your DP to achieve the same effect.
public string EditorText
{
get { return (string)GetValue(EditorTextProperty); }
set { SetValue(EditorTextProperty, value); }
}
public static readonly DependencyProperty EditorTextProperty =
DependencyProperty.Register(
"EditorText",
typeof(string),
typeof(TextEditor),
new FrameworkPropertyMetadata(OnEditorTextChanged));
private static void OnEditorTextChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var textEditor = dependencyObject as TextEditor;
// do your extraneous work here
}

Silverlight 3 user control data Binding in xaml

I’m trying to achieve something that is conceptually quite simple but can’t seem to get it working.
I have a class called c1 it has 2 dependency properties in it an integer I and a string S. It implements INotifiyPropertyChanged.
public class c1: INotifyPropertyChanged
{
private int i;
public int I { get { return i; } set { i = value; if(PropertyChanged != null) PropertyChanged(this,new PropertyChangedEventArgs("I")); } }
private string s;
public string S { get { return s; } set { s = value; if (PropertyChanged != null) PropertyChanged(this, new PropertyChangedEventArgs("S")); } }
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
This class is referenced by a Silverlight user control SUC that also implements INotifiyPropertyChanged as a dependency property C, with a PropertyChangedCallback etc. As seen below.
public partial class SUC : UserControl, INotifyPropertyChanged
{
public c1 C
{
get { return (c1)GetValue(CProperty); }
set { SetValue(CProperty, value); }
}
public static readonly DependencyProperty CProperty =
DependencyProperty.Register("C", typeof(c1), typeof(SUC), new PropertyMetadata(new c1(), new PropertyChangedCallback(c1Changed)));
private static void c1Changed(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
SUC s = obj as SUC;
if (s != null)
s.CChanged((c1)e.NewValue);
}
public void CChanged(c1 c)
{
C = c;
if(PropertyChanged!=null)
PropertyChanged(this,new PropertyChangedEventArgs("C"));
}
public SUC()
{
InitializeComponent();
this.DataContext = this;
}
private void bclick(object sender, RoutedEventArgs e)
{
C.S = C.S + " Clicked";
MessageBox.Show(C.I.ToString() + " - " + C.S);
}
public event PropertyChangedEventHandler PropertyChanged;
}
In my main page which also implements INotifiyPropertyChanged I have an instance of c1 and of SUC.
public partial class MainPage : UserControl, INotifyPropertyChanged
{
public c1 MC
{
get { return (c1)GetValue(MCProperty); }
set { SetValue(MCProperty, value); }
}
public static readonly DependencyProperty MCProperty =
DependencyProperty.Register("MC", typeof(c1), typeof(MainPage), new PropertyMetadata(new c1()));
private static void MCChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
MainPage mp = d as MainPage;
if (mp != null)
mp.MCChanged();
}
public void MCChanged()
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("MC"));
}
public MainPage()
{
InitializeComponent();
MC.S = "ssss";
this.DataContext = this;
}
public event PropertyChangedEventHandler PropertyChanged;
}
I want to set the C property of the SUC user control via XAML. Like so
local:SUC x:Name="suc" C="{Binding MC, Mode=TwoWay}"
This works well in the c# code behind but not in XAML. The reason I need it in XAML is because I want to bind a collection of c1’s to SUC’s in a DataTemplate.
Any working examples with downloadable code would be most appreciated.
It's a simple little bug in the constructor of the SUC class:
public SUC()
{
InitializeComponent();
this.DataContext = this; //this line shouldn't be here, delete and it will work
}
That means the DataContext of SUC control is itself instead of the MainPage class which is what it needs to be in order to bind to MainPage.MC (the SUC class doesn't have an MC property).
Also, and I realise most of these were you probably just trying to get it to work, but MC does not need to be a DP, you don't need the 'C=c;' line in the SUC, and I wouldn't use the MainPage control class as a datacontext class as well, create another class to bind the DataContext to.
The problem seems to be that you set the DataContext of the UserControl after you load the XAML. Either set it before the XAML is loaded (i.e. before InitializeComponent), or even better, set it in the XAML as such:
<local:MainPage ... DataContext="{Binding RelativeSource={RelativeSource Self}}">
....
</local:MainPage>
The RelativeSource binding specifies that the DataContext of your MainPage should be itself, which seems to be what you want. This then eliminates the assignment of DataContext in code-behind, which is always a good thing in WPF/Silverlight.
Hope that helps.
The DataContext of the UserControl's Controls can be different from the UserControl itself or the UserControl's Parent "Form" (or Parent Page, UserControl). You have to set the Binding in the Code Behind. See this post for more information: Silverlight UserControl Custom Property Binding
Also, You may want to create a Silverlight Control instead of a Silverlight UserControl

Binding to static property

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 }/>

Resources