This is a migrated thread and some comments may be shown as answers.

The shape has no connectors defined.

3 Answers 77 Views
Diagram
This is a migrated thread and some comments may be shown as answers.
Affreux
Top achievements
Rank 1
Affreux asked on 30 Apr 2018, 02:44 PM

Hello,

I'm using a RadDiagram where the users can add images and draw arrows.

By default, when the user draws an arrow over a shape, the arrow connects to the closest connection point. I need to modify this behavior. When the ActiveTool is ConnectorTool and the user clicks on the image, the arrow must start from the point where the user clicked, and not from the closest connection point.

 

In order to achieve this, I removed all connection points from the image when I add it. This didn't work as expected.

The following code sample allows me te reproduce the error. When I click outside the image, the behavior is nominal.

When I click on the image, an exception is thrown. Instead, I want the application to behave as if the image wasn't there.

In my sample, the ActiveTool is always ConnectorTool, but in my real application, the ActiveTool can have other values. This behavior must only apply to the ConnectorTool. For example, when the PointerTool is active, the user must be able to select and move the image.

 

 

 

 

class Program
{
    [STAThread]
    static void Main(string[] args)
    {
        TestWindow win = new TestWindow();
        win.ShowDialog();
    }
}
 
 
class TestWindow : Window
{
    public TestWindow()
    {
        Grid main_grid = new Grid();
        main_grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = GridLength.Auto });
        main_grid.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) });
 
        RadDiagram my_diagram = new RadDiagram();
        my_diagram.ActiveTool = Telerik.Windows.Diagrams.Core.MouseTool.ConnectorTool;
        Grid.SetColumn(my_diagram, 1);
 
        ViewModel vm = new ViewModel(my_diagram);
 
        Button button_add_image = new Button();
        button_add_image.Content = "Add image";
        button_add_image.Command = vm.CommandAddImage;
 
        AddChild(main_grid);
        main_grid.Children.Add(my_diagram);
        main_grid.Children.Add(button_add_image);
 
        DataContext = vm;
    }
}
 
class ViewModel : ViewModelBase
{
    public ViewModel(RadDiagram diagram)
    {
        MyDiagram = diagram;
    }
 
    public RadDiagram MyDiagram { get; set; }
    public ICommand CommandAddImage { get { return new ICommandImpl(AddImage); } }
 
    public void AddImage()
    {
        Microsoft.Win32.OpenFileDialog open_file_dialog = new Microsoft.Win32.OpenFileDialog();
        open_file_dialog.Filter = "Image Files (*.png, *.jpg, *.bmp)|*.png;*.jpg;*.bmp";
        open_file_dialog.CheckFileExists = true;
 
        if (!open_file_dialog.ShowDialog().GetValueOrDefault() || !System.IO.File.Exists(open_file_dialog.FileName))
        { return; }
 
        System.Windows.Media.Imaging.BitmapImage image_source = new System.Windows.Media.Imaging.BitmapImage(new Uri(open_file_dialog.FileName));
        System.Windows.Controls.Image image_displayed = new System.Windows.Controls.Image();
        image_displayed.Source = image_source;
        Viewbox viewbox_added_to_diagram = new Viewbox();
        viewbox_added_to_diagram.Stretch = Stretch.Fill;
        viewbox_added_to_diagram.Margin = new System.Windows.Thickness(-4);
        viewbox_added_to_diagram.Child = image_displayed;
 
        RadDiagramShape shape_added_to_diagram = new RadDiagramShape();
        shape_added_to_diagram.Content = viewbox_added_to_diagram;
        shape_added_to_diagram.Background = Brushes.Transparent;
        shape_added_to_diagram.BorderBrush = Brushes.Green;
 
        shape_added_to_diagram.Connectors.Clear();
        MyDiagram.AddShape(shape_added_to_diagram, new Point(20, 20));
    }
 
 
    private class ICommandImpl : ICommand
    {
        private Action Command;
        public ICommandImpl(Action p_action) { Command = p_action; }
        public void Execute(object parameter) { Command(); }
        public bool CanExecute(object parameter) { return true; }
        public event EventHandler CanExecuteChanged;
    }
}

 

 

I followed the instructions in another post, but I didn't manage to make it work.

I used added the following tool, but the behavior is not what I need. Now, when I click on a shape, nothing happens. I want a new connection to start when I click on a shape.

class CustomConnectionTool : ConnectionTool
{
    public override bool MouseDown(PointerArgs e)
    {
        if (!this.IsActive) return false;
 
        var hitItem = this.HitTestService.ItemUnderMouse as IShape;
        if (hitItem == null || hitItem.Connectors.Count > 0)
            return base.MouseDown(e);
        else
            return false;
    }
}

 

The post suggested creating a new service or a custom tool, but I couldn't figure it out. In addition, my real application has more styles and custom behavior associated with Connections, so I'd rather not use a different class.

 

How else can I achieve what I need ? When the user clicks a shape, it must not start from the closest connection, but must instead start from the point where the user clicked.

3 Answers, 1 is accepted

Sort by
0
Accepted
Petar Mladenov
Telerik team
answered on 03 May 2018, 06:56 AM
Hi Affreux,

The default ConnectionTool either creates connection with start or end - an existing shape or creates a floating connection starting and ending in diagram surface. To make a floating connection, the ControllerService.CreateConnection(point, point) overload method must be invoked. Please find a possible implementation below:
public class CustomConnectionTool : ConnectionTool
   {
       private ICommand addCommand;      
 
       public override bool MouseDown(PointerArgs e)
       {
           if (!this.IsActive) return false;
 
           var hitItem = this.HitTestService.ItemUnderMouse as IShape;
           if (hitItem != null)
           {
               this.ActiveConnection = this.ServiceLocator.ControllerService.CreateConnection(e.TransformedPoint, e.TransformedPoint);
               this.addCommand = this.ServiceLocator.ControllerService.BeginConnectionCreation(this.ActiveConnection);
               this.addCommand.Execute(null);
               return true;
           }
 
           return base.MouseDown(e);
       }
   }

This code also assumes you don not wish to add custom connector on mouse down, before the connections creation starts. Please let us know if this is a desired outcome.


Regards,
Petar Mladenov
Progress Telerik
Want to extend the target reach of your WPF applications, leveraging iOS, Android, and UWP? Try UI for Xamarin, a suite of polished and feature-rich components for the Xamarin framework, which allow you to write beautiful native mobile apps using a single shared C# codebase.
0
Affreux
Top achievements
Rank 1
answered on 03 May 2018, 03:01 PM

Thank you, your code solves my problem.

But that's not all. I want the created connection to be on top of all the other shapes. My ConnectionTool now looks like this:

 

class CustomConnectionTool : ConnectionTool
{
    public override bool MouseDown(PointerArgs e)
    {
        if (!IsActive) return false;
 
        RadDiagramShape hit_item = HitTestService.ItemUnderMouse as RadDiagramShape;
        if (hit_item == null) return base.MouseDown(e);
 
        ActiveConnection = ServiceLocator.ControllerService.CreateConnection(e.TransformedPoint, e.TransformedPoint);
        ServiceLocator.ControllerService.BeginConnectionCreation(ActiveConnection).Execute(null);
 
        if (Graph.Items.OfType<IDiagramItem>().Any())
            ActiveConnection.ZIndex = Graph.Items.OfType<IDiagramItem>().Max(s => s.ZIndex) + 1;
        return true;
    }
}

 

I expected that defining the ActiveConnection's ZIndex would put it on top of all other shapes, but it didn't work.

How can I have the created connection on top of all other shapes when created?

0
Petar Mladenov
Telerik team
answered on 08 May 2018, 01:52 PM
Hello Affreux,

Your code works as expected on our side. Could you please add more details and eventually steps that we can perform in order to reproduce an issue with it ? Usually, all shapes have ZIndex 2 and all connections have ZIndex 1. Your code makes the first connection created with the custom connection tool with ZIndex 3, the next has ZIndex 4 , 5, 6, etc. This always ensures all new connection is on top of all shapes and on top of all previously created connections.

Regards,
Petar Mladenov
Progress Telerik
Want to extend the target reach of your WPF applications, leveraging iOS, Android, and UWP? Try UI for Xamarin, a suite of polished and feature-rich components for the Xamarin framework, which allow you to write beautiful native mobile apps using a single shared C# codebase.
Tags
Diagram
Asked by
Affreux
Top achievements
Rank 1
Answers by
Petar Mladenov
Telerik team
Affreux
Top achievements
Rank 1
Share this question
or