├── .gitattributes
├── .github
└── workflows
│ └── build.yaml
├── .gitignore
├── Android
├── Assisticant.Android.csproj
├── BindingManagerExtensions.cs
├── ButtonBindingExtensions.cs
├── ListViewBindingExtensions.cs
├── NumberPickerBindingExtensions.cs
├── Properties
│ └── AssemblyInfo.cs
├── Resources
│ └── Resource.Designer.cs
└── TextBindingExtensions.cs
├── Assisticant.Netstandard
└── Assisticant.Netstandard.csproj
├── Assisticant.UnitTest
├── Assisticant.UnitTest.Portable.csproj
├── Assisticant.UnitTest.WPF.csproj
├── CollectionContentTest.cs
├── CollectionData
│ ├── SourceCollection.cs
│ └── TargetCollection.cs
├── CollectionTest.cs
├── ComputedListTest.cs
├── ContactListData
│ ├── Contact.cs
│ ├── ContactList.cs
│ ├── ContactListSortOrder.cs
│ ├── ContactListViewModel.cs
│ └── ContactViewModel.cs
├── DictionaryTests.cs
├── DirectComputed.cs
├── DirectComputedTest.cs
├── DirectConcurrencyTest.cs
├── DynamicSortOrderTest.cs
├── Images
│ ├── UnitTestLogo.scale-100.png
│ ├── UnitTestSmallLogo.scale-100.png
│ ├── UnitTestSplashScreen.scale-100.png
│ └── UnitTestStoreLogo.scale-100.png
├── IndirectComputed.cs
├── IndirectComputedTest.cs
├── IndirectConcurrencyTest.cs
├── LargeListTest.cs
├── MemoryLeakTest.cs
├── MultithreadedData
│ ├── AbstractThread.cs
│ ├── SourceThread.cs
│ └── TargetThread.cs
├── MultithreadedTest.cs
├── NotificationTest.cs
├── NotifyDataErrorInfoTests.cs
├── Package.appxmanifest
├── Properties
│ └── AssemblyInfo.cs
├── SortedCollectionTest.cs
├── SourceData.cs
├── SubscriptionTest.cs
└── packages.config
├── Assisticant.sln
├── Assisticant
├── Assisticant.Framework.csproj
├── Assisticant.Universal.csproj
├── Assisticant.WPF.csproj
├── AssisticantExtensions.cs
├── AssisticantReflectionUtils.cs
├── Binding
│ ├── BindingManager.cs
│ ├── IDisplayDataConverter.cs
│ └── IInputSubscription.cs
├── BindingInterceptor.cs
├── Collections
│ ├── ComputedDictionary.cs
│ ├── ComputedList.cs
│ ├── Impl
│ │ └── UpdateCollectionHelper.cs
│ ├── ObservableDictionary.cs
│ └── ObservableList.cs
├── Computed.cs
├── ComputedJob.cs
├── Descriptors
│ ├── PlatformProxy.NotifyDataErrorInfo.cs
│ ├── PlatformProxy.cs
│ ├── ProxyDescriptionProvider.cs
│ ├── ProxyEventDescriptor.cs
│ ├── ProxyPropertyDescriptor.cs
│ └── ProxyTypeDescriptor.cs
├── Fields
│ ├── Computed.cs
│ ├── ComputedSubscription.cs
│ └── Observable.cs
├── ForView.cs
├── InertialProperty.cs
├── MakeCommand.cs
├── MemoizedTypeName.cs
├── Metas
│ ├── AtomSlot.cs
│ ├── BindingListSlot.cs
│ ├── CollectionItem.cs
│ ├── CollectionSlot.cs
│ ├── CommandMeta.cs
│ ├── ComputedMeta.cs
│ ├── FieldMeta.cs
│ ├── ListSlot.cs
│ ├── MemberMeta.cs
│ ├── MemberSlot.cs
│ ├── MethodCommand.cs
│ ├── ObservableMeta.cs
│ ├── PassThroughSlot.cs
│ ├── PropertyMeta.cs
│ ├── TypeMeta.cs
│ ├── ValuePropertyMeta.cs
│ ├── ViewModelTypes.cs
│ └── ViewProxy.cs
├── NamedPrecedents.cs
├── NotifyAfterAttribute.cs
├── Observable.cs
├── Precedent.cs
├── Properties
│ └── AssemblyInfo.cs
├── PropertyTracker.cs
├── RecycleBin.cs
├── StaticExtension.cs
├── ThreadLocal.cs
├── Timers
│ ├── DroppingTimeSpan.cs
│ ├── FloatingDateTime.cs
│ ├── FloatingTimeSpan.cs
│ ├── FloatingTimeZone.cs
│ ├── ObservableTimer.cs
│ ├── RisingTimeSpan.cs
│ └── UtcTimeZone.cs
├── UpdateScheduler.cs
├── Validation
│ ├── ExpressionExtensions.cs
│ ├── IValidation.cs
│ ├── IValidationRules.cs
│ ├── NumericPropertyValidationContextExtentions.cs
│ ├── ObjectPropertyValidationContextExtensions.cs
│ ├── OptionalMessagePropertyValidationContext.cs
│ ├── PropertyPredicateContext.cs
│ ├── PropertyRuleset.cs
│ ├── PropertyValidationContext.cs
│ ├── PropertyValidationContextBase.cs
│ ├── PropertyValidator.cs
│ ├── StringPropertyValidationContextExtensions.cs
│ ├── ValidationRules.cs
│ └── fluent.gv
├── ViewModelBase.cs
├── ViewModelLocatorBase.cs
├── ViewSelector.cs
├── ViewSelectorExtension.cs
├── WeakArray.cs
├── WeakHashSet.cs
└── XamlTypes
│ ├── PlatformProxy.cs
│ ├── PrimitiveXamlType.cs
│ ├── ProxyXamlMember.cs
│ ├── ProxyXamlMetadataProvider.cs
│ └── ProxyXamlType.cs
├── Common
├── CommonProperties.cs
└── Mallardsoft.snk
├── License.txt
├── NuGet
├── App
│ ├── Assisticant.App.nuspec
│ └── content
│ │ ├── Models
│ │ ├── Document.cs.pp
│ │ ├── Item.cs.pp
│ │ └── Selection.cs.pp
│ │ ├── Readme_Assisticant.txt.pp
│ │ └── ViewModels
│ │ ├── ItemHeader.cs.pp
│ │ ├── ItemViewModel.cs.pp
│ │ ├── MainViewModel.cs.pp
│ │ └── ViewModelLocator.cs.pp
├── Core
│ └── assisticant.nuspec
└── Snippets
│ ├── Assisticant.Snippets.1.1.0.nupkg
│ ├── assisticant.snippets.nuspec
│ ├── content
│ └── Readme_Assisticant_Snippets.txt
│ └── tools
│ ├── comp.snippet
│ ├── compList.snippet
│ ├── install.ps1
│ ├── obs.snippet
│ ├── obsList.snippet
│ └── uninstall.ps1
├── README.md
└── iOS
├── Assisticant.iOS.csproj
├── BindingManagerExtensions.cs
├── ButtonBindingExtensions.cs
├── Properties
└── AssemblyInfo.cs
├── StepperBindingExtensions.cs
├── TableViewBindingExtensions.cs
└── TextBindingExtensions.cs
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 | *.sln merge=union
7 | *.csproj merge=union
8 | *.vbproj merge=union
9 | *.fsproj merge=union
10 | *.dbproj merge=union
11 |
12 | # Standard to msysgit
13 | *.doc diff=astextplain
14 | *.DOC diff=astextplain
15 | *.docx diff=astextplain
16 | *.DOCX diff=astextplain
17 | *.dot diff=astextplain
18 | *.DOT diff=astextplain
19 | *.pdf diff=astextplain
20 | *.PDF diff=astextplain
21 | *.rtf diff=astextplain
22 | *.RTF diff=astextplain
23 |
--------------------------------------------------------------------------------
/.github/workflows/build.yaml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | # Run this workflow every time a commit gets pushed to main or a pull request gets opened against main
4 | on:
5 | push:
6 | branches:
7 | - master
8 | pull_request:
9 | branches:
10 | - master
11 |
12 | jobs:
13 | build:
14 | name: Call Azure Pipeline
15 | runs-on: ubuntu-latest
16 | steps:
17 | - name: Azure Pipelines Action
18 | uses: Azure/pipelines@v1
19 | with:
20 | azure-devops-project-url: https://michaellperry.visualstudio.com/Assisticant
21 | azure-pipeline-name: 'Build from GitHub'
22 | azure-devops-token: ${{ secrets.AZURE_DEVOPS_TOKEN }}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | packages/
2 | NuGet/Core/lib/
3 | NuGet/Forms/lib/
4 | .vs/
5 |
6 | #ignore thumbnails created by windows
7 | Thumbs.db
8 | #Ignore files build by Visual Studio
9 | *.obj
10 | *.pdb
11 | *.user
12 | *.aps
13 | *.pch
14 | *.vspscc
15 | *_i.c
16 | *_p.c
17 | *.ncb
18 | *.suo
19 | *.tlb
20 | *.tlh
21 | *.bak
22 | *.cache
23 | *.ilk
24 | *.log
25 | [Bb]in
26 | [Dd]ebug*/
27 | *.lib
28 | *.sbr
29 | obj/
30 | [Rr]elease*/
31 | _ReSharper*/
32 | [Tt]est[Rr]esult*
33 | .svn/
34 | *.[Mm][Dd][Ff]
35 | *.[Ll][Dd][Ff]
36 | *.dbmdl
37 | *.eto
38 | *.ncb
39 | *.user
40 | *.stuff
41 | ~*.*
42 | *.suo
43 | *.xap
44 | *.vs10x
45 | Index.dat
46 | Storage.dat
47 | Generated_Code*/
48 | *.Publish.xml
49 | *.docstates
50 | AppPackages/
51 | *.ncrunchproject
52 | *.ncrunchsolution
53 |
--------------------------------------------------------------------------------
/Android/Assisticant.Android.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Debug
5 | AnyCPU
6 | 8.0.30703
7 | 2.0
8 | {C4E189E5-7905-4678-A340-018F89274F49}
9 | {EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
10 | {9ef11e43-1701-4396-8835-8392d57abb70}
11 | Library
12 | Properties
13 | Assisticant.Binding
14 | Assisticant.Android
15 | 512
16 | True
17 | Resources\Resource.Designer.cs
18 | Off
19 | false
20 | v9.0
21 | true
22 |
23 |
24 | true
25 | portable
26 | false
27 | bin\Debug\
28 | DEBUG;TRACE
29 | prompt
30 | 4
31 |
32 |
33 | portable
34 | true
35 | bin\Release\
36 | TRACE
37 | prompt
38 | 4
39 | bin\Release\Assisticant.Android.xml
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | {2caefd06-13f2-47ea-b600-78c332d5bb2b}
61 | Assisticant.Netstandard
62 |
63 |
64 |
65 |
66 |
67 |
68 |
75 |
--------------------------------------------------------------------------------
/Android/BindingManagerExtensions.cs:
--------------------------------------------------------------------------------
1 | using Android.App;
2 | using System;
3 | using System.Threading;
4 |
5 | namespace Assisticant.Binding
6 | {
7 | ///
8 | /// Binding manager extensions.
9 | ///
10 | public static class BindingManagerExtensions
11 | {
12 | ///
13 | /// Initialize the binding manager for an activity.
14 | ///
15 | /// The binding manager for the activity.
16 | /// The activity that owns the binding manager.
17 | public static void Initialize(this BindingManager bindings, Activity activity)
18 | {
19 | UpdateScheduler.Initialize(action =>
20 | {
21 | ThreadPool.QueueUserWorkItem(delegate(Object obj)
22 | {
23 | activity.RunOnUiThread(action);
24 | });
25 | });
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Android/ButtonBindingExtensions.cs:
--------------------------------------------------------------------------------
1 | using Android.Widget;
2 | using System;
3 |
4 | namespace Assisticant.Binding
5 | {
6 | ///
7 | /// Button binding extensions.
8 | ///
9 | public static class ButtonBindingExtensions
10 | {
11 | class ButtonClickSubscription : IInputSubscription
12 | {
13 | private Button _control;
14 | private Action _action;
15 |
16 | public ButtonClickSubscription(Button control, Action action)
17 | {
18 | _control = control;
19 | _action = action;
20 | }
21 |
22 | public void Subscribe()
23 | {
24 | _control.Click += ButtonClick;
25 | }
26 |
27 | public void Unsubscribe()
28 | {
29 | _control.Click -= ButtonClick;
30 | }
31 |
32 | private void ButtonClick(object sender, EventArgs e)
33 | {
34 | _action();
35 | }
36 | }
37 |
38 | ///
39 | /// Bind a button's command to an action.
40 | ///
41 | /// The binding manager.
42 | /// The button.
43 | /// The action to perform when the button is tapped.
44 | public static void BindCommand(this BindingManager bindings, Button control, Action action)
45 | {
46 | bindings.Bind(new ButtonClickSubscription(control, action));
47 | }
48 |
49 | ///
50 | /// Bind a button's command and Enabled property to an action and a condition.
51 | ///
52 | /// The binding manager.
53 | /// The button.
54 | /// The ation to perform when the button is tapped.
55 | /// The condition that controls when the button is enabled.
56 | public static void BindCommand(this BindingManager bindings, Button control, Action action, Func condition)
57 | {
58 | bindings.Bind(condition, b => control.Enabled = b, new ButtonClickSubscription(control, action));
59 | }
60 | }
61 | }
--------------------------------------------------------------------------------
/Android/NumberPickerBindingExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Android.Widget;
3 |
4 | namespace Assisticant.Binding
5 | {
6 | ///
7 | /// Number picker binding extensions.
8 | ///
9 | public static class NumberPickerBindingExtensions
10 | {
11 | class ValueBinding : IInputSubscription
12 | {
13 | private NumberPicker _control;
14 | private Action _input;
15 | private IDisplayDataConverter _converter;
16 |
17 | public ValueBinding(NumberPicker control, Action input, IDisplayDataConverter converter)
18 | {
19 | _control = control;
20 | _input = input;
21 | _converter = converter;
22 | }
23 |
24 | public void Subscribe()
25 | {
26 | _control.ValueChanged += NumberPickerValueChanged;
27 | }
28 |
29 | public void Unsubscribe()
30 | {
31 | _control.ValueChanged -= NumberPickerValueChanged;
32 | }
33 |
34 | private void NumberPickerValueChanged (object sender, EventArgs e)
35 | {
36 | _input(_converter.ConvertInput(_control.Value));
37 | }
38 | }
39 |
40 | class Identity : IDisplayDataConverter
41 | {
42 | public static Identity Instance = new Identity();
43 |
44 | public int ConvertOutput (int data)
45 | {
46 | return data;
47 | }
48 |
49 | public int ConvertInput (int display)
50 | {
51 | return display;
52 | }
53 | }
54 |
55 | ///
56 | /// Bind the value of a number picker to a property using a value converter.
57 | ///
58 | /// The binding manager.
59 | /// The number picker.
60 | /// A function that gets the property.
61 | /// An action sets the property.
62 | /// A custom value converter to type double.
63 | /// The type of property to which the value is bound.
64 | public static void BindValue(this BindingManager bindings, NumberPicker control, Func output, Action input, IDisplayDataConverter converter)
65 | {
66 | bindings.Bind (output, s => control.Value = converter.ConvertOutput(s), new ValueBinding(control, input, converter));
67 | }
68 |
69 | ///
70 | /// Bind the value of a number picker to an integer property.
71 | ///
72 | /// The binding manager.
73 | /// The number picker.
74 | /// A function that gets the property.
75 | /// An action sets the property.
76 | public static void BindValue(this BindingManager bindings, NumberPicker control, Func output, Action input)
77 | {
78 | BindValue (bindings, control, output, input, Identity.Instance);
79 | }
80 | }
81 | }
82 |
83 |
--------------------------------------------------------------------------------
/Android/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 | using Android.App;
5 |
6 | // General Information about an assembly is controlled through the following
7 | // set of attributes. Change these attribute values to modify the information
8 | // associated with an assembly.
9 | [assembly: AssemblyTitle("Assisticant.Android")]
10 | [assembly: AssemblyDescription("")]
11 | [assembly: AssemblyConfiguration("")]
12 | [assembly: AssemblyCompany("")]
13 | [assembly: AssemblyProduct("Assisticant.Android")]
14 | [assembly: AssemblyCopyright("Copyright © 2016")]
15 | [assembly: AssemblyTrademark("")]
16 | [assembly: AssemblyCulture("")]
17 | [assembly: ComVisible(false)]
18 |
19 | // Version information for an assembly consists of the following four values:
20 | //
21 | // Major Version
22 | // Minor Version
23 | // Build Number
24 | // Revision
25 | //
26 | // You can specify all the values or you can default the Build and Revision Numbers
27 | // by using the '*' as shown below:
28 | // [assembly: AssemblyVersion("1.0.*")]
29 | [assembly: AssemblyVersion("1.0.0.0")]
30 | [assembly: AssemblyFileVersion("1.0.0.0")]
31 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/CollectionContentTest.cs:
--------------------------------------------------------------------------------
1 | #if NETFX_CORE
2 | using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
3 | #else
4 | using Microsoft.VisualStudio.TestTools.UnitTesting;
5 | #endif
6 | using Assisticant.UnitTest.ContactListData;
7 | using System.Linq;
8 |
9 | namespace Assisticant.UnitTest
10 | {
11 | [TestClass]
12 | public class CollectionContentTest
13 | {
14 | private ContactList _contactList;
15 | private ContactListViewModel _viewModel;
16 | private int _collectionChangedCount;
17 |
18 | [TestInitialize]
19 | public void Initialize()
20 | {
21 | _contactList = new ContactList();
22 | _viewModel = new ContactListViewModel(_contactList);
23 | _contactList.AddContact(new Contact() { FirstName = "Michael", LastName = "Perry" });
24 | _contactList.AddContact(new Contact() { FirstName = "Ada", LastName = "Lovelace" });
25 | _contactList.AddContact(new Contact() { FirstName = "Charles", LastName = "Babbage" });
26 |
27 | _collectionChangedCount = 0;
28 | _viewModel.ContactsCollectionChanged +=
29 | delegate
30 | {
31 | _collectionChangedCount++;
32 | };
33 | }
34 |
35 | [TestMethod]
36 | public void WhenContactAddedShouldNotifyCollectionChanged()
37 | {
38 | ContactViewModel firstByDefaultOrder = _viewModel.Contacts.First();
39 | _contactList.AddContact(new Contact() { FirstName = "Martin", LastName = "Fowler" });
40 |
41 | Assert.AreEqual(1, _collectionChangedCount);
42 | }
43 |
44 | [TestMethod]
45 | public void WhenContactDeletedShouldNotifyCollectionChanged()
46 | {
47 | ContactViewModel firstByDefaultOrder = _viewModel.Contacts.First();
48 | _contactList.DeleteContact(_contactList.Contacts.First());
49 |
50 | Assert.AreEqual(1, _collectionChangedCount);
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/CollectionData/SourceCollection.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Assisticant.UnitTest.CollectionData
4 | {
5 | public class SourceCollection
6 | {
7 | private List _numbers = new List();
8 | private Observable _indNumbers = new Observable();
9 |
10 | public void Insert(int number)
11 | {
12 | _indNumbers.OnSet();
13 | _numbers.Add(number);
14 | }
15 |
16 | public IEnumerable Numbers
17 | {
18 | get { _indNumbers.OnGet(); return _numbers; }
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/CollectionData/TargetCollection.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace Assisticant.UnitTest.CollectionData
5 | {
6 | public class TargetCollection
7 | {
8 | private SourceCollection _source;
9 | private List _results = new List();
10 | private Computed _depResults;
11 |
12 | public TargetCollection(SourceCollection source)
13 | {
14 | _source = source;
15 | _depResults = new Computed(UpdateResults);
16 | }
17 |
18 | public IEnumerable Results
19 | {
20 | get { _depResults.OnGet(); return _results; }
21 | }
22 |
23 | private void UpdateResults()
24 | {
25 | _results = _source.Numbers.Select(number => number + 1).ToList();
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/CollectionTest.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | #if NETFX_CORE
3 | using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
4 | #else
5 | using Microsoft.VisualStudio.TestTools.UnitTesting;
6 | #endif
7 | using Assisticant.UnitTest.CollectionData;
8 |
9 | namespace Assisticant.UnitTest
10 | {
11 | [TestClass]
12 | public class CollectionTest
13 | {
14 | private SourceCollection _source;
15 |
16 | [TestInitialize]
17 | public void Initialize()
18 | {
19 | _source = new SourceCollection();
20 | }
21 |
22 | [TestMethod]
23 | public void InsertIntoSourceShouldCauseNewElementInTarget()
24 | {
25 | TargetCollection target = new TargetCollection(_source);
26 |
27 | _source.Insert(3);
28 | int firstNumber = target.Results.Single();
29 | Assert.AreEqual(4, firstNumber);
30 | }
31 |
32 | [TestMethod]
33 | public void InsertIntoSourceBeforeTargetCreatedShouldCauseNewElementInTarget()
34 | {
35 | _source.Insert(3);
36 |
37 | TargetCollection target = new TargetCollection(_source);
38 |
39 | int firstNumber = target.Results.Single();
40 | Assert.AreEqual(4, firstNumber);
41 | }
42 |
43 | [TestMethod]
44 | public void InsertASecondIntoSourceTargetShouldUpdate()
45 | {
46 | TargetCollection target = new TargetCollection(_source);
47 | _source.Insert(42);
48 | target.Results.Single();
49 |
50 | _source.Insert(43);
51 | int[] results = target.Results.ToArray();
52 |
53 | Assert.AreEqual(2, results.Length);
54 | Assert.AreEqual(43, results[0]);
55 | Assert.AreEqual(44, results[1]);
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/ComputedListTest.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | #if NETFX_CORE
3 | using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
4 | #else
5 | using Microsoft.VisualStudio.TestTools.UnitTesting;
6 | #endif
7 | using Assisticant.Collections;
8 | using Assisticant.UnitTest.ContactListData;
9 |
10 | namespace Assisticant.UnitTest
11 | {
12 | [TestClass]
13 | public class ComputedListTest
14 | {
15 | private ContactList _model;
16 | private ComputedList _contactViewModels;
17 |
18 | [TestInitialize]
19 | public void Initialize()
20 | {
21 | _model = new ContactList();
22 | _model.AddContact(new Contact() { FirstName = "Charles", LastName = "Babbage" });
23 | _model.AddContact(new Contact() { FirstName = "Alan", LastName = "Turing" });
24 | _contactViewModels = new ComputedList(() =>
25 | from c in _model.Contacts
26 | select new ContactViewModel(c)
27 | );
28 | }
29 |
30 | [TestMethod]
31 | public void ComputedListMapsToSourceList()
32 | {
33 | Assert.AreEqual(2, _contactViewModels.Count);
34 | Assert.AreEqual("Charles Babbage", _contactViewModels[0].FullName);
35 | Assert.AreEqual("Alan Turing", _contactViewModels[1].FullName);
36 | }
37 |
38 | [TestMethod]
39 | public void WhenSourceListChanges_ComputedListChanges()
40 | {
41 | _model.AddContact(new Contact() { FirstName = "Bertrand", LastName = "Meyer" });
42 |
43 | Assert.AreEqual(3, _contactViewModels.Count);
44 | Assert.AreEqual("Bertrand Meyer", _contactViewModels[2].FullName);
45 | }
46 |
47 | [TestMethod]
48 | public void ComputedsAreRecycled()
49 | {
50 | ContactViewModel oldObject = _contactViewModels[0];
51 | _model.AddContact(new Contact() { FirstName = "Bertrand", LastName = "Meyer" });
52 |
53 | Assert.AreSame(oldObject, _contactViewModels[0]);
54 | }
55 |
56 | [TestMethod]
57 | public void ListDependsUponSourceCollection()
58 | {
59 | int triggerUpdate = _contactViewModels.Count;
60 | bool wasInvalidated = false;
61 | _contactViewModels.ComputedSentry.Invalidated += delegate
62 | {
63 | wasInvalidated = true;
64 | };
65 |
66 | _model.AddContact(new Contact() { FirstName = "Bertrand", LastName = "Meyer" });
67 |
68 | Assert.IsTrue(wasInvalidated, "The computed list should be invalidated.");
69 | }
70 |
71 | [TestMethod]
72 | public void ListDoesNotDependUponChildProperties()
73 | {
74 | int triggerUpdate = _contactViewModels.Count;
75 | bool wasInvalidated = false;
76 | _contactViewModels.ComputedSentry.Invalidated += delegate
77 | {
78 | wasInvalidated = true;
79 | };
80 |
81 | _model.Contacts.First().FirstName = "George";
82 |
83 | Assert.IsFalse(wasInvalidated, "The computed list should not be invalidated.");
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/ContactListData/Contact.cs:
--------------------------------------------------------------------------------
1 | using Assisticant;
2 |
3 | namespace Assisticant.UnitTest.ContactListData
4 | {
5 | public class Contact
6 | {
7 | private string _firstName;
8 | private string _lastName;
9 |
10 | #region Observable properties
11 | // Generated by Update Controls --------------------------------
12 | private Observable _indFirstName = new Observable();
13 | private Observable _indLastName = new Observable();
14 |
15 | public string FirstName
16 | {
17 | get { _indFirstName.OnGet(); return _firstName; }
18 | set { _indFirstName.OnSet(); _firstName = value; }
19 | }
20 |
21 | public string LastName
22 | {
23 | get { _indLastName.OnGet(); return _lastName; }
24 | set { _indLastName.OnSet(); _lastName = value; }
25 | }
26 | // End generated code --------------------------------
27 | #endregion
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/ContactListData/ContactList.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Assisticant.Collections;
3 |
4 | namespace Assisticant.UnitTest.ContactListData
5 | {
6 | public class ContactList
7 | {
8 | private ObservableList _contacts = new ObservableList();
9 |
10 | public void AddContact(Contact contact)
11 | {
12 | _contacts.Add(contact);
13 | }
14 |
15 | public void DeleteContact(Contact contact)
16 | {
17 | _contacts.Remove(contact);
18 | }
19 |
20 | public IEnumerable Contacts
21 | {
22 | get { return _contacts; }
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/ContactListData/ContactListSortOrder.cs:
--------------------------------------------------------------------------------
1 |
2 | namespace Assisticant.UnitTest.ContactListData
3 | {
4 | public enum ContactListSortOrder
5 | {
6 | NoOrder,
7 | FirstName,
8 | LastName
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/ContactListData/ContactListViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | namespace Assisticant.UnitTest.ContactListData
6 | {
7 | public class ContactListViewModel
8 | {
9 | private ContactList _contactList;
10 | private ContactListSortOrder _sortOrder;
11 | private Observable _indSortOrder = new Observable();
12 |
13 | private List _contactViewModels;
14 | private Computed _depContactViewModels;
15 |
16 | public delegate void NotifyCollectionChanged();
17 | public event NotifyCollectionChanged ContactsCollectionChanged;
18 |
19 | public ContactListViewModel(ContactList contactList)
20 | {
21 | _contactList = contactList;
22 | _depContactViewModels = new Computed(UpdateContactViewModels);
23 | _depContactViewModels.Invalidated += new Action(_depContactViewModels_Invalidated);
24 | }
25 |
26 | void _depContactViewModels_Invalidated()
27 | {
28 | ContactsCollectionChanged();
29 | }
30 |
31 | public ContactListSortOrder SortOrder
32 | {
33 | get
34 | {
35 | _indSortOrder.OnGet();
36 | return _sortOrder;
37 | }
38 | set
39 | {
40 | if (_sortOrder != value) _indSortOrder.OnSet();
41 | _sortOrder = value;
42 | }
43 | }
44 |
45 | public IEnumerable Contacts
46 | {
47 | get
48 | {
49 | _depContactViewModels.OnGet();
50 | return _contactViewModels;
51 | }
52 | }
53 |
54 | private void UpdateContactViewModels()
55 | {
56 | IEnumerable contacts = _contactList.Contacts;
57 | if (SortOrder == ContactListSortOrder.FirstName)
58 | contacts = contacts.OrderBy(contact => contact.FirstName);
59 | else if (SortOrder == ContactListSortOrder.LastName)
60 | contacts = contacts.OrderBy(contact => contact.LastName);
61 |
62 | _contactViewModels = contacts
63 | .Select(contact => new ContactViewModel(contact))
64 | .ToList();
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/ContactListData/ContactViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Assisticant.UnitTest.ContactListData
4 | {
5 | public class ContactViewModel
6 | {
7 | private Contact _contact;
8 |
9 | public ContactViewModel(Contact contact)
10 | {
11 | _contact = contact;
12 | }
13 |
14 | public string FullName
15 | {
16 | get
17 | {
18 | return string.Format("{0} {1}", _contact.FirstName, _contact.LastName);
19 | }
20 | }
21 |
22 | public override bool Equals(object obj)
23 | {
24 | if (this == obj)
25 | return true;
26 | ContactViewModel that = obj as ContactViewModel;
27 | if (that == null)
28 | return false;
29 | return _contact == that._contact;
30 | }
31 |
32 | public override int GetHashCode()
33 | {
34 | return _contact.GetHashCode();
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/DirectComputed.cs:
--------------------------------------------------------------------------------
1 | using Assisticant.Fields;
2 |
3 | namespace Assisticant.UnitTest
4 | {
5 | public class DirectComputed
6 | {
7 | private SourceData _source;
8 |
9 | private Computed _property;
10 |
11 | public DirectComputed(SourceData source)
12 | {
13 | _source = source;
14 | _property = new Computed(() => _source.SourceProperty);
15 | }
16 |
17 | public int ComputedProperty
18 | {
19 | get { return _property; }
20 | }
21 |
22 | public bool IsUpToDate
23 | {
24 | get { return _property.IsUpToDate; }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/DirectComputedTest.cs:
--------------------------------------------------------------------------------
1 | #if NETFX_CORE
2 | using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
3 | #else
4 | using Microsoft.VisualStudio.TestTools.UnitTesting;
5 | #endif
6 |
7 | namespace Assisticant.UnitTest
8 | {
9 | [TestClass]
10 | public class DirectComputedTest
11 | {
12 | public TestContext TestContext { get; set; }
13 |
14 | private SourceData _source;
15 | private DirectComputed _computed;
16 |
17 | [TestInitialize]
18 | public void Initialize()
19 | {
20 | _source = new SourceData();
21 | _computed = new DirectComputed(_source);
22 | }
23 |
24 | [TestMethod]
25 | public void ComputedIsInitiallyOutOfDate()
26 | {
27 | Assert.IsFalse(_computed.IsUpToDate, "The dependent is initially up to date");
28 | }
29 |
30 | [TestMethod]
31 | public void ComputedRemainsOutOfDateOnChange()
32 | {
33 | _source.SourceProperty = 3;
34 | Assert.IsFalse(_computed.IsUpToDate, "The dependent is up to date after change");
35 | }
36 |
37 | [TestMethod]
38 | public void ComputedIsUpdatedOnGet()
39 | {
40 | int fetch = _computed.ComputedProperty;
41 | Assert.IsTrue(_computed.IsUpToDate, "The dependent has not been updated");
42 | }
43 |
44 | [TestMethod]
45 | public void ComputedIsUpdatedAfterChangeOnGet()
46 | {
47 | _source.SourceProperty = 3;
48 | int fetch = _computed.ComputedProperty;
49 | Assert.IsTrue(_computed.IsUpToDate, "The dependent has not been updated");
50 | }
51 |
52 | [TestMethod]
53 | public void ComputedGetsValueFromItsPrecedent()
54 | {
55 | _source.SourceProperty = 3;
56 | Assert.AreEqual(3, _computed.ComputedProperty);
57 | }
58 |
59 | [TestMethod]
60 | public void ComputedIsOutOfDateAgainAfterChange()
61 | {
62 | _source.SourceProperty = 3;
63 | int fetch = _computed.ComputedProperty;
64 | _source.SourceProperty = 4;
65 | Assert.IsFalse(_computed.IsUpToDate, "The dependent did not go out of date");
66 | }
67 |
68 | [TestMethod]
69 | public void ComputedIsUpdatedAgainAfterChange()
70 | {
71 | _source.SourceProperty = 3;
72 | int fetch = _computed.ComputedProperty;
73 | _source.SourceProperty = 4;
74 | fetch = _computed.ComputedProperty;
75 | Assert.IsTrue(_computed.IsUpToDate, "The dependent did not get udpated");
76 | }
77 |
78 | [TestMethod]
79 | public void ComputedGetsValueFromItsPrecedentAgainAfterChange()
80 | {
81 | _source.SourceProperty = 3;
82 | int fetch = _computed.ComputedProperty;
83 | _source.SourceProperty = 4;
84 | Assert.AreEqual(4, _computed.ComputedProperty);
85 | }
86 |
87 | [TestMethod]
88 | public void PrecedentIsOnlyAskedOnce()
89 | {
90 | int getCount = 0;
91 | _source.AfterGet += () => ++getCount;
92 |
93 | _source.SourceProperty = 3;
94 | int fetch = _computed.ComputedProperty;
95 | fetch = _computed.ComputedProperty;
96 |
97 | Assert.AreEqual(1, getCount);
98 | }
99 |
100 | [TestMethod]
101 | public void PrecedentIsAskedAgainAfterChange()
102 | {
103 | int getCount = 0;
104 | _source.AfterGet += () => ++getCount;
105 |
106 | _source.SourceProperty = 3;
107 | int fetch = _computed.ComputedProperty;
108 | fetch = _computed.ComputedProperty;
109 | _source.SourceProperty = 4;
110 | fetch = _computed.ComputedProperty;
111 | fetch = _computed.ComputedProperty;
112 |
113 | Assert.AreEqual(2, getCount);
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/DirectConcurrencyTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | #if NETFX_CORE
3 | using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
4 | #else
5 | using Microsoft.VisualStudio.TestTools.UnitTesting;
6 | #endif
7 |
8 | namespace Assisticant.UnitTest
9 | {
10 | [TestClass]
11 | public class DirectConcurrencyTest
12 | {
13 | public TestContext TestContext { get; set; }
14 |
15 | private SourceData _source;
16 | private DirectComputed _computed;
17 |
18 | [TestInitialize]
19 | public void Initialize()
20 | {
21 | _source = new SourceData();
22 | _computed = new DirectComputed(_source);
23 | }
24 |
25 | [TestMethod]
26 | public void ComputedIsOutOfDateAfterConcurrentChange()
27 | {
28 | _source.AfterGet += () => _source.SourceProperty = 4;
29 |
30 | _source.SourceProperty = 3;
31 | int fetch = _computed.ComputedProperty;
32 |
33 | Assert.IsFalse(_computed.IsUpToDate, "The dependent is up to date after a concurrent change");
34 | }
35 |
36 | [TestMethod]
37 | public void ComputedHasOriginalValueAfterConcurrentChange()
38 | {
39 | _source.AfterGet += () => _source.SourceProperty = 4;
40 |
41 | _source.SourceProperty = 3;
42 | Assert.AreEqual(3, _computed.ComputedProperty);
43 | }
44 |
45 | [TestMethod]
46 | public void ComputedIsUpToDateAfterSecondGet()
47 | {
48 | Action concurrentChange = () => _source.SourceProperty = 4;
49 | _source.AfterGet += concurrentChange;
50 |
51 | _source.SourceProperty = 3;
52 | int fetch = _computed.ComputedProperty;
53 |
54 | _source.AfterGet -= concurrentChange;
55 |
56 | fetch = _computed.ComputedProperty;
57 |
58 | Assert.IsTrue(_computed.IsUpToDate, "The dependent is not up to date after the second get");
59 | }
60 |
61 | [TestMethod]
62 | public void ComputedHasModifiedValueAfterSecondGet()
63 | {
64 | Action concurrentChange = () => _source.SourceProperty = 4;
65 | _source.AfterGet += concurrentChange;
66 |
67 | _source.SourceProperty = 3;
68 | int fetch = _computed.ComputedProperty;
69 |
70 | _source.AfterGet -= concurrentChange;
71 |
72 | Assert.AreEqual(4, _computed.ComputedProperty);
73 | }
74 |
75 | [TestMethod]
76 | public void ComputedStillDependsUponPrecedent()
77 | {
78 | Action concurrentChange = () => _source.SourceProperty = 4;
79 | _source.AfterGet += concurrentChange;
80 |
81 | _source.SourceProperty = 3;
82 | int fetch = _computed.ComputedProperty;
83 |
84 | _source.AfterGet -= concurrentChange;
85 |
86 | fetch = _computed.ComputedProperty;
87 | _source.SourceProperty = 5;
88 |
89 | Assert.IsFalse(_computed.IsUpToDate, "The dependent no longer depends upon the precedent");
90 | }
91 |
92 | [TestMethod]
93 | public void ComputedGetsTheUltimateValue()
94 | {
95 | Action concurrentChange = () => _source.SourceProperty = 4;
96 | _source.AfterGet += concurrentChange;
97 |
98 | _source.SourceProperty = 3;
99 | int fetch = _computed.ComputedProperty;
100 |
101 | _source.AfterGet -= concurrentChange;
102 |
103 | fetch = _computed.ComputedProperty;
104 | _source.SourceProperty = 5;
105 |
106 | Assert.AreEqual(5, _computed.ComputedProperty);
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/DynamicSortOrderTest.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | #if NETFX_CORE
3 | using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
4 | #else
5 | using Microsoft.VisualStudio.TestTools.UnitTesting;
6 | #endif
7 | using Assisticant.UnitTest.ContactListData;
8 |
9 | namespace Assisticant.UnitTest
10 | {
11 | [TestClass]
12 | public class DynamicSortOrderTest
13 | {
14 | private ContactList _contactList;
15 | private ContactListViewModel _viewModel;
16 | private int _collectionChangedCount;
17 |
18 | [TestInitialize]
19 | public void Initialize()
20 | {
21 | _contactList = new ContactList();
22 | _viewModel = new ContactListViewModel(_contactList);
23 | _contactList.AddContact(new Contact() { FirstName = "Michael", LastName = "Perry" });
24 | _contactList.AddContact(new Contact() { FirstName = "Ada", LastName = "Lovelace" });
25 | _contactList.AddContact(new Contact() { FirstName = "Charles", LastName = "Babbage" });
26 |
27 | _collectionChangedCount = 0;
28 | _viewModel.ContactsCollectionChanged +=
29 | delegate
30 | {
31 | _collectionChangedCount++;
32 | };
33 | }
34 |
35 | [TestMethod]
36 | public void InitiallyNoEventFired()
37 | {
38 | _viewModel.SortOrder = ContactListSortOrder.FirstName;
39 | ContactViewModel firstByFirstName = _viewModel.Contacts.First();
40 | Assert.AreEqual(0, _collectionChangedCount);
41 | }
42 |
43 | [TestMethod]
44 | public void WhenOrderByFirstNameAndFirstNameChangesShouldNotify()
45 | {
46 | _viewModel.SortOrder = ContactListSortOrder.FirstName;
47 | ContactViewModel firstByFirstName = _viewModel.Contacts.First();
48 | _contactList.Contacts.First().FirstName = "George";
49 |
50 | Assert.AreEqual(1, _collectionChangedCount);
51 | }
52 |
53 | [TestMethod]
54 | public void WhenOrderByLastNameAndFirstNameChangesShouldNotNotify()
55 | {
56 | _viewModel.SortOrder = ContactListSortOrder.LastName;
57 | ContactViewModel firstByLastName = _viewModel.Contacts.First();
58 | _contactList.Contacts.First().FirstName = "George";
59 |
60 | Assert.AreEqual(0, _collectionChangedCount);
61 | }
62 |
63 | [TestMethod]
64 | public void WhenSortOrderChangedShouldNotify()
65 | {
66 | _viewModel.SortOrder = ContactListSortOrder.FirstName;
67 | ContactViewModel firstByFirstName = _viewModel.Contacts.First();
68 | _contactList.Contacts.First().FirstName = "George";
69 | ContactViewModel firstByFirstNameAgain = _viewModel.Contacts.First();
70 |
71 | _viewModel.SortOrder = ContactListSortOrder.LastName;
72 |
73 | Assert.AreEqual(2, _collectionChangedCount);
74 | }
75 |
76 | [TestMethod]
77 | public void WhenSortOrderChangesAndFirstNameChangesShouldNoLongerNotify()
78 | {
79 | _viewModel.SortOrder = ContactListSortOrder.FirstName;
80 | ContactViewModel firstByFirstName = _viewModel.Contacts.First();
81 | _contactList.Contacts.First().FirstName = "George";
82 | ContactViewModel firstByFirstNameAgain = _viewModel.Contacts.First();
83 |
84 | _viewModel.SortOrder = ContactListSortOrder.LastName;
85 | var firstByLastName = _viewModel.Contacts.First();
86 |
87 | _contactList.Contacts.First().FirstName = "Charles";
88 |
89 | Assert.AreEqual(2, _collectionChangedCount);
90 | }
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/Images/UnitTestLogo.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaellperry/Assisticant/1b8aae89f823ddc7fa4a3fa0e7df68a5bc0540bc/Assisticant.UnitTest/Images/UnitTestLogo.scale-100.png
--------------------------------------------------------------------------------
/Assisticant.UnitTest/Images/UnitTestSmallLogo.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaellperry/Assisticant/1b8aae89f823ddc7fa4a3fa0e7df68a5bc0540bc/Assisticant.UnitTest/Images/UnitTestSmallLogo.scale-100.png
--------------------------------------------------------------------------------
/Assisticant.UnitTest/Images/UnitTestSplashScreen.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaellperry/Assisticant/1b8aae89f823ddc7fa4a3fa0e7df68a5bc0540bc/Assisticant.UnitTest/Images/UnitTestSplashScreen.scale-100.png
--------------------------------------------------------------------------------
/Assisticant.UnitTest/Images/UnitTestStoreLogo.scale-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/michaellperry/Assisticant/1b8aae89f823ddc7fa4a3fa0e7df68a5bc0540bc/Assisticant.UnitTest/Images/UnitTestStoreLogo.scale-100.png
--------------------------------------------------------------------------------
/Assisticant.UnitTest/IndirectComputed.cs:
--------------------------------------------------------------------------------
1 | using Assisticant.Fields;
2 |
3 | namespace Assisticant.UnitTest
4 | {
5 | public class IndirectComputed
6 | {
7 | private DirectComputed _indermediateComputed;
8 |
9 | private Computed _property;
10 |
11 | public IndirectComputed(DirectComputed indermediateComputed)
12 | {
13 | _indermediateComputed = indermediateComputed;
14 | _property = new Computed(() => _indermediateComputed.ComputedProperty);
15 | }
16 |
17 | public int ComputedProperty
18 | {
19 | get { return _property; }
20 | }
21 |
22 | public bool IsUpToDate
23 | {
24 | get { return _property.IsUpToDate; }
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/IndirectComputedTest.cs:
--------------------------------------------------------------------------------
1 | #if NETFX_CORE
2 | using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
3 | #else
4 | using Microsoft.VisualStudio.TestTools.UnitTesting;
5 | #endif
6 |
7 | namespace Assisticant.UnitTest
8 | {
9 | [TestClass]
10 | public class IndirectComputedTest
11 | {
12 | public TestContext TestContext { get; set; }
13 |
14 | private SourceData _source;
15 | private DirectComputed _intermediateComputed;
16 | private IndirectComputed _computed;
17 |
18 | [TestInitialize]
19 | public void Initialize()
20 | {
21 | _source = new SourceData();
22 | _intermediateComputed = new DirectComputed(_source);
23 | _computed = new IndirectComputed(_intermediateComputed);
24 | }
25 |
26 | [TestMethod]
27 | public void ComputedIsInitiallyOutOfDate()
28 | {
29 | Assert.IsFalse(_computed.IsUpToDate, "The dependent is initially up to date");
30 | }
31 |
32 | [TestMethod]
33 | public void ComputedRemainsOutOfDateOnChange()
34 | {
35 | _source.SourceProperty = 3;
36 | Assert.IsFalse(_computed.IsUpToDate, "The dependent is up to date after change");
37 | }
38 |
39 | [TestMethod]
40 | public void ComputedIsUpdatedOnGet()
41 | {
42 | int fetch = _computed.ComputedProperty;
43 | Assert.IsTrue(_computed.IsUpToDate, "The dependent has not been updated");
44 | }
45 |
46 | [TestMethod]
47 | public void ComputedIsUpdatedAfterChangeOnGet()
48 | {
49 | _source.SourceProperty = 3;
50 | int fetch = _computed.ComputedProperty;
51 | Assert.IsTrue(_computed.IsUpToDate, "The dependent has not been updated");
52 | }
53 |
54 | [TestMethod]
55 | public void ComputedGetsValueFromItsPrecedent()
56 | {
57 | _source.SourceProperty = 3;
58 | Assert.AreEqual(3, _computed.ComputedProperty);
59 | }
60 |
61 | [TestMethod]
62 | public void ComputedIsOutOfDateAgainAfterChange()
63 | {
64 | _source.SourceProperty = 3;
65 | int fetch = _computed.ComputedProperty;
66 | _source.SourceProperty = 4;
67 | Assert.IsFalse(_computed.IsUpToDate, "The dependent did not go out of date");
68 | }
69 |
70 | [TestMethod]
71 | public void ComputedIsUpdatedAgainAfterChange()
72 | {
73 | _source.SourceProperty = 3;
74 | int fetch = _computed.ComputedProperty;
75 | _source.SourceProperty = 4;
76 | fetch = _computed.ComputedProperty;
77 | Assert.IsTrue(_computed.IsUpToDate, "The dependent did not get udpated");
78 | }
79 |
80 | [TestMethod]
81 | public void ComputedGetsValueFromItsPrecedentAgainAfterChange()
82 | {
83 | _source.SourceProperty = 3;
84 | int fetch = _computed.ComputedProperty;
85 | _source.SourceProperty = 4;
86 | Assert.AreEqual(4, _computed.ComputedProperty);
87 | }
88 |
89 | [TestMethod]
90 | public void PrecedentIsOnlyAskedOnce()
91 | {
92 | int getCount = 0;
93 | _source.AfterGet += () => ++getCount;
94 |
95 | _source.SourceProperty = 3;
96 | int fetch = _computed.ComputedProperty;
97 | fetch = _computed.ComputedProperty;
98 |
99 | Assert.AreEqual(1, getCount);
100 | }
101 |
102 | [TestMethod]
103 | public void PrecedentIsAskedAgainAfterChange()
104 | {
105 | int getCount = 0;
106 | _source.AfterGet += () => ++getCount;
107 |
108 | _source.SourceProperty = 3;
109 | int fetch = _computed.ComputedProperty;
110 | fetch = _computed.ComputedProperty;
111 | _source.SourceProperty = 4;
112 | fetch = _computed.ComputedProperty;
113 | fetch = _computed.ComputedProperty;
114 |
115 | Assert.AreEqual(2, getCount);
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/IndirectConcurrencyTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | #if NETFX_CORE
3 | using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
4 | #else
5 | using Microsoft.VisualStudio.TestTools.UnitTesting;
6 | #endif
7 |
8 | namespace Assisticant.UnitTest
9 | {
10 | [TestClass]
11 | public class IndirectConcurrencyTest
12 | {
13 | public TestContext TestContext { get; set; }
14 |
15 | private SourceData _source;
16 | private DirectComputed _intermediateComputed;
17 | private IndirectComputed _computed;
18 |
19 | [TestInitialize]
20 | public void Initialize()
21 | {
22 | _source = new SourceData();
23 | _intermediateComputed = new DirectComputed(_source);
24 | _computed = new IndirectComputed(_intermediateComputed);
25 | }
26 |
27 | [TestMethod]
28 | public void ComputedIsOutOfDateAfterConcurrentChange()
29 | {
30 | _source.AfterGet += () => _source.SourceProperty = 4;
31 |
32 | _source.SourceProperty = 3;
33 | int fetch = _computed.ComputedProperty;
34 |
35 | Assert.IsFalse(_computed.IsUpToDate, "The dependent is up to date after a concurrent change");
36 | }
37 |
38 | [TestMethod]
39 | public void ComputedHasOriginalValueAfterConcurrentChange()
40 | {
41 | _source.AfterGet += () => _source.SourceProperty = 4;
42 |
43 | _source.SourceProperty = 3;
44 | Assert.AreEqual(3, _computed.ComputedProperty);
45 | }
46 |
47 | [TestMethod]
48 | public void ComputedIsUpToDateAfterSecondGet()
49 | {
50 | Action concurrentChange = () => _source.SourceProperty = 4;
51 | _source.AfterGet += concurrentChange;
52 |
53 | _source.SourceProperty = 3;
54 | int fetch = _computed.ComputedProperty;
55 |
56 | _source.AfterGet -= concurrentChange;
57 |
58 | fetch = _computed.ComputedProperty;
59 |
60 | Assert.IsTrue(_computed.IsUpToDate, "The dependent is not up to date after the second get");
61 | }
62 |
63 | [TestMethod]
64 | public void ComputedHasModifiedValueAfterSecondGet()
65 | {
66 | Action concurrentChange = () => _source.SourceProperty = 4;
67 | _source.AfterGet += concurrentChange;
68 |
69 | _source.SourceProperty = 3;
70 | int fetch = _computed.ComputedProperty;
71 |
72 | _source.AfterGet -= concurrentChange;
73 |
74 | Assert.AreEqual(4, _computed.ComputedProperty);
75 | }
76 |
77 | [TestMethod]
78 | public void ComputedStillDependsUponPrecedent()
79 | {
80 | Action concurrentChange = () => _source.SourceProperty = 4;
81 | _source.AfterGet += concurrentChange;
82 |
83 | _source.SourceProperty = 3;
84 | int fetch = _computed.ComputedProperty;
85 |
86 | _source.AfterGet -= concurrentChange;
87 |
88 | fetch = _computed.ComputedProperty;
89 | _source.SourceProperty = 5;
90 |
91 | Assert.IsFalse(_computed.IsUpToDate, "The dependent no longer depends upon the precedent");
92 | }
93 |
94 | [TestMethod]
95 | public void ComputedGetsTheUltimateValue()
96 | {
97 | Action concurrentChange = () => _source.SourceProperty = 4;
98 | _source.AfterGet += concurrentChange;
99 |
100 | _source.SourceProperty = 3;
101 | int fetch = _computed.ComputedProperty;
102 |
103 | _source.AfterGet -= concurrentChange;
104 |
105 | fetch = _computed.ComputedProperty;
106 | _source.SourceProperty = 5;
107 |
108 | Assert.AreEqual(5, _computed.ComputedProperty);
109 | }
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/LargeListTest.cs:
--------------------------------------------------------------------------------
1 | using Assisticant.Collections;
2 | using Assisticant.Fields;
3 | using Assisticant.UnitTest.ContactListData;
4 | using Microsoft.VisualStudio.TestTools.UnitTesting;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.Linq;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 |
11 | namespace Assisticant.UnitTest
12 | {
13 | [TestClass]
14 | public class LargeListTest
15 | {
16 | [TestMethod]
17 | public void CanHaveManyPrecedents()
18 | {
19 | var contacts = new ObservableList(
20 | Enumerable.Range(0, 10000)
21 | .Select(i => new Contact()
22 | {
23 | FirstName = "FirstName" + i,
24 | LastName = "LastName" + i
25 | }));
26 |
27 | var sorted = new ComputedList(() =>
28 | from c in contacts
29 | orderby c.FirstName, c.LastName
30 | select c);
31 | Assert.AreEqual("FirstName100", sorted.ElementAt(3).FirstName);
32 |
33 | sorted.ElementAt(3).FirstName = "George";
34 | Assert.AreEqual("FirstName1000", sorted.ElementAt(3).FirstName);
35 | }
36 |
37 | class Projection
38 | {
39 | private readonly Observable _prefix;
40 | private readonly Contact _contact;
41 | private Computed _name;
42 |
43 | public Projection(Observable prefix, Contact contact)
44 | {
45 | _prefix = prefix;
46 | _contact = contact;
47 |
48 | _name = new Computed(() =>
49 | prefix.Value + _contact.FirstName + _contact.LastName);
50 | }
51 |
52 | public string Name
53 | {
54 | get { return _name; }
55 | }
56 | }
57 |
58 | [TestMethod]
59 | public void CanHaveManyComputeds()
60 | {
61 | var prefix = new Observable("Before");
62 |
63 | var contacts = new ObservableList(
64 | Enumerable.Range(0, 10000)
65 | .Select(i => new Contact()
66 | {
67 | FirstName = "FirstName" + i,
68 | LastName = "LastName" + i
69 | }));
70 |
71 | var projections = new ComputedList(() =>
72 | from c in contacts
73 | select new Projection(prefix, c));
74 | string dummy;
75 | foreach (var projection in projections)
76 | dummy = projection.Name;
77 | Assert.AreEqual("BeforeFirstName3LastName3", projections.ElementAt(3).Name);
78 |
79 | prefix.Value = "After";
80 | foreach (var projection in projections)
81 | dummy = projection.Name;
82 | Assert.AreEqual("AfterFirstName3LastName3", projections.ElementAt(3).Name);
83 | }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/MultithreadedData/AbstractThread.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Threading;
7 | #if NETFX_CORE
8 | using Windows.Foundation;
9 | using Windows.System.Threading;
10 | #endif
11 |
12 | namespace Assisticant.UnitTest.MultithreadedData
13 | {
14 | public abstract class AbstractThread
15 | {
16 | #if NETFX_CORE
17 | private IAsyncAction _asyncAction;
18 | #else
19 | private Thread _thread;
20 | #endif
21 |
22 | protected abstract void ThreadProc();
23 |
24 | public AbstractThread()
25 | {
26 | #if !NETFX_CORE
27 | _thread = new Thread(() => ThreadProc());
28 | #endif
29 | }
30 |
31 | public void Start()
32 | {
33 | #if NETFX_CORE
34 | _asyncAction = ThreadPool.RunAsync(wi => ThreadProc());
35 | #else
36 | _thread.Start();
37 | #endif
38 | }
39 |
40 | public void Join()
41 | {
42 | #if NETFX_CORE
43 | _asyncAction.AsTask().Wait();
44 | #else
45 | _thread.Join();
46 | #endif
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/MultithreadedData/SourceThread.cs:
--------------------------------------------------------------------------------
1 | using System.Threading;
2 | using Assisticant.Fields;
3 |
4 | namespace Assisticant.UnitTest.MultithreadedData
5 | {
6 | public class SourceThread : AbstractThread
7 | {
8 | public const int MaxValue = 10000;
9 |
10 | private Observable _value = new Observable();
11 |
12 | protected override void ThreadProc()
13 | {
14 | for (int i = 0; i <= MaxValue; i++)
15 | {
16 | Value = i;
17 | }
18 | }
19 |
20 | public int Value
21 | {
22 | get
23 | {
24 | lock (this)
25 | return _value;
26 | }
27 | set
28 | {
29 | lock (this)
30 | _value.Value = value;
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/MultithreadedData/TargetThread.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Threading;
4 | using Assisticant.Fields;
5 |
6 | namespace Assisticant.UnitTest.MultithreadedData
7 | {
8 | public class TargetThread : AbstractThread
9 | {
10 | private SourceThread[] _sources;
11 | private Computed _total;
12 |
13 | public TargetThread(SourceThread[] sources)
14 | {
15 | _sources = sources;
16 | _total = new Computed(() => _sources.Sum(source => source.Value));
17 | }
18 |
19 | public int Total
20 | {
21 | get
22 | {
23 | lock (this)
24 | return _total;
25 | }
26 | }
27 |
28 | protected override void ThreadProc()
29 | {
30 | for (int i = 0; i < SourceThread.MaxValue; i++)
31 | {
32 | int total = Total;
33 | if (total < 0) // This will never happen, but we need to ensure that the property get is not optimized out.
34 | throw new InvalidOperationException();
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/MultithreadedTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | #if NETFX_CORE
6 | using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
7 | #else
8 | using Microsoft.VisualStudio.TestTools.UnitTesting;
9 | #endif
10 | using Assisticant.UnitTest.MultithreadedData;
11 |
12 | namespace Assisticant.UnitTest
13 | {
14 | [TestClass]
15 | public class MultithreadedTest
16 | {
17 | [TestMethod]
18 | public void IsThreadSafe()
19 | {
20 | const int ThreadCount = 10;
21 |
22 | // Start source threads.
23 | SourceThread[] sources = new SourceThread[ThreadCount];
24 | for (int i = 0; i < ThreadCount; i++)
25 | {
26 | sources[i] = new SourceThread();
27 | sources[i].Start();
28 | }
29 |
30 | // Start target threads.
31 | TargetThread[] targets = new TargetThread[ThreadCount];
32 | for (int i = 0; i < ThreadCount; i++)
33 | {
34 | targets[i] = new TargetThread(sources);
35 | targets[i].Start();
36 | }
37 |
38 | // Wait for all threads to finish.
39 | for (int i = 0; i < ThreadCount; i++)
40 | {
41 | sources[i].Join();
42 | targets[i].Join();
43 | }
44 |
45 | // All targets are in the correct state.
46 | for (int i = 0; i < ThreadCount; i++)
47 | {
48 | Assert.AreEqual(ThreadCount * SourceThread.MaxValue, targets[i].Total);
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/NotificationTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | #if NETFX_CORE
3 | using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
4 | #else
5 | using Microsoft.VisualStudio.TestTools.UnitTesting;
6 | #endif
7 |
8 | namespace Assisticant.UnitTest
9 | {
10 | public class NotifyingObservable : Observable
11 | {
12 | public event Action OnGainComputed;
13 | public event Action OnLoseComputed;
14 |
15 | protected override void GainDependent()
16 | {
17 | if (OnGainComputed != null)
18 | OnGainComputed();
19 | }
20 |
21 | protected override void LoseDependent()
22 | {
23 | if (OnLoseComputed != null)
24 | OnLoseComputed();
25 | }
26 | }
27 |
28 | [TestClass]
29 | public class NotificationTest
30 | {
31 | private bool _gained;
32 | private bool _lost;
33 | private NotifyingObservable _observable;
34 | private Computed _computed;
35 | private Computed _secondComputed;
36 |
37 | [TestInitialize]
38 | public void Initialize()
39 | {
40 | _gained = false;
41 | _observable = new NotifyingObservable();
42 | _observable.OnGainComputed += () => { _gained = true; };
43 | _observable.OnLoseComputed += () => { _lost = true; };
44 | _computed = new Computed(() => { _observable.OnGet(); });
45 | _secondComputed = new Computed(() => { _observable.OnGet(); });
46 | }
47 |
48 | [TestMethod]
49 | public void DoesNotGainComputedOnCreation()
50 | {
51 | Assert.IsFalse(_gained, "The observable should not have gained a dependent.");
52 | }
53 |
54 | [TestMethod]
55 | public void GainsComputedOnFirstUse()
56 | {
57 | _computed.OnGet();
58 | Assert.IsTrue(_gained, "The observable should have gained a dependent.");
59 | }
60 |
61 | [TestMethod]
62 | public void DoesNotGainComputedOnSecondUse()
63 | {
64 | _computed.OnGet();
65 | _gained = false;
66 | _secondComputed.OnGet();
67 | Assert.IsFalse(_gained, "The observable should not have gained a dependent.");
68 | }
69 |
70 | [TestMethod]
71 | public void DoesNotLoseComputedOnCreation()
72 | {
73 | Assert.IsFalse(_lost, "The observable should not have lost a dependent.");
74 | }
75 |
76 | [TestMethod]
77 | public void DoesNotLoseComputedOnFirstUse()
78 | {
79 | _computed.OnGet();
80 | Assert.IsFalse(_lost, "The observable should not have lost a dependent.");
81 | }
82 |
83 | [TestMethod]
84 | public void LosesComputedWhenChanging()
85 | {
86 | _computed.OnGet();
87 | _observable.OnSet();
88 | Assert.IsTrue(_lost, "The observable should have lost a dependent.");
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/Package.appxmanifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Assisticant.UnitTest.Universal
6 | rv
7 | Images\UnitTestStoreLogo.png
8 | Assisticant.UnitTest.Universal
9 |
10 |
11 | 6.3.0
12 | 6.3.0
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Assisticant.UnitTest")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("Assisticant.UnitTest")]
13 | [assembly: AssemblyCopyright("Copyright © 2013")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("93f186f5-6e37-40b1-8767-4c2305929548")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/SortedCollectionTest.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | #if NETFX_CORE
3 | using Microsoft.VisualStudio.TestPlatform.UnitTestFramework;
4 | #else
5 | using Microsoft.VisualStudio.TestTools.UnitTesting;
6 | #endif
7 | using Assisticant.UnitTest.ContactListData;
8 |
9 | namespace Assisticant.UnitTest
10 | {
11 | [TestClass]
12 | public class SortedCollectionTest
13 | {
14 | private ContactList _contactList;
15 | private ContactListViewModel _viewModel;
16 | private int _collectionChangedCount;
17 |
18 | [TestInitialize]
19 | public void Initialize()
20 | {
21 | _contactList = new ContactList();
22 | _viewModel = new ContactListViewModel(_contactList);
23 | _contactList.AddContact(new Contact() { FirstName = "Michael", LastName = "Perry" });
24 | _contactList.AddContact(new Contact() { FirstName = "Ada", LastName = "Lovelace" });
25 | _contactList.AddContact(new Contact() { FirstName = "Charles", LastName = "Babbage" });
26 |
27 | _collectionChangedCount = 0;
28 | _viewModel.ContactsCollectionChanged +=
29 | delegate
30 | {
31 | _collectionChangedCount++;
32 | };
33 | }
34 |
35 | [TestMethod]
36 | public void WhenSortByFirstNameShouldSortViewModel()
37 | {
38 | _viewModel.SortOrder = ContactListSortOrder.FirstName;
39 |
40 | ContactViewModel contactViewModel = _viewModel.Contacts.First();
41 | Assert.AreEqual("Ada Lovelace", contactViewModel.FullName);
42 | }
43 |
44 | [TestMethod]
45 | public void WhenViewModelIsCreatedShouldNotNotifyCollectionChanged()
46 | {
47 | _viewModel.SortOrder = ContactListSortOrder.FirstName;
48 | ContactViewModel firstByFirstName = _viewModel.Contacts.First();
49 | Assert.AreEqual(0, _collectionChangedCount);
50 | }
51 |
52 | [TestMethod]
53 | public void WhenSortOrderIsChangedShouldNotifyViewModelCollectionChanged()
54 | {
55 | _viewModel.SortOrder = ContactListSortOrder.FirstName;
56 | ContactViewModel firstByFirstName = _viewModel.Contacts.First();
57 |
58 | _viewModel.SortOrder = ContactListSortOrder.LastName;
59 | Assert.AreEqual(1, _collectionChangedCount);
60 | Assert.AreEqual("Charles Babbage", _viewModel.Contacts.First().FullName);
61 | }
62 |
63 | [TestMethod]
64 | public void WhenSortOrderIsChangedButNotDifferentWeShouldNotGetACollectionChanged()
65 | {
66 | ContactViewModel defaultOrder = _viewModel.Contacts.First();
67 | _viewModel.SortOrder = ContactListSortOrder.FirstName;
68 |
69 | ContactViewModel firstByFirstName = _viewModel.Contacts.First();
70 | _viewModel.SortOrder = ContactListSortOrder.FirstName;
71 |
72 | Assert.AreEqual(1, _collectionChangedCount);
73 |
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/SourceData.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading;
3 | #if NETFX_CORE
4 | using Windows.System.Threading;
5 | #endif
6 | using Assisticant.Fields;
7 |
8 | namespace Assisticant.UnitTest
9 | {
10 | public class SourceData
11 | {
12 | private Observable _sourceProperty = new Observable();
13 | private AutoResetEvent _continue = new AutoResetEvent(false);
14 |
15 | public int SourceProperty
16 | {
17 | get
18 | {
19 | int result = _sourceProperty;
20 | #if NETFX_CORE
21 | var ignored = ThreadPool.RunAsync(wi =>
22 | #else
23 | ThreadPool.QueueUserWorkItem(o =>
24 | #endif
25 | {
26 | if (AfterGet != null)
27 | AfterGet();
28 | _continue.Set();
29 | });
30 | _continue.WaitOne();
31 | return result;
32 | }
33 | set { _sourceProperty.Value = value; }
34 | }
35 |
36 | public event Action AfterGet;
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Assisticant.UnitTest/packages.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/Assisticant/AssisticantExtensions.cs:
--------------------------------------------------------------------------------
1 | /**********************************************************************
2 | *
3 | * Update Controls .NET
4 | * Copyright 2010 Michael L Perry
5 | * MIT License
6 | *
7 | * http://updatecontrols.net
8 | * http://www.codeplex.com/updatecontrols/
9 | *
10 | **********************************************************************/
11 |
12 | using System;
13 | using System.Collections.Generic;
14 |
15 | namespace Assisticant
16 | {
17 | public static class AssisticantExtensions
18 | {
19 | ///
20 | /// Moves all objects into a new recycle bin, from which they can be extracted.
21 | ///
22 | /// A collection of objects to add to the bin.
23 | ///
24 | /// After the objects are added to the bin, the collection
25 | /// is cleared. Then it can be repopulated by extraction from
26 | /// the bin.
27 | ///
28 | public static RecycleBin Recycle(this ICollection collection)
29 | {
30 | RecycleBin bin = new RecycleBin(collection);
31 |
32 | if (collection != null)
33 | collection.Clear();
34 |
35 | return bin;
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Assisticant/Binding/BindingManager.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Assisticant.Fields;
4 |
5 | namespace Assisticant.Binding
6 | {
7 | ///
8 | /// Manages all data bindings for a view. Be sure to Initialize on load, Bind properties when
9 | /// the view is displayed, and Unbind when the view dissapears.
10 | ///
11 | public class BindingManager
12 | {
13 | struct SubscriptionPair
14 | {
15 | public ComputedSubscription Output;
16 | public IInputSubscription Input;
17 | }
18 |
19 | private List _subscriptions = new List();
20 |
21 | ///
22 | /// Initializes a new instance of the class.
23 | ///
24 | public BindingManager()
25 | {
26 | }
27 |
28 | ///
29 | /// Bind the results of a function to an action.
30 | ///
31 | /// The function that computes a value to output.
32 | /// The action to perform when new output is computed.
33 | /// The 1st type parameter.
34 | public void Bind(Func function, Action action)
35 | {
36 | _subscriptions.Add (new SubscriptionPair
37 | {
38 | Output = new Computed (function).Subscribe (action)
39 | });
40 | }
41 |
42 | ///
43 | /// Bind a custom input subscription.
44 | ///
45 | /// The custom input subscription.
46 | public void Bind(IInputSubscription input)
47 | {
48 | input.Subscribe ();
49 | _subscriptions.Add (new SubscriptionPair
50 | {
51 | Input = input
52 | });
53 | }
54 |
55 | ///
56 | /// Bind the results of a function to an action, and a custom input subscription.
57 | ///
58 | /// The function that computes a value to output.
59 | /// The action to perform when new output is computed.
60 | /// The custom input subscription.
61 | /// The 1st type parameter.
62 | public void Bind(Func function, Action action, IInputSubscription input)
63 | {
64 | input.Subscribe ();
65 | _subscriptions.Add (new SubscriptionPair
66 | {
67 | Output = new Computed (function).Subscribe (action),
68 | Input = input
69 | });
70 | }
71 |
72 | ///
73 | /// Unbind all bindings. Call this method when the view disappers.
74 | ///
75 | public void Unbind()
76 | {
77 | foreach (var subscription in _subscriptions) {
78 | if (subscription.Output != null)
79 | subscription.Output.Unsubscribe ();
80 | if (subscription.Input != null)
81 | subscription.Input.Unsubscribe ();
82 | }
83 | _subscriptions.Clear();
84 | }
85 | }
86 | }
87 |
88 |
--------------------------------------------------------------------------------
/Assisticant/Binding/IDisplayDataConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Assisticant.Binding
4 | {
5 | public interface IDisplayDataConverter
6 | {
7 | TDisplay ConvertOutput(TData data);
8 | TData ConvertInput(TDisplay display);
9 | }
10 | }
11 |
12 |
--------------------------------------------------------------------------------
/Assisticant/Binding/IInputSubscription.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Assisticant.Binding
4 | {
5 | public interface IInputSubscription
6 | {
7 | void Subscribe();
8 | void Unsubscribe();
9 | }
10 | }
11 |
12 |
--------------------------------------------------------------------------------
/Assisticant/BindingInterceptor.cs:
--------------------------------------------------------------------------------
1 | using Assisticant.Metas;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Assisticant
10 | {
11 | public class BindingInterceptor
12 | {
13 | public static BindingInterceptor Current = new BindingInterceptor();
14 |
15 | public virtual object GetValue(MemberSlot member) { return member.GetValue(); }
16 | public virtual void SetValue(MemberSlot member, object value) { member.SetValue(value); }
17 | public virtual void UpdateValue(MemberSlot member) { member.UpdateValue(); }
18 | public virtual void Execute(MethodCommand command, object parameter) { command.ContinueExecute(parameter); }
19 | public virtual bool CanExecute(MethodCommand command, object parameter) { return command.ContinueCanExecute(parameter); }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Assisticant/Collections/ComputedList.cs:
--------------------------------------------------------------------------------
1 | /**********************************************************************
2 | *
3 | * Update Controls .NET
4 | * Copyright 2011 Michael L Perry
5 | * MIT License
6 | *
7 | * http://updatecontrols.net
8 | * http://www.codeplex.com/updatecontrols/
9 | *
10 | **********************************************************************/
11 |
12 | using System;
13 | using System.Collections.Generic;
14 |
15 | namespace Assisticant.Collections
16 | {
17 | public class ComputedList : IEnumerable, IReadOnlyList
18 | {
19 | private readonly Func> _computeCollection;
20 |
21 | private List _list = new List();
22 | private Computed _computedSentry;
23 |
24 | public ComputedList(Func> computeCollection)
25 | {
26 | _computeCollection = computeCollection;
27 |
28 | _computedSentry = new NamedComputed(MemoizedTypeName>.GenericName(),
29 | delegate {
30 | using (var bin = new RecycleBin(_list))
31 | {
32 | _list.Clear();
33 |
34 | var collection = computeCollection();
35 | if (collection != null)
36 | foreach (T item in collection)
37 | _list.Add(bin.Extract(item));
38 | }
39 | });
40 | }
41 |
42 | public int IndexOf(T item)
43 | {
44 | _computedSentry.OnGet();
45 | return _list.IndexOf(item);
46 | }
47 |
48 | public T this[int index]
49 | {
50 | get
51 | {
52 | _computedSentry.OnGet();
53 | return _list[index];
54 | }
55 | }
56 |
57 | public bool Contains(T item)
58 | {
59 | _computedSentry.OnGet();
60 | return _list.Contains(item);
61 | }
62 |
63 | public void CopyTo(T[] array, int arrayIndex)
64 | {
65 | _computedSentry.OnGet();
66 | _list.CopyTo(array, arrayIndex);
67 | }
68 |
69 | public int Count
70 | {
71 | get { _computedSentry.OnGet(); return _list.Count; }
72 | }
73 |
74 | public IEnumerator GetEnumerator()
75 | {
76 | _computedSentry.OnGet();
77 | return _list.GetEnumerator();
78 | }
79 |
80 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
81 | {
82 | _computedSentry.OnGet();
83 | return ((System.Collections.IEnumerable)_list).GetEnumerator();
84 | }
85 |
86 | public Computed ComputedSentry
87 | {
88 | get { return _computedSentry; }
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/Assisticant/Collections/Impl/UpdateCollectionHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 |
7 | namespace Assisticant.Collections.Impl
8 | {
9 | /// Helper structure used by ComputedDictionary and
10 | /// ObservableDictionary to represent the "Keys" and "Values" members.
11 | ///
12 | /// If you save a reference to the Keys or Values property of ,
13 | /// the observable sentry should be informed when that collection is accessed.
14 | /// This helper class ensures that the sentry is notified.
15 | ///
16 | /// For , this class is even more
17 | /// important. Whenever ComputedDictionary is updated, a new dictionary is
18 | /// created to hold the updated content, so the Keys and Values collections
19 | /// change frequently. This wrapper ensure that you do not accidentally hold
20 | /// a reference to an out-of-date version of the Keys or Values collection.
21 | /// It also ensures that the dictionary is updated if necessary when it is
22 | /// accessed through the Keys or Values collection.
23 | ///
24 | public struct UpdateCollectionHelper : ICollection, ICollection
25 | {
26 | readonly Func> _get;
27 |
28 | public UpdateCollectionHelper(Func> getCollection)
29 | {
30 | _get = getCollection;
31 | }
32 |
33 | public void Add(T item)
34 | {
35 | throw new NotSupportedException();
36 | }
37 | public void Clear()
38 | {
39 | throw new NotSupportedException();
40 | }
41 | public bool Contains(T item)
42 | {
43 | return _get().Contains(item);
44 | }
45 | public void CopyTo(T[] array, int arrayIndex)
46 | {
47 | _get().CopyTo(array, arrayIndex);
48 | }
49 |
50 | public void CopyTo(Array array, int index)
51 | {
52 | _get().CopyTo((T[])array, index);
53 | }
54 |
55 | public int Count
56 | {
57 | get { return _get().Count; }
58 | }
59 |
60 | public bool IsSynchronized => false;
61 | public object SyncRoot => this;
62 |
63 | public bool IsReadOnly
64 | {
65 | get { return true; }
66 | }
67 | public bool Remove(T item)
68 | {
69 | throw new NotSupportedException();
70 | }
71 |
72 | public IEnumerator GetEnumerator()
73 | {
74 | return _get().GetEnumerator();
75 | }
76 |
77 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
78 | {
79 | return _get().GetEnumerator();
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Assisticant/Collections/ObservableList.cs:
--------------------------------------------------------------------------------
1 | /**********************************************************************
2 | *
3 | * Update Controls .NET
4 | * Copyright 2010 Michael L Perry
5 | * MIT License
6 | *
7 | * http://updatecontrols.net
8 | * http://www.codeplex.com/updatecontrols/
9 | *
10 | **********************************************************************/
11 |
12 | using System;
13 | using System.Collections;
14 | using System.Collections.Generic;
15 |
16 | namespace Assisticant.Collections
17 | {
18 | public class ObservableList : IList, IList, IReadOnlyList
19 | {
20 | private List _list;
21 | private Observable _indList = new NamedObservable(MemoizedTypeName>.GenericName());
22 |
23 | public ObservableList()
24 | {
25 | _list = new List();
26 | }
27 |
28 | public ObservableList(IEnumerable collection)
29 | {
30 | _list = new List(collection);
31 | }
32 |
33 | public int IndexOf(T item)
34 | {
35 | _indList.OnGet();
36 | return _list.IndexOf(item);
37 | }
38 |
39 | public void Insert(int index, T item)
40 | {
41 | _indList.OnSet();
42 | _list.Insert(index, item);
43 | }
44 |
45 | public void RemoveAt(int index)
46 | {
47 | _indList.OnSet();
48 | _list.RemoveAt(index);
49 | }
50 |
51 | public T this[int index]
52 | {
53 | get
54 | {
55 | _indList.OnGet();
56 | return _list[index];
57 | }
58 | set
59 | {
60 | _indList.OnSet();
61 | _list[index] = value;
62 | }
63 | }
64 |
65 | public void Add(T item)
66 | {
67 | _indList.OnSet();
68 | _list.Add(item);
69 | }
70 |
71 | public void AddRange(IEnumerable items)
72 | {
73 | _indList.OnSet();
74 | _list.AddRange(items);
75 | }
76 |
77 | public void Clear()
78 | {
79 | _indList.OnSet();
80 | _list.Clear();
81 | }
82 |
83 | public bool Contains(T item)
84 | {
85 | _indList.OnGet();
86 | return _list.Contains(item);
87 | }
88 |
89 | public void CopyTo(T[] array, int arrayIndex)
90 | {
91 | _indList.OnGet();
92 | _list.CopyTo(array, arrayIndex);
93 | }
94 |
95 | public int Count
96 | {
97 | get { _indList.OnGet(); return _list.Count; }
98 | }
99 |
100 | public bool IsReadOnly
101 | {
102 | get { return false; }
103 | }
104 |
105 | public bool Remove(T item)
106 | {
107 | _indList.OnSet();
108 | return _list.Remove(item);
109 | }
110 |
111 | public IEnumerator GetEnumerator()
112 | {
113 | _indList.OnGet();
114 | return _list.GetEnumerator();
115 | }
116 |
117 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
118 | {
119 | _indList.OnGet();
120 | return ((System.Collections.IEnumerable)_list).GetEnumerator();
121 | }
122 |
123 | bool ICollection.IsSynchronized { get { return false; } }
124 | object ICollection.SyncRoot { get { return this; } }
125 | bool IList.IsFixedSize { get { return false; } }
126 | object IList.this[int index] { get { return this[index]; } set { this[index] = (T)value; } }
127 |
128 | int IList.Add(object value) { Add((T)value); return Count - 1; }
129 | bool IList.Contains(object value) { return Contains((T)value); }
130 | int IList.IndexOf(object value) { return IndexOf((T)value); }
131 | void IList.Insert(int index, object value) { Insert(index, (T)value); }
132 | void IList.Remove(object value) { Remove((T)value); }
133 | void ICollection.CopyTo(Array array, int index) { CopyTo((T[])array, index); }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/Assisticant/ComputedJob.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 |
6 | namespace Assisticant
7 | {
8 | public class ComputedJob : IDisposable
9 | {
10 | Computed _computed;
11 | bool _running;
12 |
13 | public ComputedJob(Action action)
14 | {
15 | _computed = new Computed(action);
16 | _computed.Invalidated += () => UpdateScheduler.ScheduleUpdate(UpdateNow);
17 | }
18 |
19 | public void Start()
20 | {
21 | if (_computed == null)
22 | throw new InvalidOperationException("Cannot restart ComputedJob");
23 | _running = true;
24 | UpdateScheduler.ScheduleUpdate(UpdateNow);
25 | }
26 |
27 | public void Stop()
28 | {
29 | _running = false;
30 | _computed.Dispose();
31 | _computed = null;
32 | }
33 |
34 | public void Dispose() { Stop(); }
35 |
36 | private void UpdateNow()
37 | {
38 | if (_running)
39 | _computed.OnGet();
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Assisticant/Descriptors/PlatformProxy.NotifyDataErrorInfo.cs:
--------------------------------------------------------------------------------
1 | using Assisticant.Validation;
2 | using System;
3 | using System.Collections;
4 | using System.ComponentModel;
5 | using System.Linq;
6 |
7 | namespace Assisticant.Descriptors
8 | {
9 | public abstract partial class PlatformProxy : INotifyDataErrorInfo
10 | {
11 | private IValidationRules _validator;
12 |
13 | public event EventHandler ErrorsChanged;
14 |
15 | partial void PlatformProxy_NotifyDataErrorInfo()
16 | {
17 | var validation = Instance as IValidation;
18 | _validator = validation?.Rules;
19 | if (_validator != null)
20 | {
21 | _validator.ErrorsChanged += Validator_ErrorsChanged;
22 | }
23 | }
24 |
25 | public bool HasErrors => _validator?.HasErrors ?? false;
26 |
27 | public IEnumerable GetErrors(string propertyName)
28 | {
29 | return _validator?.GetErrors(propertyName) ?? Enumerable.Empty();
30 | }
31 |
32 | private void Validator_ErrorsChanged(string propertyName)
33 | {
34 | ErrorsChanged?.Invoke(this, new DataErrorsChangedEventArgs(propertyName));
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Assisticant/Descriptors/PlatformProxy.cs:
--------------------------------------------------------------------------------
1 | using Assisticant.Metas;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.ComponentModel;
5 | using System.Diagnostics;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace Assisticant.Descriptors
11 | {
12 | public abstract partial class PlatformProxy : ViewProxy, IDataErrorInfo
13 | {
14 | public string Error
15 | {
16 | get
17 | {
18 | var errorInfo = Instance as IDataErrorInfo;
19 | return errorInfo != null ? errorInfo.Error : null;
20 | }
21 | }
22 |
23 | public string this[string columnName]
24 | {
25 | get
26 | {
27 | var errorInfo = Instance as IDataErrorInfo;
28 | return errorInfo != null ? errorInfo[columnName] : null;
29 | }
30 | }
31 |
32 | protected PlatformProxy(object instance, ProxyTypeDescriptor descriptor)
33 | : base(instance, descriptor.Meta)
34 | {
35 | PlatformProxy_NotifyDataErrorInfo();
36 | }
37 |
38 | public abstract ProxyTypeDescriptor GetTypeDescriptor();
39 |
40 | public override ViewProxy WrapObject(object value)
41 | {
42 | if (value == null)
43 | return null;
44 | return (PlatformProxy)Activator.CreateInstance(typeof(PlatformProxy<>).MakeGenericType(value.GetType()), value);
45 | }
46 |
47 | partial void PlatformProxy_NotifyDataErrorInfo();
48 | }
49 |
50 | [TypeDescriptionProvider(typeof(ProxyDescriptionProvider))]
51 | public sealed class PlatformProxy : PlatformProxy
52 | {
53 | public static readonly ProxyTypeDescriptor TypeDescriptor = new ProxyTypeDescriptor(typeof(TViewModel));
54 |
55 | public PlatformProxy(object instance)
56 | : base(instance, TypeDescriptor)
57 | {
58 | }
59 |
60 | public override ProxyTypeDescriptor GetTypeDescriptor()
61 | {
62 | return TypeDescriptor;
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Assisticant/Descriptors/ProxyDescriptionProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Assisticant.Descriptors
10 | {
11 | public class ProxyDescriptionProvider : TypeDescriptionProvider
12 | {
13 | public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
14 | {
15 | var objectInstance = instance as PlatformProxy;
16 | if (objectInstance != null)
17 | return objectInstance.GetTypeDescriptor();
18 |
19 | if (objectType.IsGenericType && objectType.GetGenericTypeDefinition() == typeof(PlatformProxy<>))
20 | {
21 | var field = objectType.GetField("TypeDescriptor", BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly);
22 | return (ICustomTypeDescriptor)field.GetValue(null);
23 | }
24 |
25 | return new ProxyTypeDescriptor(objectType);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/Assisticant/Descriptors/ProxyEventDescriptor.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Linq;
5 | using System.Reflection;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Assisticant.Descriptors
10 | {
11 | public class ProxyEventDescriptor : EventDescriptor
12 | {
13 | readonly EventInfo _eventInfo;
14 |
15 | public ProxyEventDescriptor(EventInfo eventInfo)
16 | : base(eventInfo.Name, null)
17 | {
18 | _eventInfo = eventInfo;
19 | }
20 |
21 | public override void AddEventHandler(object proxy, Delegate value)
22 | {
23 | // Add the event handler to the wrapped object.
24 | _eventInfo.AddEventHandler(Unwrap(proxy), value);
25 | }
26 |
27 | public override void RemoveEventHandler(object proxy, Delegate value)
28 | {
29 | // Remove the event handler from the wrapped object.
30 | _eventInfo.RemoveEventHandler(Unwrap(proxy), value);
31 | }
32 |
33 | public override Type ComponentType
34 | {
35 | get { return _eventInfo.DeclaringType; }
36 | }
37 |
38 | public override Type EventType
39 | {
40 | get { return _eventInfo.EventHandlerType; }
41 | }
42 |
43 | public override bool IsMulticast
44 | {
45 | get { return _eventInfo.IsMulticast; }
46 | }
47 |
48 | private static object Unwrap(object proxy)
49 | {
50 | return ((PlatformProxy)proxy).Instance;
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Assisticant/Descriptors/ProxyPropertyDescriptor.cs:
--------------------------------------------------------------------------------
1 | using Assisticant.Metas;
2 | using System;
3 | using System.Collections;
4 | using System.Collections.Generic;
5 | using System.ComponentModel;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace Assisticant.Descriptors
11 | {
12 | public class ProxyPropertyDescriptor : PropertyDescriptor
13 | {
14 | readonly ProxyTypeDescriptor _owner;
15 | readonly MemberMeta _meta;
16 | readonly Type _exposedType;
17 |
18 | public override Type ComponentType
19 | {
20 | get { return _owner.ProxyType; }
21 | }
22 |
23 | public override Type PropertyType
24 | {
25 | get { return _exposedType; }
26 | }
27 |
28 | public override bool IsReadOnly
29 | {
30 | get { return !_meta.CanWrite; }
31 | }
32 |
33 | public ProxyPropertyDescriptor(ProxyTypeDescriptor owner, MemberMeta meta)
34 | : base(meta.Name, null)
35 | {
36 | _owner = owner;
37 | _meta = meta;
38 | if (!meta.IsViewModel)
39 | _exposedType = meta.MemberType;
40 | else if (typeof(IEnumerable).IsAssignableFrom(meta.MemberType))
41 | _exposedType = typeof(IEnumerable);
42 | else
43 | _exposedType = typeof(object);
44 | }
45 |
46 | public override object GetValue(object proxy)
47 | {
48 | return BindingInterceptor.Current.GetValue(GetSlot(proxy));
49 | }
50 |
51 | public override void SetValue(object proxy, object value)
52 | {
53 | BindingInterceptor.Current.SetValue(GetSlot(proxy), value);
54 | }
55 |
56 | public override bool CanResetValue(object proxy)
57 | {
58 | return false;
59 | }
60 |
61 | public override void ResetValue(object proxy)
62 | {
63 | }
64 |
65 | public override bool ShouldSerializeValue(object proxy)
66 | {
67 | return false;
68 | }
69 |
70 | MemberSlot GetSlot(object proxy)
71 | {
72 | return ((ViewProxy)proxy).LookupSlot(_meta);
73 | }
74 |
75 | public override string ToString()
76 | {
77 | return _meta.ToString();
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Assisticant/Descriptors/ProxyTypeDescriptor.cs:
--------------------------------------------------------------------------------
1 | using Assisticant.Metas;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.ComponentModel;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace Assisticant.Descriptors
10 | {
11 | public class ProxyTypeDescriptor : CustomTypeDescriptor
12 | {
13 | public readonly TypeMeta Meta;
14 | readonly ProxyPropertyDescriptor[] _properties;
15 | readonly PropertyDescriptorCollection _propertyCollection;
16 | readonly EventDescriptorCollection _events;
17 | public readonly Type ProxyType;
18 |
19 | public ProxyTypeDescriptor(Type type)
20 | {
21 | Meta = TypeMeta.Get(type);
22 | ProxyType = typeof(PlatformProxy<>).MakeGenericType(type);
23 | _properties = Meta.Members.Select(m => new ProxyPropertyDescriptor(this, m)).ToArray();
24 | _propertyCollection = new PropertyDescriptorCollection(_properties);
25 | _events = new EventDescriptorCollection(type.GetEvents().Select(e => new ProxyEventDescriptor(e)).ToArray());
26 | }
27 |
28 | public override PropertyDescriptorCollection GetProperties()
29 | {
30 | return _propertyCollection;
31 | }
32 |
33 | public override EventDescriptorCollection GetEvents()
34 | {
35 | return _events;
36 | }
37 |
38 | public override EventDescriptorCollection GetEvents(Attribute[] attributes)
39 | {
40 | return _events;
41 | }
42 |
43 | public override string ToString()
44 | {
45 | return Meta.Type.Name;
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Assisticant/Fields/Computed.cs:
--------------------------------------------------------------------------------
1 | /**********************************************************************
2 | *
3 | * Update Controls .NET
4 | * Copyright 2011 Michael L Perry
5 | * MIT License
6 | *
7 | * This class based on a contribution by David Piepgrass.
8 | *
9 | * http://updatecontrols.net
10 | * http://www.codeplex.com/updatecontrols/
11 | *
12 | **********************************************************************/
13 |
14 | using System;
15 | using System.ComponentModel;
16 |
17 | namespace Assisticant.Fields
18 | {
19 | public class Computed : NamedComputed
20 | {
21 | protected internal T _value;
22 | protected Func _computeValue;
23 |
24 | public Computed(Func compute) : base((string)null, null)
25 | {
26 | base._update = Update; _computeValue = compute;
27 | }
28 | public Computed(string name, Func compute) : base(name, null)
29 | {
30 | base._update = Update; _computeValue = compute;
31 | }
32 |
33 | protected void Update()
34 | {
35 | _value = _computeValue();
36 | // TODO: don't propagate updates when _value did not change.
37 | // T oldValue = _value;
38 | // _value = _computeValue();
39 | // return _value == null ? oldValue != null : !_value.Equals(oldValue);
40 | }
41 |
42 | public T Value
43 | {
44 | get { base.OnGet(); return _value; }
45 | }
46 | public static implicit operator T(Computed computed)
47 | {
48 | return computed.Value;
49 | }
50 |
51 | public ComputedSubscription Subscribe(Action whenChanged)
52 | {
53 | return new ComputedSubscription(this, delegate (object prior)
54 | {
55 | T current = this.Value;
56 | whenChanged(current);
57 | return current;
58 | }, default(T));
59 | }
60 |
61 | public ComputedSubscription Subscribe(Action whenChanged)
62 | {
63 | return new ComputedSubscription(this, delegate (object prior)
64 | {
65 | T current = this.Value;
66 | whenChanged(current, (T)prior);
67 | return current;
68 | }, default(T));
69 | }
70 |
71 | public override string VisualizerName(bool withValue)
72 | {
73 | string s = VisualizerName(_name ?? "NamedComputed");
74 | if (withValue)
75 | s += " = " + (_value == null ? "null" : _value.ToString());
76 | return s;
77 | }
78 | internal static string VisualizerName(string name)
79 | {
80 | string typeName = MemoizedTypeName.GenericName();
81 | if (!string.IsNullOrEmpty(name))
82 | return string.Format("{0}: {1}", name, typeName);
83 | else
84 | return typeName;
85 | }
86 |
87 | [Obsolete, EditorBrowsable(EditorBrowsableState.Never)]
88 | public Computed ComputedSentry
89 | {
90 | get { return this; }
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/Assisticant/Fields/ComputedSubscription.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Assisticant.Fields
4 | {
5 | public class ComputedSubscription
6 | {
7 | private readonly Computed _computed;
8 | private readonly Func