Is it possible to use "PropertyChanged" on a bool in .NET MAUI? - inotifypropertychanged

I have been trying to detect it when these variables change, but I don't know how to do that since bools aren't supported by the "PropertyChanged" function.
I also tried using the communityToolKit, but I have no idea how to use that.
I want it to call the function "IconUpdater"
public class Status : INotifyPropertyChanged
{
public static bool isWorking { get; set; } = Preferences.Get("IsWorking", true);
public static bool isPaused { get; set; } = Preferences.Get("IsPaused", false);
public static void IconUpdater()
{
// The function I want to call \\
}
public event PropertyChangedEventHandler PropertyChanged;
}

You can use PropertyChanged event to notify the changes of IsEnabled property in your viewmodel.
Here's the code snippet below for your reference:
public class MainPageViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _isWorking;
public bool IsEnabled
{
get
{
return _isWorking;
}
set
{
if(_isWorking != value)
{
_isWorking = value;
var args = new PropertyChangedEventArgs(nameof(IsEnabled));
PropertyChanged?.Invoke(this, args);
}
}
}
}

I recommend using the Community Toolkit MVVM package: https://learn.microsoft.com/en-us/dotnet/communitytoolkit/mvvm/
You can then simply do the following to use the INotifyPropertyChanged interface:
using CommunityToolkit.Mvvm;
public class MyViewModel : ObservableObject
{
private bool _myBool;
public bool MyBool
{
get => _myBool;
set => SetProperty(ref _myBool, value);
}
}
You can also modify the code in such a way that you directly call any other method from within the setter:
private bool _myBool;
public bool MyBool
{
get => _myBool;
set
{
SetProperty(ref _myBool, value);
IconUpdater();
}
}
Please mind that your class is using static properties. You cannot use INotifyPropertyChanged for that.

Related

Blazor: How can i check if the Param for the Callback function does exsist?

Example:
/Parent
<SomeTemplate event="#ThisEvent"/>
#code{
private async Task ThisEvent()
{
}
/Child
#Code {
[Parameter]
public EventCallback event{ get; set; }
}
Is there a way to "check" if "event" has been passed to the Child as parameter ?
I want to use this in the Child to decide "what" this implementation of this Template should show and what not
The only Solution i have is to pass additional bool values to do so, im looking for a away to avoid that
Here's how to check if a delegate has been assigned to the callback. I've set up the check to throw a Debug break if nothing has been assigned.
Also note the EditorRequired setting on the parameter which will produce a warning in the editor if that's what you want.
#using System.Diagnostics;
<h3>CallbackComponent</h3>
#code {
[Parameter, EditorRequired] public EventCallback<int> MyEventCallback { get; set; }
protected override void OnInitialized()
{
Debug.Assert(this.MyEventCallback.HasDelegate);
}
private async Task OnClick(MouseEventArgs e)
{
await this.MyEventCallback.InvokeAsync(1);
}
}
Checking for whether a delegate has been assigned is what you are after:
public EventCallback MyEventCallback { get; set; }
public async Task MyHandler()
{
if (MyEventCallback.HasDelegate)
{
await MyEventCallback.InvokeAsync();
}
}

Action filter does not override controller action?

I have implemented an IAsyncAuthorizationFilter/IActionFilter filter and implemented TypeFilterAttribute for the filter. When I add the attribute to both the controller and action, the action filter does not appear to override the controller level filter.
public class MyAuthorizeAttribute : TypeFilterAttribute
{
public MyAuthorizeAttribute (bool redirectOnFailure = true)
: base(typeof(MyFilter))
{
Arguments = new object[]
{
redirectOnFailure
};
}
}
public class MyFilter: IAsyncAuthorizationFilter, IActionFilter
{
public bool RedirectOnFailure { get; set; }
public MyFilter(bool redirectOnFailure)
{
RedirectOnFailure = redirectOnFailure;
}
public void OnActionExecuting(ActionExecutingContext context)
{
if (context.Controller is Controller controller)
{
// Do some work
if (true)
{
if (!RedirectOnFailure)
{
context.Result = new JsonResult("Your session has expired.");
}
else
{
context.Result = new RedirectResult("LoginUrl");
}
return;
}
}
}
public void OnActionExecuted(ActionExecutedContext context)
{
// Do nothing
}
public virtual async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
// Do work
}
}
The redirectOnFailure will be true for the Index action even though the filter specified false. In ASP.NET MVC, the action filter would override the controller filter. You could have a default for all actions but override specific actions with different properties/parameters. Can you not do this in Core?
[MyAuthorize]
public class HomeController : Controller
{
[MyAuthorize(redirectOnFailure: false)]
public IActionResult Index()
{
// Do work
}
}
As per the Microsoft website, filters do not override each other. They simply run one after the other in the order described in the cited document.
Just because the same attribute is put in both the controller and the action doesn't mean that ASP.net will say "ah, you probably want to override the class-level attribute". That's just not how it works.
If you want override logic, you need to write override logic.
Here's a sample made for .Net 6. The magic is done by the FindEffectivePolicy() method. This sample shows how to compare the current object against the effective one and only run the logic if the comparison matches.
public class MyFilter : IAsyncAuthorizationFilter
{
#region Properties
public string Name { get; }
#endregion
#region Constructors
public MyFilter(string name)
{
Name = name;
}
#endregion
#region IAsyncAuthorizationFilter
public Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
var effectiveAtt = context.FindEffectivePolicy<MyFilter>();
System.Diagnostics.Debug.Print($"Effective filter's name: {effectiveAtt?.Name}");
System.Diagnostics.Debug.Print($"Am I the effective attribute? {this == effectiveAtt}");
if (this == effectiveAtt)
{
// Do stuff since this is the effective attribute (policy).
}
else
{
// ELSE part probably not needed. We just want the IF to make sure the code runs only once.
}
return Task.CompletedTask;
}
#endregion
}

Property change is not propagated to binding target (UWP)

I have a ViewModel and a Class. They look like this:
//viewmodel
public class MyViewModel : ViewModelBase {
private MyClass myClass;
public MyClass MyClass{
get{
return myClass;
}
set{
this.myClass = value;
base.OnPropertyChanged();
}
}
private string testString;
public string TestString{
get{
return testString;
}
set{
this.testString = value;
base.OnPropertyChanged();
}
}
public MyViewModel(){
this.MyClass = new MyClass();
this.TestString = "blah, blah, blah"
}}
//class
public class MyClass : ViewModelBase{
private string myString;
public string MyString{
get {
return myString
}
set{
this.myString = value;
base.OnPropertyChanged();
}
}
public MyClass (){
this.MyString = "25"; }}
The base class - ViewModelBase implements INotifyChange and contains OnPropertyChanged handler logic.
I have a UserControl where I wish to bind values from MyClass like this:
<TextBlock Text="{x:Bind Path=MyViewModel.MyClass.MyString, Mode=TwoWay}"></TextBlock>
However this does not work. Value is binded in initialisation correctly, but any change in MyViewModel.MyClass.MyString is not reflected in texblock, the text remains the same. The OnPropertyChange is raised, the breakpoint in ViewModelBase is hit with MyString value changed, but it is somehow not propagated to texblock.
Binding on simple value from MyClass works like charm, this textblock is updated, when property is changed:
<TextBlock Text="{x:Bind Path=MyViewModel.TestString, Mode=TwoWay}"></TextBlock>
What am I missing? Why the textblock with binding to "MyViewModel.MyClass.MyString" is not being updated?
I have tested the code you provided and don't think there is any problem with it. Where the problem could be, however, is your Page's code-behind. By any chance, aren't you using this as the property declaration?
public MyViewModel MyViewModel => new MyViewModel();
Because in this case, every access to MyViewModel property evaluates as a new instance of MyViewModel class. In this configuration, you would properly change the property, but the UI would never notice, as you would update the property on a new instance. If you instead use
public MyViewModel MyViewModel { get; } = new MyViewModel();
You will get the correct behavior of creating only one instance when the page is created.
I have the following code:
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
}
public MyViewModel MyViewModel { get; } = new MyViewModel();
private void Button_Click(object sender, RoutedEventArgs e)
{
MyViewModel.MyClass.MyString = "test";
}
}
And my simple ViewModelBase:
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}

PropertyChanged is always null

I am binding a Datagrid to Observablecollection.Below is my ObservableCollection class.
But the Property changed is always NULL and it is null even after Making my XAML like this.
Please Guide me in this
Thanks!
<DataGridTextColumn Binding="{Binding, Mode=TwoWay, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True, UpdateSourceTrigger=PropertyChanged} Header = "Serial" />
public class itemobject
{
public event PropertyChangedEventHandler PropertyChanged;
// This method is called by the Set accessor of each property.
// The CallerMemberName attribute that is applied to the optional propertyName
// parameter causes the property name of the caller to be substituted as an argument.
private void NotifyPropertyChanged( String propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
int sno1;
public int Sno
{
get
{ return sno1; }
set
{
if (value != sno1)
{
sno1= value;
NotifyPropertyChanged("Sno");
}
}
}
In XAML you should specify which property exactly changed.
<DataGridTextColumn Binding="{Binding Path = Sno} Header = "Serial" />
And like i did, you need to create ViewModelBase class.
public abstract class ViewModelBase : INotifyPropertyChanged, IDisposable {
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string propertyName = null) {
var handle = PropertyChanged;
handle?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public virtual void Dispose() => PropertyChanged = null;
}
public class itemobject : ViewModelBase{
int sno1;
public int Sno{
get => sno1;
set{
if (value != sno1){
sno1= value;
OnPropertyChanged(nameof(Sno));
}
}
}

using RavenDB with ServiceStack

I read this post by Phillip Haydon about how to use NHibernate/RavenDB with ServiceStack.
I don't see the point about getting the IDocumentStore and open new session every time i need something from the db like this:
public class FooService : ServiceBase<Foo>
{
public IDocumentStore RavenStore{ get; set; }
protected override object Run(ProductFind request)
{
using (var session = RavenStore.OpenSession())
{
// Do Something...
return new FooResponse{/*Object init*/};
}
}
}
Why cant i just use one session per request and when the request is ended, commit the changes or roll them back according to the response status?
If my approach is fine, than how can i implement it?
here is my attempt:
I created this class:
public class RavenSession : IRavenSession
{
#region Data Members
private readonly IDocumentStore _store;
private IDocumentSession _innerSession;
#endregion
#region Properties
public IDocumentSession InnerSession
{
get { return _innerSession ?? (_innerSession = _store.OpenSession()); }
}
#endregion
#region Ctor
public RavenSession(IDocumentStore store)
{
_store = store;
}
#endregion
#region Public Methods
public void Commit()
{
if (_innerSession != null)
{
try
{
InnerSession.SaveChanges();
}
finally
{
InnerSession.Dispose();
}
}
}
public void Rollback()
{
if (_innerSession != null)
{
InnerSession.Dispose();
}
}
#endregion
#region IDocumentSession Delegation
public ISyncAdvancedSessionOperation Advanced
{
get { return InnerSession.Advanced; }
}
public void Delete<T>(T entity)
{
InnerSession.Delete(entity);
}
public ILoaderWithInclude<object> Include(string path)
{
return InnerSession.Include(path);
}
public ILoaderWithInclude<T> Include<T, TInclude>(Expression<Func<T, object>> path)
{
return InnerSession.Include<T, TInclude>(path);
}
public ILoaderWithInclude<T> Include<T>(Expression<Func<T, object>> path)
{
return InnerSession.Include(path);
}
public T Load<T>(string id)
{
return InnerSession.Load<T>(id);
}
public T[] Load<T>(params string[] ids)
{
return InnerSession.Load<T>(ids);
}
public T Load<T>(ValueType id)
{
return InnerSession.Load<T>(id);
}
public T[] Load<T>(IEnumerable<string> ids)
{
return InnerSession.Load<T>(ids);
}
public IRavenQueryable<T> Query<T, TIndexCreator>() where TIndexCreator : AbstractIndexCreationTask, new()
{
return InnerSession.Query<T, TIndexCreator>();
}
public IRavenQueryable<T> Query<T>()
{
return InnerSession.Query<T>();
}
public IRavenQueryable<T> Query<T>(string indexName)
{
return InnerSession.Query<T>(indexName);
}
public void Store(dynamic entity, string id)
{
InnerSession.Store(entity, id);
}
public void Store(object entity, Guid etag, string id)
{
InnerSession.Store(entity, etag, id);
}
public void Store(object entity, Guid etag)
{
InnerSession.Store(entity, etag);
}
public void Store(dynamic entity)
{
InnerSession.Store(entity);
}
#endregion
}
And now my service looks like this:
public class FooService : ServiceBase<Foo>
{
public IRavenSession RavenSession { get; set; }
protected override object Run(ProductFind request)
{
// Do Something with RavenSession...
return new FooResponse {/*Object init*/};
}
}
but i still need to find a way to know when the request is ended for commit/rollback the changes.
the best way i found is by using ResponseFilters:
public class AppHost : AppHostBase
{
public AppHost()
: base("", typeof (Foo).Assembly, typeof (FooService).Assembly)
{
}
public override void Configure(Container container)
{
// Some Configuration...
this.ResponseFilters.Add((httpReq, httpResp, respnseDto) =>
{
var currentSession = (RavenSession) this.Container.Resolve<IRavenSession>();
if (!httpResp.IsErrorResponse())
{
currentSession.Commit();
}
else
{
currentSession.Rollback();
}
});
// Some Configuration...
}
}
I am sure that there is a better way to do this but how?
I just included this on the Configure method for the AppHost
var store = new DocumentStore()
{
Url = "http://127.0.0.1:8080",
DefaultDatabase = "Test"
}.Initialize();
container.Register(store);
container.Register(c => c.Resolve<IDocumentStore>().OpenSession()).ReusedWithin(ReuseScope.Request);
You can put it aside on module and initialize it.
Then in your services just add a constructor that accepts IDocumentSession
public HelloService : Service {
private readonly IDocumentSession session;
public HelloService(IDocumentSession session) {
this.session = session;
}
}
And you're good to go.
Filtering the response in ServiceStack
The ways to introspect the Response in ServiceStack is with either:
The Response Filter or Response Filter Attributes or other custom hooks
Overriding AppHost.ServiceExceptionHandler or custom OnAfterExecute() hook
Some other notes that might be helpful:
ServiceStack's built-in IOC (Funq) now supports RequestScope
You can add IDisposable to a base class which gets called immediately after the service has finished executing, e.g. if you were to use an RDBMS:
public class FooServiceBase : IService, IDisposable
{
public IDbConnectionFactory DbFactory { get; set; }
private IDbConnection db;
public IDbConnection Db
{
get { return db ?? (db = DbFactory.OpenDbConnection()); }
}
public object Any(ProductFind request)
{
return new FooResponse {
Result = Db.Id<Product>(request.Id)
};
}
public void Dispose()
{
if (db != null) db.Dispose();
}
}
I tried the answer given by Felipe Leusin but it has not worked for me. The main thing that I want to achieve is having a single DocumentSession.SaveChanges call per request. After looking at the RacoonBlog DocumentSession lifecycle management and at ServiceStack request lifecycle events I put together a configuration that works for me:
public override void Configure(Funq.Container container)
{
RequestFilters.Add((httpReq, httpRes, requestDto) =>
{
IDocumentSession documentSession = Container.Resolve<IDocumentStore>().OpenSession();
Container.Register<IDocumentSession>(documentSession);
});
ResponseFilters.Add((httpReq, httpRes, requestDto) =>
{
using (var documentSession = Container.Resolve<IDocumentSession>())
{
if (documentSession == null)
return;
if (httpRes.StatusCode >= 400 && httpRes.StatusCode < 600)
return;
documentSession.SaveChanges();
}
});
var documentStore = new DocumentStore
{
ConnectionStringName = "RavenDBServer",
DefaultDatabase = "MyDatabase",
}.Initialize();
container.Register(documentStore);
I am using funq with RequestScope for my RavenSession, and now i update it to:
public class RavenSession : IRavenSession, IDisposable
{
#region Data Members
private readonly IDocumentStore _store;
private readonly IRequestContext _context;
private IDocumentSession _innerSession;
#endregion
#region Properties
public IDocumentSession InnerSession
{
get { return _innerSession ?? (_innerSession = _store.OpenSession()); }
}
#endregion
#region Ctor
public RavenSession(IDocumentStore store, IRequestContext context)
{
_store = store;
_context = context;
}
#endregion
#region IDocumentSession Delegation
public ISyncAdvancedSessionOperation Advanced
{
get { return InnerSession.Advanced; }
}
public void Delete<T>(T entity)
{
InnerSession.Delete(entity);
}
public ILoaderWithInclude<object> Include(string path)
{
return InnerSession.Include(path);
}
public ILoaderWithInclude<T> Include<T, TInclude>(Expression<Func<T, object>> path)
{
return InnerSession.Include<T, TInclude>(path);
}
public ILoaderWithInclude<T> Include<T>(Expression<Func<T, object>> path)
{
return InnerSession.Include(path);
}
public T Load<T>(string id)
{
return InnerSession.Load<T>(id);
}
public T[] Load<T>(params string[] ids)
{
return InnerSession.Load<T>(ids);
}
public T Load<T>(ValueType id)
{
return InnerSession.Load<T>(id);
}
public T[] Load<T>(IEnumerable<string> ids)
{
return InnerSession.Load<T>(ids);
}
public IRavenQueryable<T> Query<T, TIndexCreator>() where TIndexCreator : AbstractIndexCreationTask, new()
{
return InnerSession.Query<T, TIndexCreator>();
}
public IRavenQueryable<T> Query<T>()
{
return InnerSession.Query<T>();
}
public IRavenQueryable<T> Query<T>(string indexName)
{
return InnerSession.Query<T>(indexName);
}
public void Store(dynamic entity, string id)
{
InnerSession.Store(entity, id);
}
public void Store(object entity, Guid etag, string id)
{
InnerSession.Store(entity, etag, id);
}
public void Store(object entity, Guid etag)
{
InnerSession.Store(entity, etag);
}
public void Store(dynamic entity)
{
InnerSession.Store(entity);
}
#endregion
#region Implementation of IDisposable
public void Dispose()
{
if (_innerSession != null)
{
var httpResponse = _context.Get<IHttpResponse>();
try
{
if (!httpResponse.IsErrorResponse())
{
_innerSession.SaveChanges();
}
}
finally
{
_innerSession.Dispose();
}
}
}
#endregion
}
but this would not work because:
1) although i am using RequestScope, no one is register the IRequestContext of the request so funq cant resolve my RavenSession.
2) funq does not run the Dispose method after the request is done, which is odd.