Based on a project from the answer to this question:
Graph nodes coordinates evaluation
and using the MVVM Light Toolkit I'm trying to develop an application that would allow the user not only to move items all around the canvas, but also resize them. Here is what I have so far:
http://screenshooter.net/4766406/tfcqpjw
Main Window XAML
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication2"
xmlns:localvm="clr-namespace:WpfApplication2.ViewModel"
Title="MainWindow" Height="350" Width="927.985" x:Name="view">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="EditorStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<DockPanel>
<StackPanel Width="95" DockPanel.Dock="Left" Margin="5,0,0,0">
<local:ButtonsPanel/>
</StackPanel>
<Grid Margin="10">
<Grid.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisibilityConverter"/>
<CompositeCollection x:Key="Col">
<CollectionContainer Collection="{Binding DataContext.Notes,Source={x:Reference view}}"/>
</CompositeCollection>
<DataTemplate DataType="{x:Type localvm:NoteViewModel}">
<Grid>
<Thumb DragDelta="Thumb_Drag"
IsEnabled="{Binding IsSelected,RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}">
<Thumb.Template>
<ControlTemplate TargetType="Thumb">
<Canvas>
<Path Fill="#FFAA0000" Data="{Binding ShapeGeometry}" Stretch="Fill" x:Name="Path"/>
</Canvas>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBoxItem}}}" Value="True">
<Setter TargetName="Path" Property="Fill" Value="Red"/>
</DataTrigger>
<Trigger Property="IsDragging" Value="True">
<Setter TargetName="Path" Property="Fill" Value="Green"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Thumb.Template>
</Thumb>
</Grid>
</DataTemplate>
</Grid.Resources>
<ListBox SelectedItem="{Binding SelectedNote}"
PreviewMouseMove="ListBox_PreviewMouseMove"
PreviewMouseDown="ListBox_PreviewMouseDown">
<ListBox.Template ... > <!-- Collapsed -->
</ListBox.Template>
<ListBox.ItemsSource>
<StaticResource ResourceKey="Col"/>
</ListBox.ItemsSource>
<ListBox.ItemsPanel ...> <!-- Collapsed -->
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<!-- Moves the object -->
<Setter Property="Canvas.Left" Value="{Binding X}"/>
<Setter Property="Canvas.Top" Value="{Binding Y}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<ContentPresenter x:Name="Content"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
</Grid>
</DockPanel>
Thumb's DragDelta from the MainWindow code behind + the constructor:
public MainWindow()
{
InitializeComponent();
DataContext = new MainViewModel();
}
private void Thumb_Drag(object sender, DragDeltaEventArgs e)
{
var thumb = sender as Thumb;
if (thumb == null)
return;
var note = thumb.DataContext as NoteViewModel;
if (note == null)
return;
note.X += e.HorizontalChange;
note.Y += e.VerticalChange;
}
Important properties and methods from the MainViewModel :
public class MainViewModel : ViewModelBase
{
private ObservableCollection<NoteViewModel> _notes;
public ObservableCollection<NoteViewModel> Notes
{
get { return _notes ?? (_notes = new ObservableCollection<NoteViewModel>()); }
}
private NoteViewModel _selectedNote;
public NoteViewModel SelectedNote {...}
public MainViewModel()
{
_notes = new ObservableCollection<NoteViewModel>(NotesDataSource.GetRandomNotes());
}
#region Creating New Notes
private bool _creatingNewNote;
public bool CreatingNewNote
{
get { return _creatingNewNote; }
set
{
_creatingNewNote = value;
RaisePropertyChanged("CreatingNewNote");
if (value)
CreateNewNote();
else
RemoveNewObjects();
}
}
public void CreateNewNote()
{
var newnote = new NoteViewModel()
{
Name = "Note" + (Notes.Count + 1),
IsNew = true
};
Notes.Add(newnote);
SelectedNote = newnote;
}
public void RemoveNewObjects()
{
Notes.Where(x => x.IsNew).ToList().ForEach(x => Notes.Remove(x));
}
#endregion
}
NoteViewModel:
public class NoteViewModel : ViewModelBase
{
private string _name;
public string Name
{
get { return _name; }
set
{
_name = value;
RaisePropertyChanged("Name");
}
}
#region Coordinates & Size
private double _x;
public double X { ...}
private double _y;
public double Y {...}
private double _width;
public double Width {...}
private double _height;
public double Height {...}
#endregion
#region Shape Geometry
private string _shapeGeometry;
public string ShapeGeometry {...}
#endregion
#region Selection Properties
private bool _isNew;
public bool IsNew {...}
#endregion
public NoteViewModel()
{
Random random = new Random();
var notenumber = random.Next(0, 100);
var notename = "Note" + notenumber;
Name = notename;
X = 10;
Y = 10;
ShapeGeometry = "M39.967 23.133c-0.211 0.189-0.523 0.199-0.748 0.028l-7.443-5.664l-3.526 21.095c-0.013 0.08-0.042 0.153-0.083 0.219 c-0.707 3.024-4.566 5.278-9.104 5.278c-5.087 0-9.226-2.817-9.226-6.28s4.138-6.281 9.226-6.281c2.089 0 4.075 0.466 5.689 1.324 l4.664-26.453c0.042-0.242 0.231-0.434 0.475-0.479c0.237-0.041 0.485 0.068 0.611 0.28l9.581 16.192 C40.227 22.637 40.178 22.945 39.967 23.133z";
Width = CalculateSize("w");
Height = CalculateSize("h");
}
private double CalculateSize(string s) {...}
The Thumb allows for moving, and I was trying to combine the above with the example from here (without Adorners, they look awfully complicated, as all the other examples I found): http://www.codeproject.com/Articles/22952/WPF-Diagram-Designer-Part
However, after racking my brains for a considerably long time, I didn't manage to figure out a workable solution. How can I make my items resize? Please, help a damsel in distress!