How I can bind property, which use another static property - c#

I use mvvm pattern in my progeсt (C#), and I have some problem.
I have a label on my view, and label's text is binded to property from my viewModel:
val label=new Label();
label.SetBinding<StatusViewModel>(Label.TextProperty, x=>x.TextProp);
this is my view model, which implements INotifyPropertyChanged interface:
class StatusViewModel
{
private string _textProp;
public string TextProp
{
get
{
return _textProp;
}
set
{
if(_textProp == value)
return _textProp;
_textProp=value;
OnPropertyChange();
}
}
}
but I have another static property:
static class StaticClass
{
public static string StaticText {get; set; }
}
And I want use this static property StaticText inside my TextProp property from StatusViewModel. And StaticText property mast notify label about it changes.
P.S. sorry about possible mistakes, I typed this code from my head.

If you are binding to static properties, you are probably doing it wrong :)
That said, the initial bind is super easy. You just need to add a property that returns the static one:
public string StaticTextRedirect
{
get { return StaticClass.StaticText; }
set { StaticClass.StaticText = value; }
}
The PropertyChanged event is another beast. You could raise it from the StaticTextRedirect property of course, but that won't fire if some other class changes the property. You'll probably need to just raise a custom event in the static property's setter that client code can listen to and raise the appropriate PropertyChanged event for.

Related

How to add a property to a control's DataBindings

I want to write a control derived from CheckedListBoxControl (DevExpress), and I need to add a property to the (DataBindings)
These are the standard Properties shown in the PropertyGrid:
So I can only choose between Tag and Text.
What I want is to add a third option called gttMasterField (which will be of type int, don't know if this matters)
I have been experimenting with the documentation but with no results.
These don't seem to cover exact what I am looking for, I don't know the correct search terms to find this, which makes it difficult to google for it. It will probably be somewhere in the documentation but also there I don't know on what terms to look for.
Create a Windows Forms user control that supports simple data binding
Create a Windows Forms user control that supports lookup data binding
Create a Windows Forms user control that supports complex data binding
Here is some code with comments that will also help to explain what I am searching for
public partial class gttDXManyToManyCheckedListBox : CheckedListBoxControl
{
private int _gttMasterField;
// This I want populated by setting the binding property MasterField
public int gttMasterField
{
get { return _gttMasterField; }
set { _gttMasterField = value; }
}
}
The project is a dotnet framework 4.7.2
To make a custom Property appear in the PropertyGrid's (DataBindings), decorate the Property with the BindableAttribute set to true:
[Bindable(true)]
public int gttMasterField
{
get { return _gttMasterField; }
set { _gttMasterField = value; }
}
Optionally also decorate with the desired DesignerSerializationVisibilityAttribute attribute
[Bindable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public int gttMasterField
{
get { return _gttMasterField; }
set { _gttMasterField = value; }
}
The class can also specify the default bindable Property, settings a DefaultBindingPropertyAttribute:
[DefaultBindingProperty("gttMasterField")]
public partial class gttDXManyToManyCheckedListBox : CheckedListBoxControl
{
private int _gttMasterField;
[Bindable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public int gttMasterField
{
get { return _gttMasterField; }
set { _gttMasterField = value; }
}
}
Important note:
the class should implement INotifyPropertyChanged -> a bindable Property is supposed to raise notification events. When the binding is set in the Designer, a BindingSource is generated to mediate the binding, but it requires that the objects involved send change notifications (mostrly to determine when the Property value is updated, usually as DataSourceUpdateMode.OnPropertyChanged).
For example:
using System.Runtime.CompilerServices;
[DefaultBindingProperty("gttMasterField")]
public partial class gttDXManyToManyCheckedListBox : CheckedListBoxControl, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private int _gttMasterField;
[Bindable(true)]
public int gttMasterField
{
get { return _gttMasterField; }
set { _gttMasterField = value; NotifyPropertyChanged(); }
}
private void NotifyPropertyChanged([CallerMemberName] string PropertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
}
Setting DesignerSerializationVisibility.Content implies initialization. If a BindingSource is used, this object supports initialization on itself, the Attribute is not strictly required.
It could be set to DesignerSerializationVisibility.Hidden, though, depending on the use case.

WPF: Binding is lost when bindings are updated

I have a WPF-Application for controlling a WCF-RESTful service, i.e. for starting, initializing and stopping it. Therefore I have a MainWindow UI which contains a UserControl to configure settings. When I initialize my service, some data is loaded into DependencyProperties and ObservableCollections to display it in the GUI. Here is the part of the method where I update these settings:
public partial class MainWindow : Window {
private void InitializeService (bool reInitialize = false) {
var restService = (RestService)this.ServiceHost.SingletonInstance;
var settings = restService.GetSettings();
//UCSettings is the "x:name" of the embedded UserControl "UserControlSettings" in this window
this.UCSettings.ExecutionTimes.Clear();
settings.ExecutionTimes.ForEach(x => this.UCSettings.ExecutionTimes.Add(x));
this.UCSettings.TableConfigurationLoader = settings.Timer.Find(x => x.Name == "TableConfigLoader");
}
}
public partial class UserControlSettings : UserControl {
public ObservableCollection<ExecutionTime> ExecutionTimes { get; set; }
public static readonly DependencyProperty TableConfigurationLoaderProperty = DependencyProperty.Register("TableConfigurationLoader", typeof(Setting), typeof(UserControlSettings), new FrameworkPropertyMetadata(default(Setting)));
public Setting TableConfigurationLoader {
get { return (Setting)this.GetValue(TableConfigurationLoaderProperty); }
set { this.SetValue(TableConfigurationLoaderProperty, value); }
}
}
public class Setting {
public string Name { get; set; }
public bool IsEnabled { get; set; }
public int ExecutionTimeId { get; set; }
}
public class ExecutionTime {
public int Id { get; set; }
public string Value { get; set; }
}
In the Code-Designer (UserControlSettings.xaml.cs) these properties are used in some bindings for a ComboBox:
<UserControl x:Class="InsightTool.Gui.UserControlSettings" x:Name="UCSettings">
<ComboBox x:Name="CbConfigLoadingExecutionTime" ItemsSource="{Binding ElementName=UCSettings, Path=ExecutionTimes}" DisplayMemberPath="Value" SelectedValue="{Binding ElementName=UCSettings, Path=TableConfigurationLoader.ExecutionTimeId}" SelectedValuePath="Id"/>
</UserControl>
When I first load in the data with the InitializeService method, everything works fine. The ComboBox is filled with the data of the ObservableCollection and the matching value is selected automatically by the ExecutionTimeId.
When I try to "reinitialize" the service, I call the same method again, but the SelectedValue binding does not work anymore. I checked the values of these properties in the debugger, but they are set correctly in this method again. What am I doing wrong here? Some samples:
Correct display first load:
Incorrect display seconds load:
TableConfigurationLoader is a dependency property. That means a lot of things, but one of them is that when you change the value of TableConfigurationLoader to a different instance of Setting, an event is raised, and this Binding handles that event and updates SelectedValue on the combo box:
SelectedValue="{Binding ElementName=UCSettings, Path=TableConfigurationLoader.ExecutionTimeId}"
However, Setting.ExecutionTimeId isn't a dependency property. It's a regular .NET CLR property, which doesn't notify anybody of anything when its value changes. So if you change the ExecutionTimeId property of the same old Setting that's already in TableConfigurationLoader, nobody knows and nothing happens.
Since Setting is not a control class, you don't particularly need or want its properties to be dependency properties. Instead, you can treat it as a viewmodel. In implementation terms, all a viewmodel really is, is any class that implements INotifyPropertyChanged. With changes to Setting shown below, I think the binding should work as you expect, if I correctly understand your problem. I've changed IsEnabled so it will raise PropertyChanged as well; you may not actually need that, but it's illustrative.
You may need to do the same with your ExecutionTime class.
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] String propName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
public class Setting : ViewModelBase
{
public string Name { get; set; }
#region IsEnabled Property
private bool _isEnabled = false;
public bool IsEnabled
{
get { return _isEnabled; }
set
{
if (value != _isEnabled)
{
_isEnabled = value;
OnPropertyChanged();
}
}
}
#endregion IsEnabled Property
#region ExecutionTimeId
private int _executionTimeId = 0;
public int ExecutionTimeId
{
get { return _executionTimeId; }
set
{
if (value != _executionTimeId)
{
_executionTimeId = value;
OnPropertyChanged();
}
}
}
#endregion ExecutionTimeId
}
There are three (ish) mechanisms in WPF for notifying things that properties have changed, and you need to be using one or another somehow if you want things to update correctly:
Dependency properties of dependency objects: For properties of controls
INotifyPropertyChanged: For properties of viewmodels
INotifyCollectionChanged: For collections.
A collection property should also raise INotifyPropertyChanged.PropertyChanged when you assign a new collection instance to it. A given instance of the collection will handle raising its own events when its contents change.
ObservableCollection<T> and ReadOnlyObservableCollection<T> implement INotifyCollectionChanged so you don't have to; it's a big hassle to implement that one properly so you really don't want to go there.
Creating a new instance of Setting before referring to the actual object solved my problem. It seems that the reference to the specific property of Setting is lost, if I just "override" the existing instance of this property:
var settings = restService.GetSettings();
this.UCSettings.ExecutionTimes.Clear();
settings.ExecutionTimes.ForEach(x => this.UCSettings.ExecutionTimes.Add(x));
this.UCSettings.TableConfigurationLoader = new Setting();
this.UCSettings.TableConfigurationLoader = settings.Timer.Find(x => x.Name == "TableConfigLoader");
Try adding UpdateSourceTrigger=PropertyChanged to the binding like this
<UserControl x:Class="InsightTool.Gui.UserControlSettings" x:Name="UCSettings">
<ComboBox x:Name="CbConfigLoadingExecutionTime" ItemsSource="{Binding ElementName=UCSettings, Path=ExecutionTimes}" DisplayMemberPath="Value" SelectedValue="{Binding ElementName=UCSettings, UpdateSourceTrigger=PropertyChanged,Path=TableConfigurationLoader.ExecutionTimeId}" SelectedValuePath="Id"/>

When to use the public or private property?

If I have a class like so:
public class MyClass:INotifyPropertyChanged
{
private Visibility isVisible;
private ObservableCollection<string> names;
public Visibility IsVisible
{
get{ return isVisible;}
set { isVisible = value; OnPropertyChanged("IsVisible");}
}
public ObservableCollection<string> Names
{
get { return names;}
set { names = value; OnPropertyChanged("Names");}
}
//ctor
public MyClass(){
names = new ObservableCollection<string>();
}
//INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Before any one beheads me - I have done quite a bit of looking up and have found a mixed bag of answers...
Do I modify the public or private properties/variables for use in my bindings? i.e. I have an issue where adding to names collection will trigger OnPropertyChanged and changing isVisible will NOT trigger OnPropertyChanged. My assumption is that this is because names is an ObservableCollection where as isVisible is not but I am not sure...
If I am supposed to uses the public properties - what is the need for having the private ones?
You don't need a private property, only a private field would be enough so replace:
private Visibility isVisible {get; set;}
with
private Visibility isVisible;
If I am supposed to uses the public properties - what is the need for
having the private ones?
You cannot use Auto-properties with INotifyPropertyChanged. That is why you need a backing field for your property IsVisible.
See: An elegant way to implement INotifyPropertyChanged
So I think you are confusing Properties and Fields (aka variables).
public class Example()
{
public int FieldExample;
private int _propertyExample;
public int PropertyExample
{
get
{
return _propertyExample;
}
set
{
_propertyExample = value;
}
}
}
In simple usage scenarios, the difference between a field and a property isn't obvious. But properties have different plumbing under the hood that allows them to take advantage of reflection and binding. For WPF, this means you've got to have public properties. Best practice for a Public Property is associate it with a private (or protected) field - and that field name is usually either prefixed with an _ and/or starts with lower case character. This is called a "backing field."
The private backing field holds the actual data, the public property is just the means by which other classes can interact with that data. Inside the get and set blocks, you can place any code you want: instead of returning my backing field, I could instead put: return 5;. It's not useful, and it's poor practice, but I can. Generally, the code that resides in your get and set blocks should still set or get the value; although you might validate the input first, and/or format it first. The pattern you are implementing in your sets for WPF raises an event that the property has changed. Other parts of your program are listening for that event so they know to update the UI.
So in your code, if you only change the backing field and don't raise an event that there has been a change, the UI will not update. You might desire this behavior if you are performing a complex action on an object, and want to hold off performing an UI update until a complete batch of items are finished, but that's an optimization and for starters you are probably better off always accessing/setting to the Public Property.

MVVM and INotifyPropertyChanged Issue

I have a big problem with MVVM design. I am trying to catch every PropertyChanged of my inner nested objects, including futhermore propertchanged of their nested objects, inside my ViewModel but I dont know how to do it.
Here is my structure:
class MyVM
{
public MyVM()
{
this.SomeData = new SomeData();
this.SomeData.NestedObj = new MyNestedDat();
this.SomeData.Str = "This tiggers propertychanged inside MyDat class";
// this triggers propertychanged event inside MyNestedDat class
this.SomeData.NestedObj.Num = 123;
}
// and here should be a method where i catch all possibe propertychanges from my nested objets and their nested objets, how do i do that?
public MyDat SomeData
{
get;
set;
}
}
class MyDat : INotifyPropertyChanged
{
private string str;
public string Str;
{
get { return this.str;}
set
{
this.str = value;
this.PropertyChanged(this, "Str");
}
}
publicMyNestedDat NestedObj
{
get;
set;
}
}
class MyNestedDat : INotifyPropertyChanged
{
private int num;
public int Num
{
get{ return this.num;}
set
{
this.num = value;
this.PropertyChanged(this, "Num");
}
}
}
How do i get this to work? I am really clueless where to start.
MyNestedDat class throws PropertyChanged, MyDat class throws propertychanged and i want to catch them all inside my viewmodel. How can i do that?
In my opinion there are a few conceptual things wrong with what you are asking. Just imagine you get a solution that works for your scenario (that you are happy with) and consider the following:
What happens if another layer is added? do you still expect it to work the same?
Should property changes be propagated (viewModel1.propA notifies viewModel2.PropA)?
Should property changes be transformed (viewModel1.SomeProp notifies ViewModel2.AnotherProp)?
Is performance a concern? how will this perform if you need to propagate the property changed events through many levels?
This should be raising alarm bells that the current approach is not the right path to tread.
What you need is a way to provide communication between your viewModels in a loosely coupled way so that you viewModels do not even need to know about each others existence. The beauty of this is that this will also work in other situations not just for property changes.
For your case of property changed events, one viewModel wants to know when something happens (it could be something other than a property changed event, remember). This means the other viewModel needs some way of saying "Hey, a property has changed" (or "My state has changed", "That database call has finished" etc).
Now in C# you can provide events which provide this feature....except, now your objects know about each other which leaves you with the same problem you had before.
To overcome this problem you need another object, a mediator (lets call it Messenger in this example), whose sole purpose is to handle the message passing between the objects so that they can live in ignorance of each other.
The general idea is this. In the viewModel that provides notifications you might do something like this:
public string MyProp
{
get { return _myProp; }
set
{
_mProp = value;
OnPropertyChanged("MyProp");
Messenger.PostMessage(new VMChangedMessage { ViewModel = this, PropertyName = "MyProp" });
}
}
And in the viewModel that is interested in the event you might do something like this:
public class ViewModel2
{
public ViewModel2()
{
Messenger.Subscribe<VMChangedMessage>(handleMessage);
}
private void handleMessage(VMChangedMessage msg)
{
// Do something with the information here...
}
}
Notice that the two viewModels never reference each other. They are now loosely-coupled.
There are a number of pre-existing implementations already available and it isn't difficult to create your own (the messenger basically keeps a list of objects that are interested in a certain message and iterates the list when it needs to notify the interested parties). There are a few things that can be implemented differently (some implementations just pass string messages around rather than encapsulating the information in objects, and some handle the clean-up of observers automatically).
I would recommend using Josh Smiths (excellent) MVVM Foundation which includes a messenger class. It's also open source so you can see how it works.
There is no clear constraint about what PropertyName should contains in PropertyChangedEventArgs.
See Subscribe to INotifyPropertyChanged for nested (child) objects.
Here is an example :
class A : BaseObjectImplementingINotifyPropertyChanged {
private string m_name;
public string Name {
get { return m_name; }
set {
if(m_name != value) {
m_name = value;
RaisePropertyChanged("Name");
}
}
}
}
class B : BaseObjectImplementingINotifyPropertyChanged {
private A m_a;
public A A {
get { return m_a; }
set {
if(m_a != value) {
if(m_a != null) m_a.PropertyChanged -= OnAPropertyChanged;
m_a = value;
if(m_a != null) m_a.PropertyChanged += OnAPropertyChanged;
RaisePropertyChanged("A");
}
}
}
private void OnAPropertyChanged(object sender, PropertyChangedEventArgs e) {
RaisePropertyChanged("A." + e.PropertyName);
}
}
B b = new B();
b.PropertyChanged += (s, e) => { Console.WriteLine(e.PropertyName); };
b.A.Name = "Blah"; // Will print "A.Name"
The best thing to do here is to separate the idea of a Model and a ViewModel.
By having a ViewModel object that is flatter than the Model you can avoid this scenario. Using an automatic mapping tool like Automapper then allows you to map the Model to the ViewModel and vice versa.
https://github.com/AutoMapper/AutoMapper/wiki/Flattening
class MyDatViewModel : INotifyPropertyChanged
{
public string Str
{
// ... Get Set
}
public int NestedObjNum
{
// ... Get set
}
}
// Configure AutoMapper
Mapper.CreateMap<MyDat, MyDatViewModel>();
// Perform mapping
MyDatViewModel viewModel = Mapper.Map<MyDat, MyDatViewModel>(someData);

How to have bindable properties of a UserControl which work with OnPropertyChanged

I have a simple usercontrol (WinForms) with some public properties. When I use this control, I want to databind to those properties with the DataSourceUpdateMode set to OnPropertyChanged. The datasource is a class which implements INotifyPropertyChanged.
I'm aware of the need to create bindings against the properties and I'm doing that.
I assumed that my usercontrol would have to implement an interface, or the properties would need to be decorated with some attribute, or something along those lines.But my research has come up blank.
How should this be accomplished? At the moment I'm doing it by calling OnValidating() in my usercontrol whenever a property changes, but that doesn't seem right.
I can get validation to happen if I set the CausesValidation to true on the usercontrol, but that's not very useful to me. I need to validate each child property as it changes.
Note this is a WinForms situation.
EDIT: Evidently I have no talent for explanation so hopefully this will clarify what I'm doing. This is an abbreviated example:
// I have a user control
public class MyControl : UserControl
{
// I'm binding to this property
public string ControlProperty { get; set; }
public void DoSomething()
{
// when the property value changes, the change should immediately be applied
// to the bound datasource
ControlProperty = "new value";
// This is how I make it work, but it seems wrong
OnValidating();
}
}
// the class being bound to the usercontrol
public class MyDataSource : INotifyPropertyChanged
{
private string sourceProperty;
public string SourceProperty
{
get { return sourceProperty; }
set
{
if (value != sourceProperty)
{
sourceProperty = value;
NotifyPropertyChanged("SourceProperty");
}
}
}
// boilerplate stuff
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
public class MyForm : Form
{
private MyControl myControl;
public MyForm()
{
// create the datasource
var dataSource = new MyDataSource() { SourceProperty = "test" };
// bind a property of the datasource to a property of the usercontrol
myControl.DataBindings.Add("ControlProperty", dataSource, "SourceProperty",
false, DataSourceUpdateMode.OnPropertyChanged); // note the update mode
}
}
(I have tried this using a BindingSource, but the result was the same.)
Now what I want to happen is that when the value of MyControl.ControlProperty changes, the change is immediately propagated to the datasource (the MyDataSource instance). To achieve this I call OnValidating() in the usercontrol after changing the property. If I don't do that, I have to wait until validation gets triggered by a focus change, which is the equivalent of the "OnValidation" update mode, rather than the desired "OnPropertyUpdate" validation mode. I just don't feel like calling OnValidating() after altering a property value is the right thing to do, even if it (kind of) works.
Am I right in assuming the calling OnValidating() is not the right way to do this? If so, how do I notify the datasource of the ControlProperty change?
I think I've got this figured out. I didn't understand how change notifications were sent from control to bound datasource.
Yes, calling OnValidating() is the wrong way.
From what I've pieced together, there are two ways a control can notify the datasource that a property has changed.
One way is for the control to implement INotifyPropertyChanged. I had never done this from the control side before, and I thought only the datasource side of the binding had to implement it.
When I implemented INotifyPropertyChanged on my user control, and raised the PropertyChanged event at the appropriate time, it worked.
The second way is for the control to raise a specific change event for each property. The event must follow the naming convention: <propertyname>Changed
e.g. for my example it would be
public event EventHandler ControlPropertyChanged
If my property was called Foo, it would be FooChanged.
I failed to notice the relavent part of the MSDN documentation, where it says:
For change notification to occur in a
binding between a bound client and a
data source, your bound type should
either:
Implement the INotifyPropertyChanged
interface (preferred).
Provide a change event for each
property of the bound type.
This second way is how all existing WinForms controls work, so this is how I'm doing it now. I use INotifyPropertyChanged on my datasource, but I raise the Changed events on my control. This seems to be the conventional way.
Implementing the INotifyPropertyChanged interface is very simple. Here is a sample that shows an object with a single public field...
public class Demo : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
private string _demoField;
public string DemoField
{
get {return demoField; }
set
{
if (value != demoField)
{
demoField = value;
NotifyPropertyChanged("DemoField");
}
}
}
}
Then you would create a Binding instance to bind a control property to a property (DemoField) on your source instance (instance of Demo).

Categories

Resources