1

I'm new here so please excuse my if I missed to add something needed to answer my question.

So heres my question: I am trying to add shapes to a canvas while also wanting to show a list of them in a listbox, to make them changeable (size,position, etc.). I am using WPF. Is there a way to do so?

And if it doesnt bother you: Is there maybe a question or website or whatever about how to dynamically draw shapes(circle,ellipse,rect, etc.) with mouse events?

I hope you can help me. Thanks in advance.

Edit: Given the fact that I have:

    public partial class MainWindow : Window
{
    public ObservableCollection<string> Baselist = new ObservableCollection<string>();
    public ObservableCollection<string> Crystallist = new ObservableCollection<string>();
    public ObservableCollection<Shape> Shapelist = new ObservableCollection<Shape>();
    public MainWindow()
    {
        this.ResizeMode = System.Windows.ResizeMode.CanMinimize;
        InitializeComponent();
        InitializeLists(Baseforms,CrystalGroups);
    }

private void InitializeLists(ComboBox Baseforms, ComboBox CrystalGroups)
    {
        Baseforms.ItemsSource = Baselist;
        CrystalGroups.ItemsSource = Crystallist;
        Shape Circle = new Ellipse();
        Circle.Stroke = System.Windows.Media.Brushes.Black;
        Circle.Fill = System.Windows.Media.Brushes.DarkBlue;
        Circle.HorizontalAlignment = HorizontalAlignment.Left;
        Circle.VerticalAlignment = VerticalAlignment.Center;
        Circle.Width = 50;
        Circle.Height = 50;
        Shapelist.Add(Circle);
    }

How can I use an ItemsControl to show the shapes in Shapelist in an canvas while also listing them in a Listbox?

Hope this makes the question less broad.

Patrick
  • 123
  • 2
  • 10
  • Take a look at the MVVM pattern. Then create a view model with a collection of your shapes. Bind a ListBox to the shapes collection, and also an ItemsControl that draws the shapes, as e.g. shown here: http://stackoverflow.com/a/22325266/1136211. When you have more specific problems, come back and ask a less broad question. – Clemens Feb 01 '16 at 11:26
  • I edited my question now. Hope its clearer now what I'm trying to do. – Patrick Feb 01 '16 at 16:56

1 Answers1

1

Please try the next solution:

Updated version (xaml and code behind)

  1. DetailsList list view - presents detailed data based on ShapeDataPresentation class (supports multi-select). Shows data by data template named ShapeDataPresentationDataTemplate.
  2. ShapesPresentor items control - presents shapes on canvas (doesn't support multi-select only one can be selected).

ListView XAML code ("This" is the name of the ListView containing window)

<Window x:Class="ListViewWithCanvasPanel.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:listViewWithCanvasPanel="clr-namespace:ListViewWithCanvasPanel"
    Title="MainWindow" Height="350" Width="525" x:Name="This" ResizeMode="CanResize">
<Grid>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="2.5*"></ColumnDefinition>
            <ColumnDefinition Width="4*"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <ListView x:Name="DetailsList" Panel.ZIndex="999" Grid.Column="0" ItemsSource="{Binding ElementName=This, Path=Shapes}" SelectionMode="Extended" SelectionChanged="Selector_OnSelectionChanged">
            <ListView.Resources>
                <DataTemplate x:Key="ShapeDataPresentationDataTemplate" DataType="listViewWithCanvasPanel:ShapeDataPresentation">
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition></ColumnDefinition>
                            <ColumnDefinition></ColumnDefinition>
                            <ColumnDefinition></ColumnDefinition>
                        </Grid.ColumnDefinitions>
                        <TextBlock Grid.Column="0" Text="{Binding Name, StringFormat={}{0:N0}%}"></TextBlock>
                        <TextBlock Grid.Column="1">
                            <Run Text="W:"></Run>
                            <Run Text="{Binding OriginalRectAroundShape.Width}"></Run>
                        </TextBlock>
                        <TextBlock Grid.Column="2">
                            <Run Text="H:"></Run>
                            <Run Text="{Binding OriginalRectAroundShape.Height}"></Run>
                        </TextBlock>
                    </Grid>
                </DataTemplate>
            </ListView.Resources>
            <ListView.ItemContainerStyle>
                <Style TargetType="ListViewItem">
                    <Setter Property="ContentTemplate">
                        <Setter.Value>
                            <DataTemplate DataType="Shape">
                               <ContentControl Content="{Binding Tag}" ContentTemplate="{StaticResource ShapeDataPresentationDataTemplate}"></ContentControl>
                            </DataTemplate>
                        </Setter.Value>
                    </Setter>
                </Style>
            </ListView.ItemContainerStyle>
        </ListView>
        <GridSplitter Grid.Column="0" Width="3" Background="Blue" Panel.ZIndex="999"
          VerticalAlignment="Stretch" HorizontalAlignment="Right" Margin="0"/>
        <ItemsControl Grid.Column="1" x:Name="ShapesPresentor" ItemsSource="{Binding ElementName=This, Path=Shapes}"
                  HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
                  MouseDown="UIElement_OnMouseDown">
            <!--<ListView.Resources>
                <ControlTemplate x:Key="SelectedTemplate" TargetType="ListViewItem">
                    <ContentControl Content="{Binding }"></ContentControl>
                </ControlTemplate>
            </ListView.Resources>-->
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas Background="White" />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <!--<ListView.ItemContainerStyle>
                <Style TargetType="ListViewItem">
                    <Style.Triggers>
                        <MultiTrigger>
                            <MultiTrigger.Conditions>
                                <Condition Property="IsSelected" Value="true" />
                                <Condition Property="Selector.IsSelectionActive" Value="true" />
                            </MultiTrigger.Conditions>
                            <Setter Property="Template" Value="{StaticResource SelectedTemplate}" />
                        </MultiTrigger>
                    </Style.Triggers>
                </Style>
            </ListView.ItemContainerStyle>-->
        </ItemsControl>
    </Grid>
    <StackPanel VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Orientation="Horizontal">
        <ComboBox x:Name="Baseforms"></ComboBox>
        <ComboBox x:Name="CrystalGroups"></ComboBox>
    </StackPanel>
</Grid>

Code behind (updated)

    /// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
    public static readonly DependencyProperty ShapesProperty = DependencyProperty.Register(
        "Shapes", typeof (ObservableCollection<Shape>), typeof (MainWindow),
        new PropertyMetadata(default(ObservableCollection<Shape>)));

    public ObservableCollection<Shape> Shapes
    {
        get { return (ObservableCollection<Shape>) GetValue(ShapesProperty); }
        set { SetValue(ShapesProperty, value); }
    }

    public ObservableCollection<string> Baselist = new ObservableCollection<string> {"a", "b", "c"};
    public ObservableCollection<string> Crystallist = new ObservableCollection<string>{"aa", "bb", "cc"};

public ObservableCollection<Shape> Shapelist = new ObservableCollection<Shape>();
    private SolidColorBrush _originalColorBrush = Brushes.Tomato;
    private SolidColorBrush _selectedColorBrush;
    private double _diameter;

    public MainWindow()
    {
        _diameter = 50d;
        this.ResizeMode = System.Windows.ResizeMode.CanMinimize;
        Shapes = new ObservableCollection<Shape>();
        InitializeComponent();
        InitializeLists(Baseforms, CrystalGroups);
    }

    private void InitializeLists(ComboBox Baseforms, ComboBox CrystalGroups)
    {
        Baseforms.ItemsSource = Baselist;
        CrystalGroups.ItemsSource = Crystallist;
        Shape Circle = new Ellipse();
        Circle.Stroke = System.Windows.Media.Brushes.Black;
        Circle.Fill = System.Windows.Media.Brushes.DarkBlue;
        Circle.HorizontalAlignment = HorizontalAlignment.Left;
        Circle.VerticalAlignment = VerticalAlignment.Center;
        Circle.Width = 50;
        Circle.Height = 50;
        Shapelist.Add(Circle);
    }


    private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e)
    {
        var inputElement = sender as IInputElement;
        if (inputElement == null) return;
        var point = e.GetPosition(inputElement);
        Shape shape = new Ellipse
        {
            Stroke = Brushes.Black,
            Fill = _originalColorBrush,
            Width = _diameter,
            Height = _diameter
        };
        var byX = point.X - _diameter / 2d;
        var byY = point.Y - _diameter / 2d;

        var existingShape = Shapes.FirstOrDefault(shapeToCheck =>
        {
            var data = shapeToCheck.Tag as ShapeDataPresentation;
            if (data == null) return false;
            var res = data.OriginalRectAroundShape.IntersectsWith(new Rect(point,point));
            return res;
        });

        if (existingShape == null)
        {
            var shapeDataPresentation = new ShapeDataPresentation { Name = string.Format("Ox:{0}, Oy:{1}", point.X.ToString("##.###"), point.Y.ToString("##.###")), OriginalRectAroundShape = new Rect(new Point(byX, byY), new Size(_diameter, _diameter)) };
            shape.Tag = shapeDataPresentation;
            shape.ToolTip = new ToolTip{Content = shapeDataPresentation.Name};
            var translateTransform = new TranslateTransform(byX, byY);
            shape.RenderTransform = translateTransform;
            Shapes.Add(shape);
        }
        else
        {
            if (DetailsList.SelectedItems.Contains(existingShape) == false)
            {
                DetailsList.SelectedItems.Clear();
                DetailsList.SelectedItems.Add(existingShape);
            }
        }


    }

    private void Selector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        var currentSelected = e.AddedItems.OfType<Shape>().ToList();
        var prevSelected = e.RemovedItems.OfType<Shape>().ToList();
        if (currentSelected.Count > 0)
        {
            currentSelected.ForEach(shape =>
            {
                _selectedColorBrush = Brushes.CadetBlue;
                shape.Fill = _selectedColorBrush;
            });
        }
        if (prevSelected.Count > 0)
        {
            prevSelected.ForEach(shape =>
            {
                shape.Fill = _originalColorBrush;
            });
        }

    }
}

public class ShapeDataPresentation
{
    public string Name { get; set; }
    public Rect OriginalRectAroundShape { get; set; }
}

How it is looks like here

Summary:

  1. Here you can create item by mouse click on canvas, mouse down handled in code (UIElement_OnMouseDown).
  2. Allowed selection and multi-selection, each time you make selection it will be handled in code (Selector_OnSelectionChanged).

But it is better to use the MVVM based approach to work in wpf.

Regards.

Ilan
  • 2,762
  • 1
  • 13
  • 24
  • Sorry, was away for a few days. Your answers is just what I looked for. Thank you. – Patrick Feb 08 '16 at 16:36
  • Edit: I copied your code to try it out, however, even when I click on the canvas, it wont draw any shapes. It just does nothing. – Patrick Feb 08 '16 at 16:44
  • @Patrick did you named your Window/UserControl as "This"? please post your XAML. – Ilan Feb 08 '16 at 19:19
  • Oh, that was my mistake. Now it works. How one last question: I resized the whole listview and canvas, however now there is a big offset when im clicking at a point on the canvas. It gets placed for more right and far more down than the point I pressed. – Patrick Feb 08 '16 at 19:46
  • @Patrick please see the updated version of code behind. please take this code as a starting point for your solution. – Ilan Feb 08 '16 at 20:07
  • Your updated code behind works just great. You wouldn't know by chance the best way to show the attributes of the shapes in the shapelist in a listbox? If know then a big thanks for your great help anyway. – Patrick Feb 08 '16 at 20:24
  • No problem. I want to show the attributes, only the attributes, not the shape itself, of the shape in a listbox. Would you know an easy way to do so? Or should I upen a new question for this? – Patrick Feb 09 '16 at 08:18
  • @Patrick see updates, feel free to mark it answered, if it was helpful. – Ilan Feb 09 '16 at 10:06