This project is read-only.

Tool tips on edges

Oct 2, 2009 at 4:32 PM
Edited Oct 2, 2009 at 4:34 PM

I want to be able to set a tool tip on each edge, where the contents of the tool tip is defined by a non-element property of the edge's target vertex.  I started by defining something like this:

<Style TargetType="{x:Type graphsharp:EdgeControl}">
    <Setter Property="ToolTip">
      <Setter.Value>
        <StackPanel>
          <ListView ItemsSource="...">
            ...
          </ListView>
        </StackPanel>
      </Setter.Value>
    </Setter>
    ...
  </Style>

This works if the tool tip contains something simple, like a string.  However, I couldn't figure out how to set up a data context/binding to populate the list view from the target vertex's properties.  I eventually gave up and duplicated the template code for the EdgeControl, so I now have something like this: 

  <Style TargetType="{x:Type graphsharp:EdgeControl}">
    <Setter Property="Template">
      <Setter.Value>
        <ControlTemplate TargetType="{x:Type graphsharp:EdgeControl}">
          <Path ...>
            ...
            <Path.ToolTip>
              <StackPanel>
                <ListView ItemsSource="{Binding Path=Target.Vertex.MyProperty,
                          RelativeSource={RelativeSource TemplatedParent}}">
                  ...
                </ListView>
              </StackPanel>
            </Path.ToolTip>
          </Path>
        </ControlTemplate>
      </Setter.Value>
    </Setter>
  </Style>

This displays the required tool tip.  However, apart from the obvious downside of duplicating the template code, it also seems to cache the result.  So when the property of the destination vertex that provides the data for the tool tip is altered the tooltip still shows the old result.  But if I set a tool tip on the custom target vertex itself that's connected to the same property it shows the new result.  Anyone have any idea as to why the value is not changing?

Of course ideally it would make more sense to store the data driving the tool tip in the edge, rather than the target vertex, as there may be multiple edges to the target, each one needing it's own data.  But although I could build my graph using a custom edge class, derived from IEdge, it's unclear to me what XAML I need to write to tie this into Graph#.  For custom vertices I just define a data template for the class, but edges are more complex, with their paths etc.  Has anyone managed to use Graph# with custom edge classes, and if so what does the XAML look like?

Thanks,

Kevin

 

 

Oct 3, 2009 at 1:48 PM

I'll answer the final part of the question myself... Suppose you define your own vertex class, MyVertex, and the corresponding edge class, MyEdge:Edge<MyVertex>.  You then define a custom graph class MyGraph:BidirectionalGraph<MyVertex, MyEdge>  When you try to use these classes you find that no graph appears.  The bit I was missing was that you also need to define class

MyGraphLayout : GraphSharp.Controls.GraphLayout<MyVertex, MyEdge, MyGraph>

and update the XAML so that it uses the MyGraphLayout element, instead of GraphLayout, to instantiate the graph view.   The library desperately needs some documentation, or at the very least an FAQ, to avoid such frustrations :-(

Oct 6, 2009 at 1:52 AM

Kevin, the answer to your first question (how to get the ToolTip change to appear when using just the style setters) is the following:

1) the edge control template should be modified to use ToolTip from a template binding on the path.  In other words, it should have a Path.ToolTip property set to a {TemplateBinding ToolTip}.  This is an oversight in the implementation of the control template, so it makes sense that the control template needs to change - this is not something that each user should have to change - it should be changed in the 'stock' templates.

2) your style will then be able to set ToolTip on the EdgeControl and have it passed down by virtue of (1) to the Path.  This means for your own customization (adding tooltip values) you can do so in the "regular" style template without having to replace the control templates.

3) if your binding is not updating after changes to your property, it's likely you aren't implementing INotifyPropertyChanged properly, and you aren't using a DependencyObject/DepedencyProperty for your source object/property.  You should raise PropertyChanged when changing the value of your property on your Edge class, when the property is changed by whatever is making the change.  This will notify the binding to refresh the tooltip.  I don't know if refreshes occur during the tooltip showing, so if you need that, you may need some "magic".

Oct 6, 2009 at 4:37 PM

Thanks for the reply Kelly.  I think I understand the first part of your suggestion; I modified my copied version of the EdgeControl template to set the tool tip to {TemplateBinding ToolTip} instead of my custom tooltip, and hopefully I'll be able to strip out this copy when Graph# gets updated.  But I'm not sure what to write for the second part of your suggestion.  If I define

    <Style TargetType="{x:Type graphsharp:EdgeControl}">
      <Setter Property="ToolTip">
        <Setter.Value>
          ...
        </Setter.Value>
      </Setter>
    </Style>
then the system generates an XamlParseException complaining about a duplicate key.  I think I'm reaching (or passed) the limits of my current XAML knowledge :-(
Oct 6, 2009 at 6:34 PM

Kevin, the only way this should be giving a duplicate resource exception (if I understand correctly) is if you have two of these styles in the same resource dictionary.  Are you using a merged dictionary, or a dictionary that you copied from the sample project?  If so, you should probably just modify that style to add the setter as appropriate in there.  If you want to keep those as "stock" styles that you customize, you need to add your customizations at a lower (deeper child) level in the logical tree.  For instance, in Window.Resources (but don't merge the resource dictionary if you're doing so).

You definitely won't be able to put both styles in the same resource dictionary (whether merged or inline).

Oct 6, 2009 at 8:11 PM

Thanks Kelly, now you mention it I remember reading about this stuff.  I'll go back and look at my WPF book.  Thanks for your help.

Kevin

Oct 7, 2009 at 9:54 AM
Edited Oct 7, 2009 at 9:55 AM

Nearly there...  Rather than modifying and rebuilding Graph# I moved the modified version of the edge control template to the application xaml file and kept the tooltip style setter in the main window xaml file.  This fixed the duplicate resource exception, as you had predicted.  So now if the tool tip is a simple string I can just define something like this:

<Style TargetType="{x:Type graphsharp:EdgeControl}">
  <Setter Property="ToolTip">
    <Setter.Value>...</Setter.Value>
  </Setter>
</Style>

That works fine.  However, if I want a slightly more ambitious tooltip, e.g. a list in a stack panel populated by a collection property in my edge class, I can't just write something like this:

<Style TargetType="{x:Type graphsharp:EdgeControl}">
  <Setter Property="ToolTip">
    <Setter.Value>
      <StackPanel>
        <ListView ItemsSource="{Binding Path=Edge.MyList}">
          ...
        </ListView>
      </StackPanel>
    </Setter.Value>
  </Setter>
</Style>

as you get a Xaml parse exception.  I can get around this by putting the stack panel in a resource, and then referencing it in the setter, as in

<StackPanel x:Key="EdgeToolTip">
  ...
</StackPanel>

<Style TargetType="{x:Type graphsharp:EdgeControl}">
  <Setter Property="ToolTip" Value="{StaticResource EdgeToolTip}"/>
</Style>

However, I can't figure out how to set the ItemsSource attribute to pick up the edge property list.  As with most problems in WPF, there's probably a one line bit of magic I'm missing somewhere.  Suggestions would be appreciated.

Kevin

Oct 7, 2009 at 10:03 AM

Kevin, what's the XamlParseException that you get when trying the Setter.Value with StackPanel in it.  I see no reason why that shouldn't work.  BTW, I just checked in a change that allows you to use bindings that refer to the Edge or Vertex properties using simple binding syntax.  Currently, the binding stuff would refer to the Graph object, not the Vertex or Edge objects, since DataContext was not being set on the VertexControl and EdgeControl.  The latest source (revision 31550 or later) should contain this fix, and allow you to use "normal" binding syntax.  With my fix, you would just bind to {Binding MyList}, not {Binding Edge.MyList}.  If you wanted to go through the "Edge" property, you need to use the templated source (i.e. {Binding Edge.MyList, RelativeSource={RelativeSource TemplatedParent}} instead of the simple Binding, since the simple binding looks in the DataContext for it's source).

Oct 7, 2009 at 10:19 AM

The error you get is "Cannot add content of type 'System.Windows.Controls.StackPanel' to an object of type 'System.Object'".  It sounds like it's a known problem, with the use of a resource the suggested fix.  It's just getting the binding set up correctly that's causing the problem.  I'll try using the revision you suggested to see if I can get further.

Oct 7, 2009 at 10:47 AM

Kevin, check out revision 31556, specifically the Graph#.Sample/MainWindow.xaml file (line 316) and the resource that I've added to the graph layout tag.  You should be able to see how to do a binding from this example (using the inline resource, like you found is necessary for ToolTip's (I never knew that was necessary)).  In order to get yours to work, you should be able to just replace my TextBlock with your ListView and have it work.  If that doesn't work, you might try a simpler ItemsControl type, so that interactivity is not involved.  I remember reading that there are some weird things about interactive controls being in tooltips.

I also pushed the ToolTip support code into the Poc GraphVertex and GraphEdge templates.  I didn't push them into the stock templates, but if you're using them and you'd like me to, I can.  I just figured nobody is using the stock ones, so I didn't bother unless someone asks for it.

Oct 7, 2009 at 10:55 AM

Actually, I went ahead and pushed the changes down to the core templates.  That way if you're using the templates in GraphSharp.Controls.dll, you will get the ToolTip support (i.e. you just need a style to set ToolTip on the Vertex / Edge, and it will be pulled down to the appropriate place in the logical tree).

Kelly

Oct 7, 2009 at 1:33 PM

Excellent.  Your 31556 build, together with ItemsSource set to "(Binding Path=MyListProperty}", worked fine.  Thanks for all your help Kelly.

Kevin 

Oct 7, 2009 at 4:24 PM

On a related note, suppose I wanted to use a data trigger to alter a property of an edge, e.g. it's stroke thickness.  I started by defining the following:

<Style TargetType="{x:Type graphsharp:EdgeControl}">
  <Setter Property="ToolTip" Value="{StaticResource EdgeToolTipContent}"/>
  <Setter Property="ContextMenu" Value="{StaticResource EdgeContextMenu}"/>
  <Style.Triggers>
    <DataTrigger Binding="{Binding Path=Active}" Value="True">
      <Setter Property="???StrokeThickness???" Value="4"></Setter>
    </DataTrigger>
  </Style.Triggers>
</Style>

However, it's not clear to me how to refer to the StrokeThickness property of the path making up the edge in the setter element.  I've tried various specifying various things for the property, e.g. Path.StrokeThickness, and none of them seem to alter the thickness.

Oct 7, 2009 at 8:16 PM

You can probably use the TargetName property of Setter, and use the TargetName of your "path" element, but the better approach would be for us to add a DP on EdgeControl to allow you to 'pass on' these values just like what we did with ToolTip.  I'll work on that over lunch.

Oct 7, 2009 at 8:35 PM

Hmm... I don't know what's going on, but no matter what I change the stroke thickness in the path to, it doesn't seem to matter - it always looks the same.  I even changed it from 2 to 5 and then to 0.5 and couldn't see any difference.  If you can figure out how to get the strokethickness to do whatever you want (by mucking with the path directly) then let me know and I'll figure out how you can get the value set via a binding or style.  I'm not going to check in what I've done so far, since it seems silly to add a DP and the plumbing in the xaml if the Path is just going to ignore it anyway.

Oct 8, 2009 at 10:03 AM

If I copy the edge control template into my zoom control resources, as I did for the tool tip, then I can set the stroke thickness to different values and get the expected outcome.  Furthermore, if I add a trigger to the control template, for example

<ControlTemplate.Triggers>
  <DataTrigger Binding="{Binding Path=Active}" Value="True">
    <Setter Property="StrokeThickness" Value="5" TargetName="edgePath"></Setter>
  </DataTrigger>
</ControlTemplate.Triggers>

then the line thickness varies, as expected.  But ideally you'd want to be able to write something like the trigger without having to also duplicate the EdgeControl control template code.

 

Oct 9, 2009 at 2:34 AM

ok... I have no idea why I couldn't see any changes in the size, but I've checked in the changes I made.  See if when you set the StrokeThickness on the style whether it does what you'd expect.  It didn't seem to do anything when I tried it, but maybe I was not using it correctly, or looking in the wrong place.

Kelly

Feb 3, 2011 at 12:14 AM
Edited Feb 3, 2011 at 12:14 AM

And do you do when the tooltip is a property of the custom Edge that you want to bind, and the datacontext do not include him?

Here is the custom Edge

 

public class PocEdge : Edge<PocVertex>
    {
        public string ID
        {
            get;
            private set;
        }

        public PocEdge(string id, PocVertex source, PocVertex target)
            : base(source, target)
        {
            ID = id;
        }
    }

 

Here is the Xaml

 

<Style TargetType="{x:Type graphsharp:EdgeControl}">
            <Style.Resources>
                <ToolTip x:Key="ToolTipContent">
                    <StackPanel>
                        <TextBlock FontWeight="Bold" Text="Edge.ID"/>
                        <TextBlock Text="{Binding ID}"/>
                    </StackPanel>
                </ToolTip>
            </Style.Resources>
            <Setter Property="ToolTip" Value="{StaticResource ToolTipContent}"/>
</Style>

 

The error I get at runtime:

System.Windows.Data Error: 40 : BindingExpression path error: 'ID' property not found on 'object' ''MainWindowViewModel' (HashCode=27666100)'. BindingExpression:Path=ID; DataItem='MainWindowViewModel' (HashCode=27666100); target element is 'TextBlock' (Name=''); target property is 'Text' (type 'String')

Thanks

 

Feb 3, 2011 at 4:49 AM

I'm going to guess that you're using the Jun 2009 build (v1.0) and not building from the latest sources.  I made several changes but a new build has not been posted with my changes.  I'd recommend you grab the sources and build sources and see if that fixes your problems.

Kelly

Feb 8, 2011 at 9:32 PM

Thanks a lot problem solved with the last version build from source of graphsharp and the control.

Feb 10, 2011 at 2:15 PM

 

<TextBlock FontWeight="Bold" Text="Edge.ID"/>

There is no error is running the code except there is no binding to the Edge.ID
Each Tooltip has exactly the same word "Edge.ID"

Any suggestion, kind of learning WPF now.
Feb 10, 2011 at 4:51 PM

That’s because you didn’t put any binding for Text. Bindings look like “{Binding Edge.ID}”, you just supplied text (“Edge.ID”).

This is a tough project to start learning WPF with, I’d recommend you try to learn how binding works a bit before you dive into trying to use it on this project.

Kelly

From: OnSharp <notifications@codeplex.com> [mailto:OnSharp <notifications@codeplex.com>]
Sent: Thursday, February 10, 2011 6:15 AM
To: kelly.leahy@milliman.com
Subject: Re: Tool tips on edges [graphsharp:70832]

From: OnSharp

<TextBlock FontWeight="Bold" Text="Edge.ID"/>
 
There is no error is running the code except there is no binding to the Edge.ID
Each Tooltip has exactly the same word "Edge.ID"
 
Any suggestion, kind of learning WPF now.

**************************************************************************************
This communication is intended solely for the addressee and is
confidential. If you are not the intended recipient, any disclosure,
copying, distribution or any action taken or omitted to be taken in
reliance on it, is prohibited and may be unlawful. Unless indicated
to the contrary: it does not constitute professional advice or opinions
upon which reliance may be made by the addressee or any other party,
and it should be considered to be a work in progress. Unless otherwise
noted in this email or its attachments, this communication does not form
a Statement of Actuarial Opinion under American Academy of Actuaries guidelines.
**************************************************************************************
Jun 27, 2011 at 3:51 PM

Hi there,

I've been reading through this thread with great interest. I am unfortunately still trying to puzzle out the correct style template to set the edge thickness. Any change you could write it out in full, i think i am missing things between posts.  Even just to set the stroke thickness to a constant value would get me going

many thanks

David

Jul 6, 2011 at 1:37 PM

Hi. I have been impressed with the functionlaity of GraphSharp. I have been working on what information is available on this. I also am new to the C# environment.

 

I have read through the thread, since I am looking for a functionlaity to display a tooltip on a Vertex. The tooltip needs to take the string value from the Vertex and do a database call to obtain other information. Hence each tooltip will be unique.

 

I would apprecita if the code that was dicussed in this thread could be available. This helps to understand how each componenet work together.

 

Kind regards.