T
T
toSteel2019-11-21 12:31:37
WPF
toSteel, 2019-11-21 12:31:37

How to link a specific shape to a selected element?

The viewmodel contains a collection of four models. There are four lines in the view. How to change the color of the line depending on the selected model? If the first model is chosen, then the color of the first line changes from blue to red. The second pattern is selected -- the second line turns red, the first line turns blue again.

public class Model : INotifyPropertyChanged
        {
            private string name;


            public string Name
            {
                get { return name; }
                set
                {
                    name = value;
                    OnPropertyChanged("Name");
                }
            }


            public event PropertyChangedEventHandler PropertyChanged;
            public void OnPropertyChanged(string prop)
            {
                if (PropertyChanged != null)
                    PropertyChanged(this, new PropertyChangedEventArgs(prop));
            }
        }


 public class ViewModel : INotifyPropertyChanged
    {
        private Model selectedModel;

        public ObservableCollection<Model> Models { get; set; }
        public Model SelectedModel
        {
            get { return selectedModel; }
            set
            {
                selectedModel = value;
                OnPropertyChanged("SelectedModel");
            }
        }

        public ViewModel()
        {
            Models = new ObservableCollection<Model>
            {
                new Model { Name="A" },
                new Model {Name="B", },
                new Model {Name="C" },
                new Model {Name="D" }
            };
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void OnPropertyChanged(string prop)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new PropertyChangedEventArgs(prop));
        }
    }

<Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="0.8*" />
            <ColumnDefinition Width="0.8*" />
        </Grid.ColumnDefinitions>

        <ListBox
            Grid.Column="0"
            ItemsSource="{Binding Models}"
            SelectedItem="{Binding SelectedModel}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel Margin="5">
                        <TextBlock FontSize="18" Text="{Binding Path=Name}" />

                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>

        <StackPanel Grid.Column="1" DataContext="{Binding SelectedModel}">
            <TextBlock Text="Выбранный элемент" />
            <TextBlock Text="Модель" />
            <TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />

        </StackPanel>
        <Canvas Grid.Column="2">
            <Line
                Canvas.Left="10"
                Canvas.Top="20"
                Stroke="Red"
                X1="0"
                X2="100"
                Y1="0"
                Y2="00" />
            <Line
                Canvas.Left="10"
                Canvas.Top="40"
                Stroke="Blue"
                X1="0"
                X2="100"
                Y1="0"
                Y2="00" />
            <Line
                Canvas.Left="10"
                Canvas.Top="60"
                Stroke="Blue"
                X1="0"
                X2="100"
                Y1="0"
                Y2="00" />
            <Line
                Canvas.Left="10"
                Canvas.Top="80"
                Stroke="Blue"
                X1="0"
                X2="100"
                Y1="0"
                Y2="00" />

        </Canvas>
    </Grid>

Answer the question

In order to leave comments, you need to log in

1 answer(s)
F
Foggy Finder, 2019-11-21
@toSteel

While there is no opportunity to write an example, but I will try to supplement the answer when it appears.
If the number of displayed elements is not too large (in the range of 100-1000), then it is probably best to move some of the information into the ViewModel (namely, the coordinates of the lines and the IsSelected state ). In your example, this class is named Model, but I recommend renaming it, it can be confusing later on.
Then you will bind the collection to the ItemsControl , where the panel will be the Canvas.
Bind coordinates individually. It will be possible to react to a color change either through a trigger or through a converter, but if there are only 2 options, then it will be easier, in my opinion, to use the trigger.
When selecting an element in the collection, reset the IsSelected state of the previous element to False and set the specified value to True .
There are a few free minutes, I give an example:
To begin with, for convenience, let's create ViewModelBase so as not to implement the INPC interface several times :

public abstract class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    protected virtual bool SetProperty<T>(ref T storage, T value, [CallerMemberName] string propertyName = "")
    {
        if (EqualityComparer<T>.Default.Equals(storage, value))
            return false;
        storage = value;
        this.OnPropertyChanged(propertyName);
        return true;
    }
}

Now, as I wrote above, we need a separate ViewModel for the cell, the list element:
public class LineVM : ViewModelBase
{
    private bool isSelected;
    public bool IsSelected
    {
        get { return isSelected; }
        set { SetProperty(ref isSelected, value); }
    }

    public LineVM(string name, int x, int y)
    {
        Name = name;
        X = x;
        Y = y;
    }
    public string Name { get; set; }

    public int X { get; private set; }

    public int Y { get; private set; }
}

There is no need to change the coordinates while the program is running, so we'll leave the setters private.
Main ViewModel :
public class ItemsVM : ViewModelBase
{
    private LineVM selectedVM;

    public ObservableCollection<LineVM> Models { get; }
    public LineVM SelectedModel
    {
        get { return selectedVM; }
        set
        {
            if (SelectedModel != null)
                SelectedModel.IsSelected = false;
            value.IsSelected = true;
            SetProperty(ref selectedVM, value);
        }
    }

    public ItemsVM()
    {
        Models = new ObservableCollection<LineVM>
        {
            new LineVM ("A", 10, 20 ),
            new LineVM ("B", 10, 40),
            new LineVM ("C", 10, 60),
            new LineVM ("D", 10, 80)
        };
    }
}

I did not bother with renaming, but it is better, of course, that the properties reflect the real purpose.
Now the View part. I give only the modified third column:
<ItemsControl Grid.Column="2" ItemsSource="{Binding Models}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas IsItemsHost="True" />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Line
            X1="0"
            X2="100"
            Y1="0"
            Y2="00">
                <Line.Style>
                    <Style TargetType="Line">
                        <Setter Property="Stroke" Value="Blue" />
                        <Style.Triggers>
                            <DataTrigger Binding="{Binding IsSelected}" Value="True">
                                <Setter Property="Stroke" Value="Red" />
                            </DataTrigger>
                        </Style.Triggers>
                    </Style>
                </Line.Style>
            </Line>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
    <ItemsControl.ItemContainerStyle>
        <Style TargetType="ContentPresenter">
            <Setter Property="Canvas.Left" Value="{Binding X}" />
            <Setter Property="Canvas.Top" Value="{Binding Y}" />
        </Style>
    </ItemsControl.ItemContainerStyle>
</ItemsControl>

Here, too, everything is quite simple - the trigger reacts to a change in the IsSelected property and changes the color to red for the current element.
And, for clarity, I leave a gif:
5dd6e7e03664e626729135.gif

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question