Skip to content

Commit

Permalink
Observable logics improved
Browse files Browse the repository at this point in the history
  • Loading branch information
CrackAndDie committed Feb 19, 2024
1 parent eedee08 commit 58e2192
Show file tree
Hide file tree
Showing 27 changed files with 312 additions and 87 deletions.
2 changes: 1 addition & 1 deletion Hypocrite.Avalonia/MVVM/ViewModelBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace Hypocrite.Mvvm
{
public class ViewModelBase : EngineViewModelBase, INavigationAware
public class ViewModelBase : CoreViewModelBase, INavigationAware
{
public ViewModelBase()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Prism.Events;

namespace Hypocrite.Core.Mvvm.Events
namespace Hypocrite.Core.Events
{
public class DialogClosedEvent : PubSubEvent
{
Expand Down
9 changes: 9 additions & 0 deletions Hypocrite.Core/Events/NavigationEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Hypocrite.Core.Mvvm;
using Prism.Events;

namespace Hypocrite.Core.Events
{
public class NavigationEvent : PubSubEvent<CoreViewModelBase>
{
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Prism.Events;

namespace Hypocrite.Core.Mvvm.Events
namespace Hypocrite.Core.Events
{
public class PreviewDoneEvent : PubSubEvent
{
Expand Down
37 changes: 37 additions & 0 deletions Hypocrite.Core/Events/PropertySetEventHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;

namespace Hypocrite.Core.Events
{
//
// Summary:
// Represents the method that will handle the BindableObject.PropertySet
// event raised when a property is set on a component.
//
// Parameters:
// sender:
// The source of the event.
//
// e:
// A Hypocrite.Core.Events.PropertySetEventArgs that contains the event data.
public delegate void PropertySetEventHandler(object sender, PropertySetEventArgs e);

/// <summary>
/// Provides data for the <see langword='PropertySet'/> event.
/// </summary>
public class PropertySetEventArgs : EventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref='Events.PropertySetEventArgs'/>
/// class.
/// </summary>
public PropertySetEventArgs(string propertyName)
{
PropertyName = propertyName;
}

/// <summary>
/// Indicates the name of the property that set.
/// </summary>
public virtual string PropertyName { get; }
}
}
37 changes: 37 additions & 0 deletions Hypocrite.Core/Events/PropertySettingEventHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;

namespace Hypocrite.Core.Events
{
//
// Summary:
// Represents the method that will handle the BindableObject.PropertySetting
// event raised when a property is setting on a component.
//
// Parameters:
// sender:
// The source of the event.
//
// e:
// A Hypocrite.Core.Events.PropertySettingEventArgs that contains the event data.
public delegate void PropertySettingEventHandler(object sender, PropertySettingEventArgs e);

/// <summary>
/// Provides data for the <see langword='PropertySetting'/> event.
/// </summary>
public class PropertySettingEventArgs : EventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref='Events.PropertySettingEventArgs'/>
/// class.
/// </summary>
public PropertySettingEventArgs(string propertyName)
{
PropertyName = propertyName;
}

/// <summary>
/// Indicates the name of the property that is setting.
/// </summary>
public virtual string PropertyName { get; }
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Prism.Events;

namespace Hypocrite.Core.Mvvm.Events
namespace Hypocrite.Core.Events
{
public class WindowProgressChangedEvent : PubSubEvent<bool>
{
Expand Down
66 changes: 48 additions & 18 deletions Hypocrite.Core/Extensions/BindableObjectExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
using Hypocrite.Core.Mvvm;
using Hypocrite.Core.Mvvm.ObserverLogics;
using Hypocrite.Core.Reactive;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
Expand All @@ -18,7 +16,7 @@ public static class BindableObjectExtensions
/// </summary>
/// <typeparam name="TObj">The type of the This.</typeparam>
/// <typeparam name="TRet">The type of the return value.</typeparam>
/// <param name="reactiveObject">The <see cref="BindableObject"/> raising the notification.</param>
/// <param name="bindableObject">The <see cref="BindableObject"/> raising the notification.</param>
/// <param name="backingField">A Reference to the backing field for this
/// property.</param>
/// <param name="newValue">The new value.</param>
Expand All @@ -33,21 +31,34 @@ public static class BindableObjectExtensions
where TObj : BindableObject
{
#if NET6_0_OR_GREATER
ArgumentNullException.ThrowIfNull(propertyName);
ArgumentNullException.ThrowIfNull(propertyName);
#else
if (propertyName is null)
{
throw new ArgumentNullException(nameof(propertyName));
}
#endif
bindableObject.SetProperty(ref backingField, newValue, propertyName);
return newValue;
}

if (EqualityComparer<TRet>.Default.Equals(backingField, newValue))
/// <summary>
/// WhenPropertySet returns an Observable representing the
/// property set notifications for a specific property on a
/// BindableObject.
/// </summary>
/// <param name="sender">The object where the property chain starts.</param>
/// <param name="property">The first property chain to reference. This will be a expression pointing to a end property or field.</param>
public static IObservable<TRet> WhenPropertySet<TSender, TRet>(
this TSender sender,
Expression<Func<TSender, TRet>> property, bool initialCall = false)
{
var mi = property.Body.GetMemberInfo();
if (mi.MemberType == MemberTypes.Property && mi is PropertyInfo pi && sender is BindableObject bindableObject)
{
return newValue;
return new PropertySetObservable<TRet>(bindableObject, pi, initialCall);
}

bindableObject.SetProperty(ref backingField, newValue, propertyName);
return newValue;
return null;
}

/// <summary>
Expand All @@ -59,12 +70,12 @@ public static class BindableObjectExtensions
/// <param name="property">The first property chain to reference. This will be a expression pointing to a end property or field.</param>
public static IObservable<TRet> WhenPropertyChanged<TSender, TRet>(
this TSender sender,
Expression<Func<TSender, TRet>> property)
Expression<Func<TSender, TRet>> property, bool initialCall = false)
{
var mi = property.Body.GetMemberInfo();
if (mi.MemberType == MemberTypes.Property && mi is PropertyInfo pi && sender is BindableObject bindableObject)
{
return new PropertyChangedObservable<TRet>(bindableObject, pi);
return new PropertyChangedObservable<TRet>(bindableObject, pi, initialCall);
}
return null;
}
Expand All @@ -76,11 +87,30 @@ public static class BindableObjectExtensions
/// </summary>
/// <param name="sender">The object where the property chain starts.</param>
public static IObservable<TRet> WhenAnyPropertyChanged<TSender, TRet>(
this TSender sender)
this TSender sender, bool initialCall = false)
{
if (sender is BindableObject bindableObject)
{
return new PropertyChangedObservable<TRet>(bindableObject, null);
return new PropertyChangedObservable<TRet>(bindableObject, null, initialCall);
}
return null;
}

/// <summary>
/// WhenPropertySet returns an Observable representing the
/// property is setting notifications for a specific property on a
/// BindableObject.
/// </summary>
/// <param name="sender">The object where the property chain starts.</param>
/// <param name="property">The first property chain to reference. This will be a expression pointing to a end property or field.</param>
public static IObservable<TRet> WhenPropertySetting<TSender, TRet>(
this TSender sender,
Expression<Func<TSender, TRet>> property, bool initialCall = false)
{
var mi = property.Body.GetMemberInfo();
if (mi.MemberType == MemberTypes.Property && mi is PropertyInfo pi && sender is BindableObject bindableObject)
{
return new PropertySetObservable<TRet>(bindableObject, pi, initialCall, false);
}
return null;
}
Expand All @@ -94,12 +124,12 @@ public static class BindableObjectExtensions
/// <param name="property">The first property chain to reference. This will be a expression pointing to a end property or field.</param>
public static IObservable<TRet> WhenPropertyChanging<TSender, TRet>(
this TSender sender,
Expression<Func<TSender, TRet>> property)
Expression<Func<TSender, TRet>> property, bool initialCall = false)
{
var mi = property.Body.GetMemberInfo();
if (mi.MemberType == MemberTypes.Property && mi is PropertyInfo pi && sender is BindableObject bindableObject)
{
return new PropertyChangedObservable<TRet>(bindableObject, pi, false);
return new PropertyChangedObservable<TRet>(bindableObject, pi, initialCall, false);
}
return null;
}
Expand All @@ -111,11 +141,11 @@ public static class BindableObjectExtensions
/// </summary>
/// <param name="sender">The object where the property chain starts.</param>
public static IObservable<TRet> WhenAnyPropertyChanging<TSender, TRet>(
this TSender sender)
this TSender sender, bool initialCall = false)
{
if (sender is BindableObject bindableObject)
{
return new PropertyChangedObservable<TRet>(bindableObject, null, false);
return new PropertyChangedObservable<TRet>(bindableObject, null, initialCall, false);
}
return null;
}
Expand Down
2 changes: 1 addition & 1 deletion Hypocrite.Core/Extensions/ObservableExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Hypocrite.Core.Mvvm.ObserverLogics;
using Hypocrite.Core.Reactive;
using Hypocrite.Core.Utils;
using System;
using System.Collections.Generic;
Expand Down
64 changes: 56 additions & 8 deletions Hypocrite.Core/Mvvm/BindableObject.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using Hypocrite.Core.Events;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
Expand All @@ -15,10 +16,12 @@ namespace Hypocrite.Core.Mvvm
public abstract class BindableObject : INotifyPropertyChanged
{
/// <summary>
/// Occurs when a property value changes.
/// Occurs when a property value changes/set.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
public event PropertyChangingEventHandler PropertyChanging;
public event PropertySetEventHandler PropertySet;
public event PropertySettingEventHandler PropertySetting;

// public ObservableCollection<IObservable> PropertyChangedObservables { get; set; } = new ObservableCollection<System.IObservable>();

Expand Down Expand Up @@ -54,14 +57,19 @@ public virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] st
/// desired value.</returns>
public virtual bool SetPropertyWithCallback<T>(ref T storage, T value, Action onChanged, [CallerMemberName] string propertyName = null)
{
if (EqualityComparer<T>.Default.Equals(storage, value)) return false;
bool isDifferent = !EqualityComparer<T>.Default.Equals(storage, value);

RaisePropertyChanging(propertyName);
storage = value;
onChanged?.Invoke();
RaisePropertyChanged(propertyName);
RaisePropertySetting(propertyName);
if (isDifferent)
{
RaisePropertyChanging(propertyName);
storage = value;
onChanged?.Invoke();
RaisePropertyChanged(propertyName);
}
RaisePropertySet(propertyName);

return true;
return isDifferent;
}

/// <summary>
Expand All @@ -86,6 +94,28 @@ protected void RaisePropertyChanged([CallerMemberName] string propertyName = nul
OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}

/// <summary>
/// Raises this object's PropertySetting event.
/// </summary>
/// <param name="propertyName">Name of the property used to notify listeners. This
/// value is optional and can be provided automatically when invoked from compilers
/// that support <see cref="CallerMemberNameAttribute"/>.</param>
protected void RaisePropertySetting([CallerMemberName] string propertyName = null)
{
OnPropertySetting(new PropertySettingEventArgs(propertyName));
}

/// <summary>
/// Raises this object's PropertySet event.
/// </summary>
/// <param name="propertyName">Name of the property used to notify listeners. This
/// value is optional and can be provided automatically when invoked from compilers
/// that support <see cref="CallerMemberNameAttribute"/>.</param>
protected void RaisePropertySet([CallerMemberName] string propertyName = null)
{
OnPropertySet(new PropertySetEventArgs(propertyName));
}

/// <summary>
/// Raises this object's PropertyChanging event.
/// </summary>
Expand All @@ -103,5 +133,23 @@ protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
{
PropertyChanged?.Invoke(this, args);
}

/// <summary>
/// Raises this object's PropertySetting event.
/// </summary>
/// <param name="args">The PropertySettingEventArgs</param>
protected virtual void OnPropertySetting(PropertySettingEventArgs args)
{
PropertySetting?.Invoke(this, args);
}

/// <summary>
/// Raises this object's PropertySet event.
/// </summary>
/// <param name="args">The PropertySetEventArgs</param>
protected virtual void OnPropertySet(PropertySetEventArgs args)
{
PropertySet?.Invoke(this, args);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
using Hypocrite.Core.Interfaces;
using Hypocrite.Core.Mvvm.Events;
using Hypocrite.Core.Events;
using Hypocrite.Core.Interfaces;
using Hypocrite.Core.Logging.Interfaces;
using Prism.Events;
using Prism.Ioc;
using Prism.Mvvm;

namespace Hypocrite.Core.Mvvm
{
public class EngineViewModelBase : BindableObject, IViewModel
public class CoreViewModelBase : BindableObject, IViewModel
{
public EngineViewModelBase()
public CoreViewModelBase()
{
viewModelName = GetType().FullName;
}
Expand Down
8 changes: 0 additions & 8 deletions Hypocrite.Core/Mvvm/Events/NavigationEvent.cs

This file was deleted.

Loading

0 comments on commit 58e2192

Please sign in to comment.