I create ValidatableModelBase class and have some troubles. I need subscribe to SourceCache changes and cast Count collection to IObservable bool . How can i do it?
private readonly SourceCache<ValidationResult, string> _results;
public IObservalbe<bool> IsValid { get; }
public ValidatableModelBase()
{
_results = new SourceCach<ValidationResult, string>(x => x.PropertyName);
//Doesn't work. I think because i dont .Subscribe() to changes?
IsValid = _results.Connect().IsEmpty();
}
upd:
HasErrors = collection.CountChanged.Subscribe(x => {Count = x;});
IsValid = this.WhenAnyValie(x => x.HasErrors).Select(x => x == 0);
You can do something like this:
var databasesValid = collectionOfReactiveObjects
.Connect().Count().Select(x => x == 0);
// Then you can convert that IObservable<bool> to a view model
// property declared as ObservableAsPropertyHelper<bool>.
_databasesValid = databasesValid.ToProperty(this, x => x.DatabasesValid);
You'll need to include the DynamicData.Aggregation namespace.
See https://github.com/reactiveui/DynamicData/blob/63960b0fa7bd0362c40e137498cd0014ba02f3dc/src/DynamicData/Aggregation/CountEx.cs#L57 here for code reference.
Related
How I can write this code with a single line?
public class RadGridColumnObservable
{
public RadGridColumnObservable(RadGridView grid, IMessageBus messageBus)
{
this.grid = grid;
this.messageBus = messageBus;
}
public IDisposable Initialize()
{
CreateStates();
return Observable
.Interval(TimeSpan.FromSeconds(1))
.Subscribe(_ => CheckUp());
}
//
private readonly RadGridView grid;
private readonly IMessageBus messageBus;
private readonly List<bool> states = new List<bool>();
//
private void CreateStates()
{
states.Clear();
states.AddRange(grid.Columns.Select(it => it.IsVisible));
}
private void CheckUp()
{
if (states.SequenceEqual(grid.Columns.Select(it => it.IsVisible))) return;
CreateStates();
messageBus.Publish(new NotifyMessage(MessageTypes.HistoryColumnsChanged));
}
}
The idea is: I want to check if the IsVisible property is changed.
I don't like to use this line:
private readonly List<bool> states = new List<bool>();
You could get your IsVisible values with something like this:
private IObservable<List<bool>> CreateStatesObservable()
{
return Observable.Interval(TimeSpan.FromSeconds(1))
.Select(_ => grid.Columns.Select(it => it.IsVisible));
}
And then use Scan to keep track of your previous value:
public void Initialize()
{
var observable =
CreateStatesObservable()
.Scan(
(prev: default(List<bool>), actual: default(List<bool>)),
(acc, c) => (acc.actual, current))
.Where(values => !values.actual.SequenceEqual(values.prev))
.Select(_ => true);
}
Or you could use Defer and do something like this:
public void Initialize2()
{
var observable = CreateStatesObservable();
var deferred =
Observable.Defer(
() =>
{
List<bool> previous = null;
return observable.Select(
values =>
{
if (previous is null)
{
previous = values;
return false;
}
if (!values.SequenceEqual(previous))
{
previous = values;
return true;
}
return false;
});
})
.Where(value => value);
}
Both options should give you an observable which only produces a value when one of the columns IsVisible has changed. (That value just being a true)
Edit:
You can also use DistinctUntilChanged() with your own IEqualityComparer like this:
class ListComparer : IEqualityComparer<List<bool>>
{
bool IEqualityComparer<List<bool>>.Equals(List<bool>? a, List<bool>? b)
{
if (a is null && b is null)
{
return true;
}
if (a is null || b is null)
{
return false;
}
return a.SequenceEqual(b);
}
int IEqualityComparer<List<bool>>.GetHashCode(List<bool> obj)
{
return obj.GetHashCode();
}
}
And then use it like this:
public void Initialize()
{
var observable =
CreateStatesObservable()
.DistinctUntilChanged(new ListComparer())
.Select(_ => true);
}
I think you can adjust your Initialize() to something like this:
public IDisposable Initialize()
{
return Observable
.Interval(TimeSpan.FromSeconds(1))
.Select(v => grid.Columns.Select(it => it.IsVisible).ToList())
.Scan((prev: new List<bool>(), actual: new List<bool>()),
(acc, c) => (acc.actual, c))
.Where(it => !it.actual.SequenceEqual(it.prev))
.Subscribe(_ => messageBus.Publish(new NotifyMessage(MessageTypes.HistoryColumnsChanged)));
}
This should give you the same result and you only need SequenceEqual() once. The scan is just there so you have the "current" and the "previous" value. You don't really need to do a check inside it in your case.
I use now something like this:
public class RadGridColumnObservable
{
public RadGridColumnObservable(RadGridView grid, IMessageBus messageBus)
{
this.grid = grid;
this.messageBus = messageBus;
}
public IDisposable Initialize()
{
return Observable
.Interval(TimeSpan.FromSeconds(1))
.Scan((prev: grid.Columns.Select(it => it.IsVisible).ToList(), actual: grid.Columns.Select(it => it.IsVisible).ToList()),
(acc, c) =>
{
if (!acc.prev.SequenceEqual(acc.actual))
acc.prev = acc.actual;
acc.actual = grid.Columns.Select(it => it.IsVisible).ToList();
return acc;
})
.Where(it => !it.prev.SequenceEqual(it.actual))
.Subscribe(it => messageBus.Publish(new NotifyMessage(MessageTypes.HistoryColumnsChanged)));
}
//
private readonly RadGridView grid;
private readonly IMessageBus messageBus;
}
but i don't like because i use 2nd time "SequenceEqual".
I have this viewModel where I like to check the accessGroupList has any value of True and set baccess base on that value. If they are both false it then baccess would be false but if one of them is true baccess would be true.
MemberViewModel result = new MemberViewModel();
result.IsPractices = true;
result.IsUser = false;
var accessGroupList = new List<string>();
accessGroupList.Add("IsUser");
accessGroupList.Add("IsBestPractices");
var baccess = result.GetType().GetProperties().First(o => o.Name == accessGroupList).GetValue(result, null);
bool? baccess = Property as bool?;
I create this simple console project. You can do this, remove comment from where for using in your project
class Program
{
static void Main(string[] args)
{
var cl = new MyClass();
cl._item1 = false;
cl._item2 = false;
var a = cl.GetType().GetProperties()
//.Where(x => accessGroupList.Contains(x.Name))
.Select(x => new
{
name = x.Name,
value = (bool)x.GetValue(cl, null)
})
.Any(x => x.value);
Console.WriteLine(a);
}
}
public class MyClass
{
public bool _item1 { get; set; }
public bool _item2 { get; set; }
}
First of all note that accessGroupList is list and you need to use Contains or Any to compare it with property name. Then you can select the value of those property that appeared in accessGroupList
var baccess = result.GetType().GetProperties()
.Where(o => accessGroupList.Contains(o.Name))
.Select(t=>(bool)t.GetValue(result, null));
var baccess = result.GetType().GetProperties()
.Where(o => accessGroupList.Any(propName => Equals(propName, o.Name))
.Select(x => (bool)x.GetValue(result, null))
.Any(val => val);
Your problem is that you were using .First (which will only return one item) but then in there, you're also comparing the property name to the list itself. You need to do another linq operation to get the appropriate properties out, then you can check if any of those properties have a value of true
I am trying to filter the ICollection but the filter is not getting applied and instead it gives me all records. is there any issue in the predicate ? so there are two methods where the filter is applied on collection. First it filters records for SW_Version and then in the second filter it filters for matching id. basically i am implementing search functionality.
public void Updateswlist()
{
CRmappings2 = new ObservableCollection<SPFetchCREntity>(crentities.ToList());
AllCRSP = CollectionViewSource.GetDefaultView(CRmappings2);
SearchMU = SelectedSW.SW_Version;
AllCRSP.Filter = obj =>
{
SPFetchCREntity entity = obj as SPFetchCREntity;
return obj != null && entity.SW_Version == SearchMU.ToString();
};
AllCRSP.Refresh();
2nd Filter
public void searchMUID()
{
AllCRSP.Filter = obj =>
{
SPFetchCREntity entity = obj as SPFetchCREntity;
return obj != null && entity.MU_Identifier == Mupass.ToString();
};
AllCRSP.Refresh();
}
The second filter overwrites the first one. If you want to be able to filter by both properties, you need to include both conditions in your predicate:
public void searchMUID()
{
string Mupass = "";
AllCRSP.Filter = obj =>
{
SPFetchCREntity entity = obj as SPFetchCREntity;
return obj != null && entity.SW_Version == SearchMU.ToString() && entity.MU_Identifier == Mupass.ToString();
};
AllCRSP.Refresh();
}
I much prefer using ReactiveList from ReactiveUI and its derived collection. It just works - no need to manualy fire Refresh
public class TweetsListViewModel : ReactiveObject
{
ReactiveList<Tweet> Tweets = new ReactiveList<Tweet>();
IReactiveDerivedList<TweetTileViewModel> TweetTiles;
IReactiveDerivedList<TweetTileViewModel> VisibleTiles;
public TweetsListViewModel()
{
TweetTiles = Tweets.CreateDerivedCollection(
x => new TweetTileViewModel() { Model = x },
x => true,
(x, y) => x.CreatedAt.CompareTo(y.CreatedAt));
VisibleTiles = TweetTiles.CreateDerivedCollection(
x => x,
x => !x.IsHidden);
}
}
If SPFetchCREntity does not implement INotifyPropertyChanged you can create dervied collection with reset argument, which is observable. Every time it ticks, whole list is filtered
This is how i resolved dual filter issue. i got the idea from this link https://code.msdn.microsoft.com/windowsdesktop/CollectionView-Tips-MVVM-d6ebb4a7
public void searchMUID()
{
Muview = (CollectionView)new CollectionViewSource { Source = CRmappings2 }.View; //CRmappings2 is an observable collection and Muview is a public property of collectionview
//FirstCRSP = AllCRSP;
//muview.Filter = null;
Muview.Filter = obj =>
{
SPFetchCREntity entity = obj as SPFetchCREntity;
return obj != null && entity.SW_Version == SearchMU.ToString() && entity.MU_Identifier == Mupass.ToString();
};
Muview.Refresh();
}
Ultimately I want to have an internal interface with a setter and a public one with a getter. The code that replicates this scenario is roughed below:
[TestMethod]
public void TestMethod3()
{
var fake1 = A.Fake<IInterface1>(a => a.Implements(typeof(IInterface2)));
string backingString = null;
IInterface2 fake2 = (IInterface2)fake1;
A.CallTo(fake1)
.Where(a => a.Method.Name.Equals("set_Property"))
.Invokes((string param) => { backingString = param; });
A.CallTo(fake1)
.Where(a => a.Method.Name.Equals("get_Property"))
.WithReturnType<string>().Returns(backingString); //doesn't work
A.CallTo(fake2)
.Where(a => a.Method.Name.Equals("set_Property"))
.Invokes((string param) => { backingString = param; });
A.CallTo(fake2)
.Where(a => a.Method.Name.Equals("get_Property"))
.WithReturnType<string>().Returns(backingString); //doesn't work
fake1.Property = "asdf";
Assert.AreEqual("asdf", fake1.Property); //fails -> fake1.Property is null
Assert.AreEqual(fake1.Property, fake2.Property); //fails -> fake2.Property is null
}
}
public interface IInterface1
{
string Property { get; set; }
}
public interface IInterface2
{
string Property { get; }
}
I could get as far as using backingString to store the setter, but when setting up the getter it doesn't work as I wanted it to.
I also tried something in the line of A.CallTo(() => fake1.Property).Returns(backingString) to no avail.
Would appreciate assistance of them experts :)
When you set up your
A.CallTo(fake1)
.Where(a => a.Method.Name.Equals("get_Property"))
.WithReturnType<string>().Returns(backingString);
(and similarly for fake2),
the value of backingString is null, so that's what's returned later on when you access the Property getter.
In order to return the value of backingString at the time the Property getter is called, you want ReturnsLazily.
Make this change in each place and the tests pass:
A.CallTo(fake1)
.Where(a => a.Method.Name.Equals("get_Property"))
.WithReturnType<string>().ReturnsLazily(() => backingString);
I have a class and my validation looks like this:
public ValidationResult Validate(CustomerType customerType)
{
CustomerType Validator validator = new CustomerTypeValidator();
validator.RuleFor(x => x.Number).Must(BeUniqueNumber);
return validator.Validate(customerType);
}
public bool BeUniqueNumber(int number)
{
//var result = repository.Get(x => x.Number == customerType.Number)
// .Where(x => x.Id != customerType.Id)
// .FirstOrDefault();
//return result == null;
return true;
}
The CustomerTypeValidator is a basic validator class that validates string properties.
I also add a new rule to check if the number is unique in the db. I do it in this class because there's a reference to the repository. The validator class has no such reference.
The problem here is that the BeUniqueNumber method should have a CustomerType parameter. However when I do this, I get an error on the RuleFor line above because 'Must' needs an int as a parameter.
Is there a way around this?
Can you try this?
public ValidationResult Validate(CustomerType customerType)
{
CustomerTypeValidator validator = new CustomerTypeValidator();
validator.RuleFor(x => x).Must(HaveUniqueNumber);
return validator.Validate(customerType);
}
public bool HaveUniqueNumber(CustomerType customerType)
{
var result = repository.Get(x => x.Number == customerType.Number)
.Where(x => x.Id != customerType.Id)
.FirstOrDefault();
return result == null;
//return true;
}
You should also be able to do this:
public ValidationResult Validate(CustomerType customerType)
{
CustomerTypeValidator validator = new CustomerTypeValidator();
validator.RuleFor(x => x.Number).Must(BeUniqueNumber);
return validator.Validate(customerType);
}
public bool BeUniqueNumber(CustomerType customerType, int number)
{
var result = repository.Get(x => x.Number == number)
.Where(x => x.Id != customerType.Id)
.FirstOrDefault();
return result == null;
//return true;
}
"I also add a new rule to check if the number is unique in the db. I do
it in this class because there's a reference to the repository."
Well, why can't you give your validator a reference to the repository too?
CustomerTypeValidator validator = new CustomerTypeValidator(repository);