-1

So I'm using mvvm to bind some elements in an item control witch has a canvas in the ItemsPanel. I want to change Canvas.Top and Left of the elements inside itemsControl when ever it's clicked. xaml file:

<ItemsControl ItemsSource="{Binding Elements}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                    <Canvas></Canvas>
                </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemContainerStyle>
            <Style TargetType="ContentPresenter">
                <Setter  Property="Canvas.Top" Value="{Binding Y}" />
                <Setter Property="Canvas.Left" Value="{Binding X}" />
            </Style>
        </ItemsControl.ItemContainerStyle>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Rectangle MouseLeftButtonDown="ChangeLocation" Width="50" Height="50"/>
            </DataTemplate>
        </ItemsControl.ItemTemplate>
    </ItemsControl>

and here's my viewModel


        private double _X;
        public double X
        {
            get
            {
                return _X;
            }
            set
            {
                _X= value;
                OnPropertyChanged(nameof(_X));
            }
        }
        private double _Y;
        public double Y
        {
            get
            {
                return _Y;
            }
            set
            {
                _Y= value;
                OnPropertyChanged(nameof(_Y));
            }
        }

and listing viewModel

        public IEnumerable<ElementViewModel> Elements => _elements;
        public ObservableCollection<ElementViewModel> _elements { get; set; }
         public ElementListingViewModel()
        {
            //GetElements
        }

So I tried this to change the top and left of the element that get clicked:

     private void ChangeLocation(object sender, MouseEventArgs e)
     {

                Canvas.SetLeft(sender as Rectangle, 200);
                Canvas.SetTop(sender as Rectangle,, 200);
     }

but this doesn't work and also you can't use any of Canvas static methods. Maybe it's because you can't use Canvas methods in ItemsControl, but I set a canvas in the ItemsPanel. So,this ItemControl and canvas should do the same, but it doesn't. what should I do?

Danial
  • 23
  • 8
  • Set X or Y in your item viewmodel. – Andy Jun 27 '23 at 17:51
  • Instead of rectangle you could template a button as a rectangle and then bind command to an icommand in your item viewmodel. – Andy Jun 27 '23 at 17:53
  • I also tried that, but that didnt work either. You mean if the view model changes, the view have to change? – Danial Jun 28 '23 at 03:20

1 Answers1

1

You could set the ChangeLocation event handler by an EventSetter in the ItemContainerStyle. The Bindings must also be TwoWay in order to update the view model when the view changes.

<ItemsControl ItemsSource="{Binding Elements}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemContainerStyle>
        <Style TargetType="ContentPresenter">
            <Setter Property="Canvas.Left" Value="{Binding X, Mode=TwoWay}" />
            <Setter Property="Canvas.Top" Value="{Binding Y, Mode=TwoWay}" />
            <EventSetter Event="MouseLeftButtonDown" Handler="ChangeLocation"/>
        </Style>
    </ItemsControl.ItemContainerStyle>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Rectangle Width="50" Height="50" Fill="Red"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

The sender of the event is a ContentPresenter:

private void ChangeLocation(object sender, MouseButtonEventArgs e)
{
    var cp = (ContentPresenter)sender;
    Canvas.SetLeft(cp, 200);
    Canvas.SetTop(cp, 200);
}

Besides that, you must notify about the change of a property, not its backing field:

OnPropertyChanged(nameof(X)); // not nameof(_X)
OnPropertyChanged(nameof(Y)); // not nameof(_Y)

The Elements property in the main view model could be declared simpler. There is no need for two public properties.

public ObservableCollection<ElementViewModel> Elements { get; }
    = new ObservableCollection<ElementViewModel>();
Clemens
  • 123,504
  • 12
  • 155
  • 268