Пока нет возможности написать пример, но попробую дополнить ответ когда она появится.
Если количество отображаемых элементов не слишком большое (в пределах 100-1000), то, вероятно, лучшим вариантом будет вынести часть информации в
ViewModel (а именно, координаты расположения линий и состояние
IsSelected). В вашем примере этот класс назван Model, но я рекомендую его переименовать, в дальнейшем это может сбить с толку.
Тогда вы привяжите коллекцию к
ItemsControl, где панелью будет выступать Canvas.
Координаты привяжите индивидуально. Реагировать на изменение цвета можно будет или через триггер или через конвертер, но если вариантов всего 2, то проще, на мой взгляд, будет использовать именно триггер.
При выделении элемента в коллекции сбрасываете состояние
IsSelected предыдущего элемента в
False и указанному соответственно ставите
True.
Обновлено:
Появились несколько свободных минут, привожу пример:
Для начала, для удобства создадим
ViewModelBase, чтобы несколько раз не реализовывать интерфейс
INPC:
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;
}
}
Теперь, как я уже писал выше, нам понадобиться отдельная
ViewModel для ячейки, элемента списка:
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; }
}
Здесь нет необходимости в изменении координат во время работы программы, поэтому сеттеры оставим приватными.
Главная
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)
};
}
}
я не стал заниматься переименовыванием, но лучше, конечно, чтобы свойства отображали реальное назначение.
Теперь
View часть. Привожу только измененный третий столбец:
<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>
Тут тоже все достаточно просто - триггер реагирует на изменение свойства
IsSelected и меняет цвет на красный для текущего элемента.
И, для наглядности, оставляю гифку: