This project is read-only.

Blocking dependencies from forming in a Dependent?

Jan 20, 2014 at 10:33 PM
Edited Jan 21, 2014 at 6:57 PM
Remember that deadlock scenario? It involved a search function. Now that the deadlock is fixed, I'm trying to figure out how to address another issue: excessive refreshing. Currently, the search repeats every time anything changes that the search results might depend on (whether the dependency is real or merely a potential dependency.)

This causes hiccups in the user interface because the ListBoxes that show the search results are reset (scroll to the top) whenever the search repeats; this thwarts the user's efforts to scroll down the list of search results ([edited] <strike>I was able to fix most of this behavior by changing how I defined Equals & GetHashCode on the VMs... I originally thought that the complexity of my search screen, which is a list of lists of search results somewhat like the Start Menu search in Windows 7, might have been a factor, but evidently it is not.</strike> [edit 2] My first edit was wrong, because when the "resetting" stopped I failed to notice that search was fundamentally broken; when the search text changed, the search results did not! Clearly I do not fully understand how updates work. [edit 3] I suspect my original guess was right after all; if Equals() in the 'outer' VM SearchResultList (which contains a list of inner VMs) returns false, the inner list resets and annoys the user, but if Equals() returns true, the inner list is not updated at all... it doesn't matter if I use DependentList<SearchResultList> or Dependent<List<SearchResultList>>. in the former case I think the UC RecycleBin keeps the old VM with the old search results rather than the new VM with the new search results, while in the latter case, similar behavior can be ascribed to the outer ItemsControl rather than Update Controls.).

It seems to me that the simplest solution would be to block the formation of certain dependencies. For example, results1 (of type Dependent<SearchResultList>, let's say) should behave like it depends only on the search string and whether the search Popup is open (because searching is done on a Popup window), even though it must read many other Independents in the course of performing the search. That way, search results are not refreshed without user action. So in the Update method for results1 I would like to turn off dependency tracking while I perform some of the work.

However, I don't see any way to block dependency formation. What do you think, is dependency blocking a feature that Update Controls should have? Without the "resetting" behavior it's no big deal, but it still seems like it would be nice to have. For instance, some might argue that the search results should be "stable", not changing while the user is reviewing them. Often as a software developer you get "little" feature requests which turn out to be "big" feature requests if the underlying technology (in this case Update Controls) isn't designed to work that way.
Jan 21, 2014 at 1:43 PM
This might actually be a good scenario for the UpdateScheduler. Displaying search results could be considered a side-effect of the dependency upon the search term.

Create a Dependent<SearchParameters> and have it's update method collect the search term and any other parameters into the result. Then, create a class that implements IUpdatable and holds a reference to this dependent. In UpdateNow, get the value of the dependent to collect the search parameters. Then run the search and update the search results. The code that you execute outside of the Dependent's update function but within UpdateNow will not be tracked, so you can access any additional Independents that you need without taking a dependency.

Be sure to hook the Invalidated event to call UpdateScheduler.ScheduleUpdate(this). And prime the pump by getting the value for the first time.

Just be aware that UpdateNow will be called on the UI thread. If it takes time to run the search, you might need to kick it off and process the results asynchronously. This is getting back to your original issue about updates happening on the UI thread, so I don't have a general-purpose solution for you yet.
Jan 21, 2014 at 8:21 PM
Well, I chose a different solution... your post reminded me I already had a solution for searching asynchronously, and that I could solve my problem by applying a similar technique to the synchronous search. Previously I had these precedents:
// Simplified. in reality the compiler requires Dependents to be initialized in the constructor.
Dependent<List<SearchResultCategory>> _syncResults = new Dependent<List<SearchResultCategory>>(DoSyncSearch)
Independent<List<SearchResultCategory>> _asyncResults = new Independent<List<SearchResultCategory>>("_asyncResults", null);
Dependent _depSpawnAsyncSearch = new Dependent(SpawnAsyncSearch);
Dependent<List<SearchResultCategory>> _results = new Dependent<List<SearchResultCategory>>(CombineResults);
Now I changed _syncResults into an Independent, added a new Dependent to initiate the synchronous search (_depSpawnSearch), and removed _depSpawnAsyncSearch because _depSpawnSearch also spawns the async search:
Independent<List<SearchResultCategory>> _syncResults = new Independent<List<SearchResultCategory>>("_syncResults", null);
Independent<List<SearchResultCategory>> _asyncResults = new Independent<List<SearchResultCategory>>("_asyncResults", null);
Dependent _depSpawnSearch = new Dependent(SpawnSearch);
Dependent<List<SearchResultCategory>> _results = new Dependent<List<SearchResultCategory>>(CombineResults);
Then I added a new SpawnSearch() method which establishes dependencies on the search text (etc.) and then uses BeginInvoke() to prevent Update Controls from finding out about the other dependencies:
private void SpawnSearch()
{
    if (SearchesActive)
    {
        string _ = SearchString;
        var __ = // any other dependency I want UC to know about
        PM.ViewPrimitives.BeginInvoke(() =>
        {   // Using BeginInvoke blocks UC from finding out about other dependencies
            _syncResults.Value = DoSyncSearch();
            SpawnAsyncSearch();
        });
    }
}
I already have a set of "ViewPrimitives" that the View provides to the ViewModel, so that the viewmodel can use certain functions without taking a dependency on the GUI framework. One of them is BeginInvoke:
/// <remarks>
/// These methods are provided by the view so that viewmodels can perform 
/// certain rudimentary actions, such as showing a message box, without 
/// taking a dependency on a particular GUI library.
/// </remarks>
public interface IViewPrimitives
{
    ContextMenuHub<T> CreateEmptyMenu<T>();
    void ShowMessageBox(string message, string title);
    string ShowInputBox(string prompt, string title, string @default);
    void RunTimerOnce(Action code, int milliseconds);
    void BeginInvoke(Action code);
}
Finally, CombineResults() (for _results) calls _depSpawnSearch.OnGet() to "prime the pump" as you said.

Clearly, it would be simpler if UC just allowed me to say "ignore dependencies in this region of code". But this works. However I would point out that the search is very slightly delayed by using BeginInvoke() instead of just performing the search directly.