Propagate property changes through multiple classes - c#

I'm trying to figure out how to properly pass properties through multiple classes. I know I can just implement INotifyPropertyChanged in each class and listen for changes on the property, but this seems to be quite a lot of unnecessary code.
The situation:
I have a class (let's call it Class1) with two dependency properties: FilterStatement (String) and Filter (Filter class). Setting the statement affects the filter and vice versa.
The conversion logic between statement and filter, however, isn't located in Class1, but in Class3 - which Class1 doesn't know directly. In between there is Class2 which just has to pass through the changes. (You can imagine class 1 to 3 beeing Viewmodel, Model and Repository, though in the real situation this doesn't completly match).
public class Class1
{
public static readonly DependencyProperty FilterProperty = DependencyProperty.Register(
"Filter",
typeof(Filter),
typeof(Class1),
new FrameworkPropertyMetadata(null));
public static readonly DependencyProperty FilterStatementProperty = DependencyProperty.Register(
"FilterStatement",
typeof(String),
typeof(Class1),
new FrameworkPropertyMetadata(null));
public Filter Filter
{
get { return (Filter)GetValue(FilterProperty); }
set { SetValue(FilterProperty, value); }
}
public string FilterStatement
{
get { return (string)GetValue(FilterStatementProperty); }
set { SetValue(FilterStatementProperty, value); }
}
public Class2 MyClass2Instance { get; set; }
}
public class Class2
{
public Class3 MyClass3Instance { get; set; }
public void ChangeClass3Instance(object someParam) {
... // this can change the instance of MyClass3Instance and is called frome somewhere else
// when changed, the new Class3 instance has to get the property values of Class1
}
}
public class Class3
{
private Filter _filter; // here is where the filter set in Class 1 or determined by the statement set in class 1 has to be put
public string MyFilterToStatementConversionMemberFunction(Filter filter)
{
...
}
public Filter MyStatementToFilterConversionMemberFunction(string statement)
{
...
}
}
My naive solution would be to duplicate the properties across all three classes, implement INotifyPropertyChanged in Class2 and Class3 and listen to the changes, propagating everything down to Class3 and in Result back up to Class1. Isn't there a better solution to this?

Actually, while Class1 is not a control (given it is a ViewModel) I don't see a reason to make its properties as DependencyProperty, because implementation of INotifyPropertyChanged should be enough.
However, implementation with DependencyProperty should work as well:
public class Class1
{
public static readonly DependencyProperty FilterProperty =
DependencyProperty.Register(
nameof(Filter),
typeof(Filter),
typeof(Class1),
new PropertyMetadata(OnPropertyChanged));
public static readonly DependencyProperty FilterStatementProperty =
DependencyProperty.Register(
nameof(FilterStatement),
typeof(string),
typeof(Class1),
new PropertyMetadata(OnPropertyChanged));
public Filter Filter
{
get { return (Filter)GetValue(FilterProperty); }
set { SetValue(FilterProperty, value); }
}
public string FilterStatement
{
get { return (string)GetValue(FilterStatementProperty); }
set { SetValue(FilterStatementProperty, value); }
}
public Class2 MyClass2Instance { get; set; }
private static void OnPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var c = (Class1)d;
if (c.MyClass2Instance?.MyClass3Instance != null)
{
if (e.Property == FilterProperty)
{
c.FilterStatement = c.MyClass2Instance.MyClass3Instance.MyFilterToStatementConversionMemberFunction((Filter)e.NewValue);
}
else if (e.Property == FilterStatementProperty)
{
c.Filter = c.MyClass2Instance.MyClass3Instance.MyStatementToFilterConversionMemberFunction((string)e.NewValue);
}
}
}
}
Please note, Filter.Equals should be implemented properly and conversion methods of the Class3 should return the equal values for equal arguments.

I think you complicate this too much. Observable pattern is better fit to user interface implementation. Why don't you code in Class3 logic which set all propertis in Class1 ? I thnik that overload setter FilterStatmentand Filter in Class1, which is ViewModel is good solution as well. As simpler solution as better.

Related

Catel ViewModelToModel not linking

I have a simple ViewModel which has two Models. So it looks like this:
public class ConnectionItemSelectorViewModel : ViewModelBase {
...
#region AvailableConnectionsModel
// Model Nr. 1
[Model]
public ConnectionList AvailableConnectionsModel
{
get { return GetValue<ConnectionList>(AvailableConnectionsModelProperty); }
set { SetValue(AvailableConnectionsModelProperty, value); }
}
public static readonly PropertyData AvailableConnectionsModelProperty = RegisterProperty(nameof(AvailableConnectionsModel), typeof(ConnectionList), () => new ConnectionList());
#endregion
#region SelectedConnectionsModel
// Model Nr. 2
[Model]
public ConnectionList SelectedConnectionsModel
{
get { return GetValue<ConnectionList>(SelectedConnectionsModelProperty); }
set { SetValue(SelectedConnectionsModelProperty, value); }
}
public static readonly PropertyData SelectedConnectionsModelProperty = RegisterProperty(nameof(SelectedConnectionsModel), typeof(ConnectionList), () => new ConnectionList());
#endregion
...
}
ConnectionList extends ModelBase so I can use the [Model]-Attribute several times.
Now I want to expose the properties of the Model to the ViewModel:
public class ConnectionItemSelectorViewModel : ViewModelBase {
...
// init Model properties
#region AvailableConnections
// Using a unique name for the property in the ViewModel
// but linking to the "correct" property in the Model by its name
[ViewModelToModel(nameof(AvailableConnectionsModel), nameof(ConnectionList.Connections))]
public ObservableCollection<ConnectionItem> AvailableConnections
{
get { return GetValue<ObservableCollection<ConnectionItem>>(AvailableConnectionsProperty); }
set { SetValue(AvailableConnectionsProperty, value); }
}
public static readonly PropertyData AvailableConnectionsProperty = RegisterProperty(nameof(AvailableConnections), typeof(ObservableCollection<ConnectionItem>), () => null);
#endregion
// linking other properties to the models
...
}
The problem is that the linking doesn't work. So after initialization the property AvailableConnections (and the others also) are still null, though the Model itself is initialized correctly.
Am I missing something or isn't this possible at all?
thx in advance!
Try setting the MappingType on the ViewModelToModel attribute so that the model wins.

Xaml Parse Exception - type mismatch between "Xamarin.Forms.Binding" and "System.Collections.Generic.IEnumerable`1[System.String]"

I am trying to set/bind a list property in a content view class in xaml.
This is my Class :
public class Myclass:ContentView
{
public static readonly BindableProperty TabItemSourceProperty = BindableProperty.Create(
"TabIndicatorItemSource",
typeof(IEnumerable<string>),
typeof(Myclass)
);
public IEnumerable<string> TabIndicatorItemSource {
get
{
return (IEnumerable<string>)GetValue(TabItemSourceProperty);
}
set
{
SetValue(TabItemSourceProperty, value);
}
}}
and This is my Xaml in ExampleClass.xaml.cs
local:Myclass x:Name="myCarouselView" TabTextSizeTabIndicatorItemSource ="{Binding tabListSource}"
And tabListSource is a List property in ExampleClass.cs.
However I get this type mismatch error. Where am I going wrong?
Bindable Properties need to adhere to a set naming convention. e.g. TabItemSourceProperty relates to TabItemSource
public class Myclass: ContentView
{
public static readonly BindableProperty TabItemSourceProperty = BindableProperty.Create(
"TabItemSource",
typeof(IEnumerable<string>),
typeof(Myclass)
);
public IEnumerable<string> TabItemSource {
get
{
return (IEnumerable<string>)GetValue(TabItemSourceProperty);
}
set
{
SetValue(TabItemSourceProperty, value);
}
}}
with
local:Myclass x:Name="myCarouselView" TabItemSource="{Binding tabListSource}"

CollectionPropertiesShouldBeReadOnly and Dependency Properties

I currently have a Dependency Property as such:
public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register("MyPropertyDefaults", typeof(ICollection<string>), typeof(MyPanel), new PropertyMetadata(new List<string>()));
public ICollection<string> MyProperty
{
get
{
return GetValue(MyPropertyProperty) as ICollection<string>;
}
set
{
this.SetValue(MyPropertyProperty, value);
}
}
The aim is that this subpanel will be passed a list via a binding, that the subpanel manipulates, and then the parent can read later. E.g.
<xaml:MyPanel MyProperty="{Binding MyPropertyList}" />
However, FxCop reports CollectionPropertiesShouldBeReadOnly and I need to remove the setter, which is required by the property. How do I fix this? What is the correct way to do what I am doing?
Declare the setter as private like this:
public class MyPanel : Panel
{
public static readonly DependencyProperty MyPropertyProperty = DependencyProperty.Register("MyPropertyDefaults", typeof(ICollection<string>), typeof(MyPanel), new PropertyMetadata(new List<string>()));
public ICollection<string> MyProperty
{
get
{
return GetValue(MyPropertyProperty) as ICollection<string>;
}
private set
{
this.SetValue(MyPropertyProperty, value);
}
}
}

Dependency attribute in c#

Here is my code
private MyClass _someProperty
[Dependency]
public MyClass SomeProperty
{
get{
if(_someProperty == null)
_someProperty = new MyClass()
return (MyClass)_someProperty
}
}
This works good, but I'm directed like "No need to add "get" property for [Dependency]"
I'm not clear with this [Dependency] attribute. When I looked into other code, it is written as
[Dependency]
public AnotherClass MyNewClass
{
get; set;
}
When I tried the above for MyClass, it didn't work. It threw the Property is NULL
Can anybody tell me what is the use of the [Dependency] and whether there is an alternate way for MyClass code and why it is coming as "NULL" for that?
This has nothing to do with the DependencyAttribute. The latter case is simply a auto-generated property. If you like to use this syntax you have to fill in the value within your constructor.
public class MyClass
{
public MyClass()
{
SomeProperty = new AnotherClass();
}
public AnotherClass SomeProperty { get; set; }
}
Be aware that you in this case you can't perform any checks within the getter or setter. So if you need any nullity checks or like to raise an event within the setter, you have to implement the getter and setter on yourself.
public class MyClass
{
private AnotherClass _SomeProperty;
public MyClass()
{
_SomeProperty = new AnotherClass();
}
public AnotherClass SomeProperty
{
get { return _SomeProperty; }
set
{
if(value == null)
throw new ArgumentNullException("SomeProperty");
if(value != _SomeProperty)
{
_SomeProperty = value;
// ToDo: Implement RaiseEvent() and declare event.
RaiseEvent(MyEvent);
}
}
}
}

WPF C# Custom UserControl How To add a Property which accepts an Binding

I'm trying to design a CustomUserControl which consits of an TextEditor and a PopUp...
So the Popup control should be binded to a list...
I called it BindingList. This Property should accept any types like ObservableCollection, List, Ienumerable for example(Collections)...
<my:CustomControl BindingList="{Binding Path=Collection}"
public IEnumerable<object> BindingList
{
get { return (IEnumerable<object>)GetValue(BindingListProp); }
set { SetValue(BindingListProp, value); }
}
The BindinglistProp
public static readonly DependencyProperty BindingListProp = DependencyProperty.Register(??????
I have no clue how it should look like that it can accept a binding.
And how should i deal with the Collection which is passed? when it is of a type which i don`t know
like
class Person
{
private string _Name;
private string _forename;
public string Name
{
get { return _Name; }
set
{
_Name = value;
}
}
public string Forename
{
get { return _forename; }
set
{
_forename = value;
}
}
}
Thanks for any hints, tutorials or code snippets.
sincerely
Mark
public IObservable<object> BindingList
{
get { return (IObservable<object>)base.GetValue(BindingListProperty); }
set { base.SetValue(BindingListProperty, value); }
}
public static DependencyProperty BindingListProperty =
DependencyProperty.Register(
"BindingList",
typeof(IObservable<object>),
typeof(CustomControl),
new PropertyMetadata(null));
Look to the CollectionViewSource.GetDefaultView to work with any collection in common way.

Categories

Resources