I'm trying to enable a button that is bounded when a binded property nested field is entered, but the button is disabled even when all properties are filled with data. What should I change?
this is my base visual state
public class BaseVisualState : Exrin.Framework.VisualState
{
public BaseVisualState() : this(null)
{
}
public BaseVisualState(IBaseModel i_Model) : base(i_Model)
{
PropertyObservable = Observable.FromEventPattern(
(EventHandler<PropertyChangedEventArgs> i_Event) => new PropertyChangedEventHandler(i_Event),
i_EventChanged => this.PropertyChanged += i_EventChanged,
i_EventChanged => this.PropertyChanged -= i_EventChanged);
}
public IObservable<EventPattern<PropertyChangedEventArgs>> PropertyObservable { get; private set; }
}
and my visual state is
public class BeaconAddVisualState : BaseVisualState
{
private readonly IBeaconAddModel r_Model;
public BeaconAddVisualState(IBeaconAddModel i_Model)
: base(i_Model)
{
r_Model = i_Model;
}
[Binding(BindingType.TwoWay)]
public Beacon Beacon
{
get
{
return Get<Beacon>();
}
set
{
Set(value);
}
}
public override async void Init()
{
Beacon = new Beacon();
}
}
The beacon class notifies on property changed
public class Beacon : Document<Guid>, INotifyPropertyChanged
{
private string m_ProximityUuid;
private int? m_Major;
private int? m_Minor;
[Required]
public string ProximityUuid
{
get
{
return m_ProximityUuid;
}
set
{
if(m_ProximityUuid != value)
{
m_ProximityUuid = value;
notifyPropertyChanged();
}
}
}
[Required]
public int? Major
{
get
{
return m_Major;
}
set
{
if (m_Major != value)
{
m_Major = value;
notifyPropertyChanged();
}
}
}
[Required]
public int? Minor
{
get
{
return m_Minor;
}
set
{
if (m_Minor != value)
{
m_Minor = value;
notifyPropertyChanged();
}
}
}
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([CallerMemberName] string i_PropertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(i_PropertyName));
}
}
}
and this is the viewmodel
public class BeaconAddViewModel : BaseAuthViewModel
{
private readonly IBeaconAddModel r_Model;
private readonly IDisposable r_Subscription;
public BeaconAddViewModel(IBeaconAddModel i_Model)
: base(new BeaconAddVisualState(i_Model))
{
r_Model = i_Model;
r_Subscription = State.PropertyObservable.Where(
i_Arg => i_Arg.EventArgs.PropertyName == nameof(State.Beacon.ProximityUuid)
|| i_Arg.EventArgs.PropertyName == nameof(State.Beacon.Major) || i_Arg.EventArgs.PropertyName == nameof(State.Beacon.Minor)).Subscribe(
i_Arg =>
{
AddCommand.OnCanExecuteChanged();
});
}
private BeaconAddVisualState State => VisualState as BeaconAddVisualState;
public override Task OnNavigated(object i_Args)
{
return base.OnNavigated(i_Args);
}
public IRelayCommand AddCommand
{
get
{
return GetCommand(
() =>
{
return new RelayCommand(
async (i_Parameter) =>
{
await r_Model.AddBeacon(i_Parameter as Beacon);
await NavigationService.Navigate("Beacons");
}, (i_Obj) => !string.IsNullOrEmpty(State.Beacon.ProximityUuid) && State.Beacon.Major.HasValue && State.Beacon.Minor.HasValue;
});
}
}
public override void Disposing()
{
base.Disposing();
r_Subscription?.Dispose();
}
}
The problem here is you are trigger INPC inside your Beacon class, but the BaseVisualState is only looking at whether Beacon (the object itself) is changing.
Hence you either have to bring the properties outside of Beacon, directly into the VisualState, or relay the INPC event.
e.g. in your Set of Beacon do
That will mean, each time any property in Beacon is changed, it will say that the Beacon class itself has changed, and trigger the INPC for the VisualState.
Note: make sure the event is disposed on, when it's reset. This would mean not doing it in an anon func as I have shown above, but doing the += and tabbing it to create another method.