GeralexGR
GeralexGR

Reputation: 3582

Canvas TwoWay Biding for Coordinates inside ItemsControl

I have an ItemsControl with an ItemSource of Entities which is an ObservableCollection<Entity>.

The Entity class contains a string Name and int X,Y properties with public {get;set;}

<ItemsControl  x:Name="myCanvas" Grid.Row="1" ItemsSource="{Binding Path=Entities}" MouseMove="myCanvas_MouseMove" MouseDoubleClick="myCanvas_MouseDoubleClick" MouseUp="myCanvas_MouseUp" MouseDown="myCanvas_MouseDown" >
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemContainerStyle>
        <Style TargetType="ContentPresenter">
          <Setter Property="Canvas.Left" Value="{Binding Path=Y,Mode=TwoWay}"  />
            <Setter Property="Canvas.Top" Value="{Binding Path=X,Mode=TwoWay}" />
        </Style>
    </ItemsControl.ItemContainerStyle>

    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Rectangle Width="50" Height="50" RadiusX="4" RadiusY="4" Stroke="Black" Fill="Red" />
                <Label Content="{Binding Path=Name}"/>
            </Grid>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

When I add new Entity in my collection I can see the change in the UI normally. I want to move by mouse those rectangles, that is why I capture the mouse position in the release event of the ItemsControl and change the Entity positions in my collection Entities but I cannot see any changes in the UI.

MouseUp event

private void myCanvas_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
    Point point = Mouse.GetPosition(sender as FrameworkElement);
    if (e.LeftButton == MouseButtonState.Released)
    {
        Entities[tempIndex].X = (int)point.X;
        Entities[tempIndex].Y = (int)point.Y;           
    }
    var binding = new Binding { Source = Entities };
    myCanvas.SetBinding(ItemsControl.ItemsSourceProperty, binding);
}

tempIndex is the Entity that I want to move.

Upvotes: 0

Views: 256

Answers (1)

J.H.
J.H.

Reputation: 4322

Your Canvas.Left is binding to Y (should be X).

Your Canvas.Top is binding to X (should be Y).

And Clemens is correct. Setting the binding for ItemsSource, in code, is not needed.

Here is some code that moves Entity #2 to the location you click on on myCanvas. I didn't implement the whole drag/drop, just moves the entity to where you click. I added a datagrid to the top to show the values of the Entities.

MainWindow.xaml

<Window x:Class="WpfApp11.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp11"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="2*" />
        </Grid.RowDefinitions>
        <DataGrid ItemsSource="{Binding Path=Entities}" AutoGenerateColumns="True" CanUserAddRows="False" />
        <ItemsControl  x:Name="myCanvas" Grid.Row="1" ItemsSource="{Binding Path=Entities}" MouseMove="myCanvas_MouseMove" MouseDoubleClick="myCanvas_MouseDoubleClick" MouseUp="myCanvas_MouseUp" MouseDown="myCanvas_MouseDown" Background="Beige" >
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <Canvas />
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
            <ItemsControl.ItemContainerStyle>
                <Style TargetType="ContentPresenter">
                    <Setter Property="Canvas.Left" Value="{Binding Path=X,Mode=TwoWay}"  />
                    <Setter Property="Canvas.Top" Value="{Binding Path=Y,Mode=TwoWay}" />
                </Style>
            </ItemsControl.ItemContainerStyle>

            <ItemsControl.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Rectangle Width="50" Height="50" RadiusX="4" RadiusY="4" Stroke="Black" Fill="Red" />
                        <Label Content="{Binding Path=Name}"/>
                    </Grid>
                </DataTemplate>
            </ItemsControl.ItemTemplate>
        </ItemsControl>
    </Grid>
</Window>

MainWindow.xaml.cs

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Input;

namespace WpfApp11
{
    public partial class MainWindow : Window
    {
        public ObservableCollection<Entity> Entities { get; set; }
        private int tempIndex = 1; // Always move box "2"

        public MainWindow()
        {
            InitializeComponent();
            DataContext = this;

            Entities = new ObservableCollection<Entity>()
            {
                new Entity() { Name = "1", X=50, Y=50  },
                new Entity() { Name = "2", X=150, Y=50  },
                new Entity() { Name = "3", X=50, Y=150  },
                new Entity() { Name = "4", X=150, Y=150  },
            };
        }

        private void myCanvas_MouseMove(object sender, MouseEventArgs e)
        {
        }

        private void myCanvas_MouseDoubleClick(object sender, MouseButtonEventArgs e)
        {
        }

        private void myCanvas_MouseDown(object sender, MouseButtonEventArgs e)
        {
        }

        private void myCanvas_MouseUp(object sender, MouseButtonEventArgs e)
        {
            Point point = Mouse.GetPosition(sender as FrameworkElement);
            if (e.LeftButton == MouseButtonState.Released)
            {
                Entities[tempIndex].X = (int)point.X;
                Entities[tempIndex].Y = (int)point.Y;
            }
        }
    }

    public class Entity : INotifyPropertyChanged
    {
        #region INotifyPropertyChanged
        public event PropertyChangedEventHandler PropertyChanged;
        protected bool SetProperty<T>(ref T field, T value, [CallerMemberName]string name = null)
        {
            if (Equals(field, value))
            {
                return false;
            }
            field = value;
            this.OnPropertyChanged(name);
            return true;
        }
        protected void OnPropertyChanged([CallerMemberName]string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }
        #endregion

        #region Property string Name
        private string _Name;
        public string Name { get { return _Name; } set { SetProperty(ref _Name, value); } }
        #endregion

        #region Property int X
        private int _X;
        public int X { get { return _X; } set { SetProperty(ref _X, value); } }
        #endregion

        #region Property int Y
        private int _Y;
        public int Y { get { return _Y; } set { SetProperty(ref _Y, value); } }
        #endregion
    }
}

Upvotes: 1

Related Questions