Skip to content

Commit

Permalink
Merge pull request #281 from runceel/main
Browse files Browse the repository at this point in the history
Release v7.12.0
  • Loading branch information
runceel authored Aug 6, 2021
2 parents 28f8f6b + 52cdfab commit bcfaa41
Show file tree
Hide file tree
Showing 14 changed files with 248 additions and 53 deletions.
3 changes: 3 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# These are supported funding model platforms

github: [neuecc, runceel] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 changes: 2 additions & 2 deletions README-ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@ ReactivePropertyは、Reactive ExtensionsをMVVMパターンと非同期用に
## コミッターの情報

Yoshifumi Kawai a.k.a. [@neuecc](https://twitter.com/neuecc) is Founder/CEO/CTO of Cysharp, Inc in Tokyo, Japan.
Awarded Microsoft MVP for Visual Studio and Development Technologies since April, 2011.
Awarded Microsoft MVP for Developer Technologies since April, 2011.

Takaaki Suzuki a.k.a. [@xin9le](https://twitter.com/xin9le) software devleoper in Fukui, Japan.
Awarded Microsoft MVP for Visual Studio and Development Technologies since July, 2012.
Awarded Microsoft MVP for Developer Technologies since July, 2012.

Kazuki Ota a.k.a. [@okazuki](https://twitter.com/okazuki) software developer in Tokyo, Japan.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -160,11 +160,11 @@ If too many questions are posted, then I plan to separate posting place about fe
## Author info

Yoshifumi Kawai a.k.a. [@neuecc](https://twitter.com/neuecc) is Founder/CEO/CTO of Cysharp, Inc in Tokyo, Japan.
Awarded Microsoft MVP for Visual Studio and Development Technologies since April, 2011.
Awarded Microsoft MVP for Developer Technologies since April, 2011.
He is an original owner of ReactiveProperty.

Takaaki Suzuki a.k.a. [@xin9le](https://twitter.com/xin9le) software developer in Tokyo, Japan.
Awarded Microsoft MVP for Visual Studio and Development Technologies since July, 2012.
Takaaki Suzuki a.k.a. [@xin9le](https://twitter.com/xin9le) software developer in Fukui, Japan.
Awarded Microsoft MVP for Developer Technologies since July, 2012.

Kazuki Ota a.k.a. [@okazuki](https://twitter.com/okazuki) software developer in Tokyo, Japan.
Awarded Microsoft MVP for Windows Development since July 2011 to Feb 2017.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,18 @@ namespace ReactivePropertySamples.ViewModels
public class EventToReactiveViewModel : ViewModelBase
{
public ReactivePropertySlim<string> MousePosition { get; }

public ReactivePropertySlim<bool> CanExecute { get; }
public ReactiveCommand<string> OpenFileCommand { get; }
public ReadOnlyReactivePropertySlim<string> OpenedFile { get; }

public EventToReactiveViewModel()
{
MousePosition = new ReactivePropertySlim<string>();

OpenFileCommand = new ReactiveCommand<string>();
MousePosition = new ReactivePropertySlim<string>()
.AddTo(Disposables);
CanExecute = new ReactivePropertySlim<bool>(true)
.AddTo(Disposables);
OpenFileCommand = CanExecute.ToReactiveCommand<string>()
.AddTo(Disposables);
OpenedFile = OpenFileCommand.Select(x => $"You selected {x}")
.ToReadOnlyReactivePropertySlim()
.AddTo(Disposables);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
<Window x:Class="ReactivePropertySamples.WPF.Views.EventToReactiveWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:ReactivePropertySamples.WPF.Views"
xmlns:interactivity="http://schemas.microsoft.com/xaml/behaviors"
xmlns:behaviors="clr-namespace:ReactivePropertySamples.WPF.Behaviors"
xmlns:viewModels="clr-namespace:ReactivePropertySamples.ViewModels;assembly=ReactivePropertySamples.Shared"
xmlns:reactiveProperty="clr-namespace:Reactive.Bindings.Interactivity;assembly=ReactiveProperty.WPF"
xmlns:converters="clr-namespace:ReactivePropertySamples.WPF.Converters"
mc:Ignorable="d"
Title="EventToReactiveWindow" Height="450" Width="800">
<Window
x:Class="ReactivePropertySamples.WPF.Views.EventToReactiveWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:behaviors="clr-namespace:ReactivePropertySamples.WPF.Behaviors"
xmlns:converters="clr-namespace:ReactivePropertySamples.WPF.Converters"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:interactivity="http://schemas.microsoft.com/xaml/behaviors"
xmlns:local="clr-namespace:ReactivePropertySamples.WPF.Views"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:reactiveProperty="clr-namespace:Reactive.Bindings.Interactivity;assembly=ReactiveProperty.WPF"
xmlns:viewModels="clr-namespace:ReactivePropertySamples.ViewModels;assembly=ReactivePropertySamples.Shared"
Title="EventToReactiveWindow"
Width="800"
Height="450"
mc:Ignorable="d">
<interactivity:Interaction.Behaviors>
<behaviors:DisposeViewModelWhenClosedBehavior />
</interactivity:Interaction.Behaviors>
Expand All @@ -23,10 +26,11 @@
<Run Text="Mouse position: " />
<Run Text="{Binding MousePosition.Value}" />
</TextBlock>
<Border Height="100"
Background="LightGoldenrodYellow"
BorderThickness="1"
BorderBrush="DarkRed">
<Border
Height="100"
Background="LightGoldenrodYellow"
BorderBrush="DarkRed"
BorderThickness="1">
<interactivity:Interaction.Triggers>
<interactivity:EventTrigger EventName="MouseMove">
<reactiveProperty:EventToReactiveProperty ReactiveProperty="{Binding MousePosition}">
Expand All @@ -36,6 +40,7 @@
</interactivity:Interaction.Triggers>
</Border>
<Label Content="EventToReactiveCommand:" Style="{StaticResource TitleLabel}" />
<CheckBox Content="CanExecute" IsChecked="{Binding CanExecute.Value, Mode=TwoWay}" />
<Button Content="Open file">
<interactivity:Interaction.Triggers>
<interactivity:EventTrigger EventName="Click">
Expand Down
5 changes: 5 additions & 0 deletions Source/ReactiveProperty.Core/IReactiveProperty.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ public interface IReactiveProperty : IReadOnlyReactiveProperty, IHasErrors, INot
/// </summary>
/// <value>The value.</value>
new object Value { get; set; }

/// <summary>
/// Forces the notify.
/// </summary>
void ForceNotify();
}

/// <summary>
Expand Down
4 changes: 2 additions & 2 deletions Source/ReactiveProperty.Core/ReactivePropertySlim.cs
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ IEnumerable INotifyDataErrorInfo.GetErrors(string propertyName)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <seealso cref="Reactive.Bindings.IReadOnlyReactiveProperty{T}"/>
/// <seealso cref="Reactive.Bindings.IObserverLinkedList{T}"/>
/// <seealso cref="Reactive.Bindings.Internals.IObserverLinkedList{T}"/>
/// <seealso cref="System.IObserver{T}"/>
public class ReadOnlyReactivePropertySlim<T> : IReadOnlyReactiveProperty<T>, IObserverLinkedList<T>, IObserver<T>
{
Expand Down Expand Up @@ -455,7 +455,7 @@ public override string ToString()
/// <summary>
/// </summary>
/// <seealso cref="Reactive.Bindings.IReadOnlyReactiveProperty{T}"/>
/// <seealso cref="Reactive.Bindings.IObserverLinkedList{T}"/>
/// <seealso cref="Reactive.Bindings.Internals.IObserverLinkedList{T}"/>
/// <seealso cref="System.IObserver{T}"/>
public static class ReadOnlyReactivePropertySlim
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Reactive.Disposables;
using Reactive.Bindings.Extensions;
using System.Collections.Generic;
using System.Reactive.Concurrency;

#if NETFX_CORE
using Windows.UI.Xaml.Controls;
Expand Down Expand Up @@ -59,7 +60,7 @@ private void OnCommandInitialized(DependencyPropertyChangedEventArgs e)
{
if (e.OldValue == null && e.NewValue != null)
{
SetSubScribes();
SetSubscribes();
}
}
#else
Expand Down Expand Up @@ -91,6 +92,37 @@ private void OnAllowDisableChanged()
autoEnableSource.OnNext(EventArgs.Empty);
}

/// <summary>
/// Gets or sets calling or not ICommand.Execute method on IScheduler that is set to ReactivePropertyScheduler.Default.
/// </summary>
public bool CallExecuteOnScheduler
{
get { return (bool)GetValue(CallExecuteOnSchedulerProperty); }
set { SetValue(CallExecuteOnSchedulerProperty, value); }
}

/// <summary>
/// The CallExecuteOnSchedulere Property
/// </summary>
public static readonly DependencyProperty CallExecuteOnSchedulerProperty =
DependencyProperty.Register(
nameof(CallExecuteOnScheduler),
typeof(bool),
typeof(EventToReactiveCommand),
new PropertyMetadata(true, OnCallExecuteOnScheduler));

private static void OnCallExecuteOnScheduler(DependencyObject d, DependencyPropertyChangedEventArgs e) =>
((EventToReactiveCommand)d).OnCallExecuteOnScheduler(e);

private void OnCallExecuteOnScheduler(DependencyPropertyChangedEventArgs e)
{
if (AssociatedObject != null)
{
SetSubscribes();
}
}


/// <summary>
/// Ignore EventArgs. If value is false then uses Unit.Default.
/// </summary>
Expand All @@ -112,21 +144,25 @@ protected override void OnAttached()
{
base.OnAttached();
#if !NETFX_CORE
SetSubScribes();
SetSubscribes();
#endif
}

private void SetSubScribes()
private void SetSubscribes()
{
IObservable<object> ox = source;
foreach (var c in Converters)
{
c.AssociateObject = AssociatedObject;
ox = c.Convert(ox);
}
ox
.ObserveOnUIDispatcher()
.Where(_ => Command != null)

if (CallExecuteOnScheduler)
{
ox = ox.ObserveOn(ReactivePropertyScheduler.Default);
}

ox.Where(_ => Command != null)
.Subscribe(x => Command.Execute(x)).AddTo(Disposable);

#if NETFX_CORE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@

namespace Reactive.Bindings.ObjectExtensions
{
/// <summary>
/// ObserveEveryValueChanged extension method
/// </summary>
public static class ObserveEveryValueChangedExtensions
{
/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion Source/SharedProperties.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<RootNamespace>Reactive.Bindings</RootNamespace>
<Version>7.11.0</Version>
<Version>7.12.0</Version>
<Authors>neuecc xin9le okazuki</Authors>
<PackageProjectUrl>https://github.com/runceel/ReactiveProperty</PackageProjectUrl>
<PackageTags>rx mvvm async rx-main reactive</PackageTags>
Expand Down
18 changes: 0 additions & 18 deletions Test/ReactiveProperty.NETStandard.Tests/Init.cs

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
using System;
using System.Collections.Generic;
using System.Reactive.Concurrency;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Linq;
using Microsoft.Reactive.Testing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Reactive.Bindings;
using Reactive.Bindings.Interactivity;

namespace ReactiveProperty.Tests.Interactivity
{
[TestClass]
public class EventToReactiveCommandTest
{
[STATestMethod]
public void Test_CallExecuteOnScheduler_TrueCase()
{
var scheduler = new MockScheduler();
ReactivePropertyScheduler.SetDefault(scheduler);

var commandArgument = new object();
var mockCommand = new Mock<ICommand>();
mockCommand.Setup(x => x.Execute(It.Is<object>(y => y == commandArgument)))
.Verifiable();
mockCommand.Setup(x => x.CanExecute(It.IsAny<object>()))
.Returns(true);

var attachedTarget = new FrameworkElement();
var target = new EventToReactiveCommand
{
Command = mockCommand.Object,
};
target.Attach(attachedTarget);

target.AsDynamic().Invoke(commandArgument);

scheduler.CallHistory.Count.Is(1);
mockCommand.VerifyAll();
}

[STATestMethod]
public void Test_CallExecuteOnScheduler_FalseCase()
{
var scheduler = new MockScheduler();
ReactivePropertyScheduler.SetDefault(scheduler);

var commandArgument = new object();
var mockCommand = new Mock<ICommand>();
mockCommand.Setup(x => x.Execute(It.Is<object>(y => y == commandArgument)))
.Verifiable();
mockCommand.Setup(x => x.CanExecute(It.IsAny<object>()))
.Returns(true);

var attachedTarget = new FrameworkElement();
var target = new EventToReactiveCommand
{
CallExecuteOnScheduler = false,
Command = mockCommand.Object,
};
target.Attach(attachedTarget);

target.AsDynamic().Invoke(commandArgument);

scheduler.CallHistory.Any().Is(false);
mockCommand.VerifyAll();
}

[STATestMethod]
public void Test_CallExecuteOnScheduler_ChangePropertyAfterInitialized()
{
var scheduler = new MockScheduler();
ReactivePropertyScheduler.SetDefault(scheduler);

var commandArgument = new object();
var mockCommand = new Mock<ICommand>();
mockCommand.Setup(x => x.Execute(It.Is<object>(y => y == commandArgument)))
.Verifiable();
mockCommand.Setup(x => x.CanExecute(It.IsAny<object>()))
.Returns(true);

var attachedTarget = new FrameworkElement();
var target = new EventToReactiveCommand
{
CallExecuteOnScheduler = false,
Command = mockCommand.Object,
};
target.Attach(attachedTarget);

target.CallExecuteOnScheduler = true;

target.AsDynamic().Invoke(commandArgument);

scheduler.CallHistory.Count.Is(1);
mockCommand.VerifyAll();
}

private class MockScheduler : IScheduler
{
private readonly ImmediateScheduler _innerScheduler = ImmediateScheduler.Instance;
public DateTimeOffset Now => _innerScheduler.Now;

public List<object> CallHistory { get; } = new List<object>();

public IDisposable Schedule<TState>(TState state, Func<IScheduler, TState, IDisposable> action)
{
CallHistory.Add(state);
return _innerScheduler.Schedule(state, action);
}

public IDisposable Schedule<TState>(TState state, TimeSpan dueTime, Func<IScheduler, TState, IDisposable> action)
{
CallHistory.Add(state);
return _innerScheduler.Schedule(state, dueTime, action);
}

public IDisposable Schedule<TState>(TState state, DateTimeOffset dueTime, Func<IScheduler, TState, IDisposable> action)
{
CallHistory.Add(state);
return _innerScheduler.Schedule(state, dueTime, action);
}
}
}
}
Loading

0 comments on commit bcfaa41

Please sign in to comment.