Is it possible with WPF to load a part of the UI in the background?

1.2k Views Asked by At

I want to show a network in my WPF-Application. But always the window freeze on rendering. I use the Graphsharp-library for visualization. The network is assembled in an extra thread. So it will probably be the renders. So, is it possible with WPF to load a part of the UI in the background?

My Code:

    _graph = await Task.Run(()=> {
     var g = new BidirectionalGraph<object, IEdge<object>>();
    foreach (MyItem p in _myItemList)
    {
        g.AddVertex(p);
    }

    foreach (MyItem p in _myItemList)
    {
        foreach (MyItem n in p.Neighbors)
        {
            g.AddEdge(new Edge<object>(p, n));
        }
    }
    return g;
});
OnPropertyChanged("Graph");

XAML:

xmlns:graphsharp="clr-namespace:GraphSharp.Controls;assembly=GraphSharp.Controls"
[...]

<graphsharp:GraphLayout
    Graph="{Binding ElementName=root, Path=Graph}"
    LayoutAlgorithmType="LinLog"
    OverlapRemovalAlgorithmType="FSA"
    HighlightAlgorithmType="Simple" />
1

There are 1 best solutions below

11
On BEST ANSWER

NO operating system allows modifying the UI from another thread. In your case though you aren't trying to update the UI in the background, you are assembling the graph in the background and setting a property. There's no UI work here. If the rendering element binds to that property it will get the notification, read the property and update itself, all in the UI thread.

The code needs improvement though. Tasks aren't threads and there's no reason to use cold tasks and Task.Start() like that. Start() doesn't guarantee when a task will run. The task will still be scheduled for execution on the threadpool thread and possibly wait if there are no available threads there.

The code can be simplified to this :

var graph = await Task.Run(()=> {
    var g = new BidirectionalGraph<object, IEdge<object>>();
    foreach (MyItem p in _myItemList)
    {
        g.AddVertex(p);
    }

    foreach (MyItem p in _myItemList)
    {
        foreach (MyItem n in p.CallingList)
        {
            g.AddEdge(new Edge<object>(p, n));
        }
    }
    return g;
};

_graph = graph;
 OnPropertyChanged("Graph");

A better idea wold be to use a proper property instead of modifying fields and raising the event :

public BidirectionalGraph Graph 
{
    get => _graph;
    set 
    {
        _graph=value;
        OnPropertyChanged(nameof(Graph));
    }
}
...
public async Task MyGraphMethod()
{
    Graph=graph;
}

UPDATE

The real question seems to be why does GraphLayout take so long to display the graph?

Set the AsyncCompute property of GraphLayout to true :

<graphsharp:GraphLayout
    Graph="{Binding ElementName=root, Path=Graph}"
    LayoutAlgorithmType="LinLog"
    OverlapRemovalAlgorithmType="FSA"
    HighlightAlgorithmType="Simple" 
    AsyncCompute = "true" />

GraphSharp was abandoned 6 years ago if not more. Codeplex itself shut down and now, the only available source code is either the archived source or forks on Github, like this one.

The examples on this repo show there is an AsyncCompute property that will run the layout algorithms in the background :

        <sample:MyGraphLayout x:Name="Layout" LayoutAlgorithmType="ISOM" OverlapRemovalAlgorithmType="FSA" Graph="{Binding}"
                              AsyncCompute="true" ShowAllStates="false" HighlightAlgorithmType="Simple">
            <sample:MyGraphLayout.LayoutParameters>
                <isom:ISOMLayoutParameters Width="1200" Height="1200" />
            </sample:MyGraphLayout.LayoutParameters>
        </sample:MyGraphLayout>

When AsyncCompute is true, the Layout() method of the control uses a BackgroundWorker to perform the operation in the background

This property exists in the original projects source archive as well.