1

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" />
Baum
  • 95
  • 10
  • Do you very large view? or ViewModel is taking time to prepare data for View? or other reason for slow loading? – Sham Oct 04 '18 at 06:20
  • NO operating system allows modifying the UI from another thread. You don't need to do so anyway. BTW you don't need `BeginInvoke` here, nor a cold task. Just write `_graph= await Task.Run(()=>{...; return g};` – Panagiotis Kanavos Oct 04 '18 at 07:12
  • I use the Invoke because of the DispatcherPriority.Background-parameter. It was an attempt to prevent freezing. – Baum Oct 04 '18 at 08:25
  • @Baum you don't need it at all. `await Task.Run()` will work just fine and run the graph generation in the background. If there's any freezing, the problem is in code you didn't describe or post. How *is* the graph rendered? Using which control? – Panagiotis Kanavos Oct 04 '18 at 08:55
  • @Baum the link points to Codeplex btw, which shut down quite a while ago – Panagiotis Kanavos Oct 04 '18 at 08:55
  • @Baum how did you add GraphSharp? The NuGet package was abandoned in 2012. The most up-to-date fork is [this](https://github.com/andypelzer/GraphSharp) – Panagiotis Kanavos Oct 04 '18 at 09:02
  • @Baum seems you can set the `AsyncCompute` property of the control to `true` to have it layout the graph in the background – Panagiotis Kanavos Oct 04 '18 at 10:07

1 Answers1

1

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.

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • Thats a good point, thx. But it does not help me with the problem of freezing. – Baum Oct 04 '18 at 08:40
  • @Baum what freezing? When? The graph is generated on the background. You didn't explain how it's rendered, by which control or element so it's impossible to guess what's wrong. What you posted doesn't do *anything* on the UI, except raise the PropertyChanged event – Panagiotis Kanavos Oct 04 '18 at 08:54
  • @Baum I guess you are modifying '_myItemList' which is most likely created on the UI thread. You would need to make a local copy of the collection or invoke the modification on the dispatcher. When you run in debug mode do you get a cross thread exception? – BionicCode Oct 04 '18 at 09:46
  • @BionicCode ?? there's no `_myItemList` in this question and no cross-thread exception – Panagiotis Kanavos Oct 04 '18 at 09:48
  • @PanagiotisKanavos He is iterating over _itemList collection in his example isn't he?? And the Exception might got caught in the application. This scenario would lead to the same behavior. That's why I asked him. I am sorry. – BionicCode Oct 04 '18 at 09:53
  • @BionicCode there's no cross-thread exception. There's no modification to the list or any other UI element. Even the OP's code used `Dispatcher.BeginInvoke`. It's the Graph# control that takes too long to render – Panagiotis Kanavos Oct 04 '18 at 09:55
  • _myItemList is not the fault. I build them on an other UI, so the list is completed. – Baum Oct 04 '18 at 10:47
  • @Baum set AsyncCompute to true to force the control to run the layout algorithm in the background. You should probably find a library that's still supported though – Panagiotis Kanavos Oct 04 '18 at 10:48
  • I update the lib to the fork and I set the AsyncCompute to true. Its better it has not stopped freezing. I open the Window, it freezed, I soo all nodes in one position. Then it shopped to freez and the Nodes gos to the right place to build a network. – Baum Oct 04 '18 at 10:51
  • @Baum the original one has an `AsyncCompute` as well. The code is mostly the same. You should really find another library though. The project is *abandoned for 6 years* and never posted documentation. You'll have to read the source code to find out what's available and how it works – Panagiotis Kanavos Oct 04 '18 at 10:53
  • hmmm.... That was the best lib I found. It will be adjusted a lot and the positions will be calculated automatically. Do you happen to know a good alternative? – Baum Oct 04 '18 at 11:05