Why do not bind to the properties Width and Height of Canvas?
I tried to do it like that, but it did not .
<ItemsControl
Grid.Row="0"
ItemsSource="{Binding RectItems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
//does not work
<Canvas Height="{Binding PanelHeight, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Width="{Binding PanelWidth, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding X}" />
<Setter Property="Canvas.Top" Value="{Binding Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle
Width="{Binding Width}"
Height="{Binding Height}"
Fill="Black" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Those bindings seem to work just fine; just add
Background="Red"
to the definition of Canvas so that you can SEE the actual Canvas.
I've made you an example:
Classes
public class RectItem : INotifyPropertyChanged
{
private int _width;
private int _height;
private int _x;
private int _y;
public Brush Color { get; set; }
public int Width {
get { return _width; }
set {
if (value == _width) return;
_width = value;
OnPropertyChanged();
}
}
public int Height {
get { return _height; }
set {
if (value == _height) return;
_height = value;
OnPropertyChanged();
}
}
public int X {
get { return _x; }
set {
if (value == _x) return;
_x = value;
OnPropertyChanged();
}
}
public int Y {
get { return _y; }
set {
if (value == _y) return;
_y = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
internal class ViewModel : INotifyPropertyChanged
{
public ViewModel()
{
this.RectItems.Add(new RectItem { Height = 100, Width = 100, X = 0, Y = 0, Color = Brushes.DeepPink });
this.RectItems.Add(new RectItem { Height = 50, Width = 50, X = 100, Y = 100, Color = Brushes.DeepSkyBlue });
}
private double _panelWidth = 100;
private double _panelHeight = 100;
public ObservableCollection<RectItem> RectItems { get; } = new ObservableCollection<RectItem>();
public ICommand IncreaseSizeCommand => new RelayCommand(x =>
{
this.PanelHeight = 200;
this.PanelWidth = 200;
});
public double PanelWidth {
get { return _panelWidth; }
set {
if (value.Equals(_panelWidth)) return;
_panelWidth = value;
OnPropertyChanged();
}
}
public double PanelHeight {
get { return _panelHeight; }
set {
if (value.Equals(_panelHeight)) return;
_panelHeight = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
XAML
<StackPanel>
<Button Content="Click me" Width="80" Height="20" Command="{Binding IncreaseSizeCommand}"/>
<ItemsControl
Grid.Row="0"
ItemsSource="{Binding RectItems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Height="{Binding PanelHeight, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Width="{Binding PanelWidth, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding X}" />
<Setter Property="Canvas.Top" Value="{Binding Y}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle
Width="{Binding Width}"
Height="{Binding Height}"
Fill="{Binding Color}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
Works as aspected. I've added some colors to do some visual highlighting and a command to change the size of the Canvas
Related
Trying to get DataContext in UserControl.
My
structure
I have the model Car
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace AutoShop.MVVM.Model
{
class Car : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string prop = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
private string _Model;
public string Model
{
get
{
return _Model;
}
set
{
_Model = value;
OnPropertyChanged();
}
}
private string _Mark;
public string Mark
{
get
{
return _Mark;
}
set
{
_Mark = value;
OnPropertyChanged();
}
}
private float _Volume;
public float Volume
{
get
{
return _Volume;
}
set
{
_Volume = value;
OnPropertyChanged();
}
}
private int _DateOfIssue;
public int DateOfIssue
{
get
{
return _DateOfIssue;
}
set
{
_DateOfIssue = value;
OnPropertyChanged();
}
}
public enum EngineTypes {
DISEL,
PETROL
};
private EngineTypes _EngineType;
public EngineTypes EngineType
{
get
{
return _EngineType;
}
set
{
_EngineType = value;
OnPropertyChanged();
}
}
private string _ImageUrl;
public string ImageUrl
{
get
{
return _ImageUrl;
}
set
{
_ImageUrl = value;
OnPropertyChanged();
}
}
public Car()
{
}
}
}
And I have main view model
using AutoShop.MVVM.Model;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
namespace AutoShop.MVVM.ViewModel
{
class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string prop = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
public HomeViewModel HomeVM;
private object _CurrentPage;
public object CurrentPage
{
get
{
return _CurrentPage;
}
set
{
_CurrentPage = value;
OnPropertyChanged();
}
}
private List<Car> _Cars;
public List<Car> Cars
{
get
{
return _Cars;
}
set
{
_Cars = value;
OnPropertyChanged();
}
}
public MainViewModel()
{
Cars = new List<Car>() {
new Car
{
Mark = "Audi",
Model = "asdf",
Volume = 1.4F,
DateOfIssue = 2019,
EngineType = Car.EngineTypes.DISEL,
ImageUrl = "img/img"
},
new Car
{
Mark = "Moto",
Model = "asdf",
Volume = 1.4F,
DateOfIssue = 2019,
EngineType = Car.EngineTypes.DISEL,
ImageUrl = "img/img"
},
new Car
{
Mark = "Some",
Model = "asdf",
Volume = 1.4F,
DateOfIssue = 2019,
EngineType = Car.EngineTypes.DISEL,
ImageUrl = "img/img"
}
};
HomeVM = new HomeViewModel();
CurrentPage = HomeVM;
}
}
}
I need to display cars on the ProductPage.xaml and I do it by the next way
<UserControl x:Class="AutoShop.MVVM.View.ProductPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:AutoShop.MVVM.View"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
>
<StackPanel Background="#fff">
<WrapPanel>
<Grid Width="200px" Margin="30 0 0 0">
<TextBox x:Name="field4" Tag="C:\Users\user\Desktop\Learning\Весенний семестр\ООТП\AutoShop\AutoShop\Images\u.png" Style="{StaticResource TextBoxTemplate}" />
<TextBlock IsHitTestVisible="False" Text="Марка" Padding="20 10 0 0">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Visibility" Value="Collapsed"/>
<Setter Property="FontFamily" Value="/Fonts/#Montserrat Light" />
<Setter Property="Foreground" Value="#ccc" />
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=field4}" Value="">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
<Grid Width="200px" Margin="30 0 0 0">
<TextBox x:Name="field7" Tag="C:\Users\user\Desktop\Learning\Весенний семестр\ООТП\AutoShop\AutoShop\Images\u.png" Style="{StaticResource TextBoxTemplate}" />
<TextBlock IsHitTestVisible="False" Text="username" Padding="20 10 0 0">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Visibility" Value="Collapsed"/>
<Setter Property="FontFamily" Value="/Fonts/#Montserrat Light" />
<Setter Property="Foreground" Value="#ccc" />
<Style.Triggers>
<DataTrigger Binding="{Binding Text, ElementName=field7}" Value="">
<Setter Property="Visibility" Value="Visible" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
<Grid Width="200px" Margin="30 0 0 0">
<ComboBox Style="{StaticResource ComboBoxTheme}" SelectedIndex="0">
<ComboBoxItem>
<TextBlock Text="asdasdasd" />
</ComboBoxItem>
<ComboBoxItem>
<TextBlock Text="fsdfsd" />
</ComboBoxItem>
</ComboBox>
</Grid>
</WrapPanel>
<Grid Height="400">
<ScrollViewer VerticalScrollBarVisibility="Auto">
<ItemsControl x:Name="ItemsWrapper" ItemsSource="{Binding Cars}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Margin="10" BorderBrush="Black" BorderThickness="2" Height="279">
<Grid Height="279" Width="200">
<Image Source="{Binding Path=Image}" Width="100" Height="100" VerticalAlignment="Top" Margin="0 10 0 0" />
<TextBlock Text="{Binding Path=Name, StringFormat='Name: {0}'}" VerticalAlignment="Top" Margin="0,120,0,0"/>
<TextBlock Text="{Binding Path=Mark, StringFormat='Rating: {0}' }" VerticalAlignment="Top" Margin="0 180 0 0" />
<TextBlock Text="{Binding Path=Model, StringFormat='Category: {0}'}" VerticalAlignment="Top" Margin="0,200,0,0" />
<TextBlock Text="{Binding Path=Volume, StringFormat='Price: {0}'}" VerticalAlignment="Top" Margin="0,160,0,0" />
<TextBlock Text="{Binding Path=DateOfIssue, StringFormat='Description: {0}'}" VerticalAlignment="Top" Margin="0,140,0,0" />
</Grid>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Grid>
</StackPanel>
</UserControl>
And it the MainForm.xaml I added ProductPage.xaml
<ContentControl Margin="10" Grid.Column="1" Content="{Binding CurrentPage}"/>
The problem is that nothing is being outputted, I think it might be due to the loss of context. How do I properly pass the context to UserControl (ProductPage.xaml)?
ProductPage.xaml
Update:
I set DataContext for MainWindow
And DataContext working becouse {Binding CurrentPage} is working, but binding on Cars field is not working
Add this to the Window.Resources. Of course DataContext must be set for the MainForm.
<Window>
<Window.Resources>
<DataTemplate DataType="{x:Type xmlnsForVewModel:MainViewModel}">
<ProductPage/>
</DataTemplate>
</Window.Resources>
</Window>
and if for ProductPage HomeViewModel supposed to be a DataContext, then HomeViewModel has to have Cars property, not MainViewModel.
1.find the MainForm.xaml.cs
2.find ctor
public MainForm()
{
InitializeComponent();
//set the MainForm DataContext
DataContext = new MainViewModel();
}
3.you should add ContentControl.DataTemplate to show your HomeViewModel
----20210427 update
Here demo i make, project name is WpfApp4
MainWindow.xaml
<Window x:Class="WpfApp4.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:WpfApp4"
xmlns:views="clr-namespace:WpfApp4.Views"
xmlns:viewModels="clr-namespace:WpfApp4.ViewModels"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<viewModels:MainWindowViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<ListBox Grid.Column="0" ItemsSource="{Binding ItemsSource, Mode=OneWay}" SelectedItem="{Binding SelectedCarViewModel, Mode=TwoWay}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Grid Grid.Column="1">
<ContentControl Content="{Binding SelectedCarViewModel}">
<ContentControl.ContentTemplate>
<!--Style your product page here-->
<DataTemplate DataType="{x:Type viewModels:CarViewModel}">
<UniformGrid Columns="2">
<TextBlock Text="Name"/>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="Volume"/>
<TextBlock Text="{Binding Volume}"/>
<TextBlock Text="Price"/>
<TextBlock Text="{Binding Price}"/>
</UniformGrid>
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
</Grid>
</Grid>
</Window>
MainWindow.xaml.cs
using System.Windows;
namespace WpfApp4
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
MainWindowViewModel.cs
using System.Collections.ObjectModel;
namespace WpfApp4.ViewModels
{
class MainWindowViewModel : ViewModelBase
{
public ObservableCollection<CarViewModel> ItemsSource { get; } = new ObservableCollection<CarViewModel>();
private CarViewModel selectedCarViewModel;
public CarViewModel SelectedCarViewModel
{
get { return this.selectedCarViewModel; }
set { SetProperty(ref this.selectedCarViewModel, value); }
}
public MainWindowViewModel()
{
ItemsSource.Add(new CarViewModel() { Name = "BMW", Volume = 10, Price = 100 });
ItemsSource.Add(new CarViewModel() { Name = "Toyota", Volume = 5, Price = 80 });
ItemsSource.Add(new CarViewModel() { Name = "Benz", Volume = 20, Price = 150 });
// don't let view binding null value, so here initialze property "SelectedCarViewModel"
this.selectedCarViewModel = ItemsSource[0];
}
}
}
CarViewModel.cs
namespace WpfApp4.ViewModels
{
class CarViewModel : ViewModelBase
{
private string name;
public string Name
{
get { return this.name; }
set { SetProperty(ref this.name, value); }
}
private float volume;
public float Volume
{
get { return this.volume; }
set { SetProperty(ref this.volume, value); }
}
private decimal price;
public decimal Price
{
get { return this.price; }
set { SetProperty(ref this.price, value); }
}
}
}
ViewModelBase.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using System.Threading;
namespace WpfApp4.ViewModels
{
public class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected bool SetProperty<T>(ref T t, T value, [CallerMemberName] string propertyName = "")
{
if (EqualityComparer<T>.Default.Equals(t, value))
{
return false;
}
else
{
t = value;
RaisePropertyChanged(propertyName);
return true;
}
}
}
}
I have a TabControl which has several TabItems. In each TabItem, there are a number of Buttons. A tabItem is stand for a room, and in the room there are several tables (Button)
I have bound one TabItem, but I am not sure how to bind lists of TabItems in a TabControl.
MainWindow:
<TabControl Grid.Row="0" Name="tabTables" Margin="-1 -5 -1 -1" Background="AliceBlue" BorderBrush="White">
<TabItem Visibility="Collapsed">
<ItemsControl ItemsSource="{Binding TableCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas x:Name="canvas1"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type vm:TableViewModel}">
<Button Uid="{Binding TableName}" ContentStringFormat="{Binding TableGuestCount}" Style="{StaticResource ResourceKey=BtnTableEmpty}" Width="84" Height="90" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Path=TablePosX}" />
<Setter Property="Canvas.Top" Value="{Binding Path=TablePosY}" />
<Setter Property="Tag" Value="{Binding Path=TableID}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</TabItem>
</TabControl>
TableAreaViewModel:
public class TableAreaViewModel:BaseViewModel
{
public TableAreaViewModel()
{
TableCollection = new ObservableCollection<TableViewModel>();
}
public AreaViewModel Area { get; set; }
public ObservableCollection<TableViewModel> TableCollection { get; set;
}
}
AreaViewModel:
public class AreaViewModel: BaseViewModel
{
TableGrp _tbGrp = null;
public AreaViewModel(TableGrp tbGrp)
{
_tbGrp = tbGrp;
}
public string GrpID { get { return _tbGrp.GrpID; } }
public string GrpName { get { return _tbGrp.GrpName; } }
}
}
TableViewModel:
public class TableViewModel : BaseViewModel
{
private Table _table = null;
public TableViewModel(Table tb)
{
_table = tb;
}
public string TableID
{
get { return _table.TableID; }
set { _table.TableID = value; }
}
public string TableName
{
get { return _table.Name; }
set { _table.Name = value; }
}
public string TableGuestCount
{
get
{
int customerCnt = _table.CustCount ?? 0;
return string.Format("{0}/{1}", customerCnt, _table.MaxCount);
}
}
public double TablePosX
{
get { return _table.ShowXValue ?? 10; }
set { _table.ShowXValue = value; }
}
public double TablePosY
{
get { return _table.ShowYValue ?? 10; }
set { _table.ShowYValue = value; }
}
}
TableAreaListViewModel:
public class TableAreaListViewModel
{
public TableAreaListViewModel()
{
TableAreaCollection = new ObservableCollection<TableAreaViewModel>();
LoadTables();
}
public ObservableCollection<TableAreaViewModel> TableAreaCollection { get; set; }
private void LoadTables()
{
TableManager mgr = new TableManager();
var list = mgr.LoadTableAreas();
foreach (var tb in list)
{
TableAreaViewModel ta = new TableAreaViewModel();
ta.Area = new AreaViewModel(tb.TableGroup);
foreach(var item in tb.Tables )
{
ta.TableCollection.Add(new TableViewModel(item));
}
TableAreaCollection.Add(ta);
}
}
}
You would want to bind the ItemSource for the TabControl to you collection to generate the tabs. Something like this:
<TabControl ItemsSource="{Binding TableAreaCollection}">
<TabControl.ItemTemplate>
<DataTemplate>
<ItemsControl ItemsSource="{Binding TableCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas x:Name="canvas1"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type vm:TableViewModel}">
<Button Uid="{Binding TableName}" ContentStringFormat="{Binding TableGuestCount}" Style="{StaticResource ResourceKey=BtnTableEmpty}" Width="84" Height="90" />
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Path=TablePosX}" />
<Setter Property="Canvas.Top" Value="{Binding Path=TablePosY}" />
<Setter Property="Tag" Value="{Binding Path=TableID}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
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.
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
}
}
The Title explains my question I guess. I have one root rectangle which has children rectangles which can also have children rectangles. What would be the best way to draw all of them dynamically on a canvas?
My Rectangle-ViewModel:
public class SketchRectangleViewModel:ViewModelBase
{
public SketchRectangleViewModel(SketchRectangle sr)
{
_id = sr.Id;
_x = sr.x;
_y = sr.y;
_height = sr.Height;
_width = sr.Width;
_name = sr.Name;
_parentId = sr.ParentId;
}
private Guid _id;
private int _x;
private int _y;
private int _height;
private int _width;
private Guid _parentId;
private string _name;
private ObservableCollection<SketchRectangleViewModel> _children = new ObservableCollection<SketchRectangleViewModel>();
private bool _isSelected;
}
You could make a flat collection of all SketchRectangleViewModel objects in your view model:
How to flatten tree via LINQ?
...and bind the collection of all SketchRectangleViewModel objects to an ItemsControl:
<ItemsControl ItemsSource="{Binding YourCollection}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Width="100" Height="100" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding X}"/>
<Setter Property="Canvas.Top" Value="{Binding Y}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Rectangle Width="{Binding Width}" Height="{Binding Height}" Fill="Green" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Note that you can only bind to public properties of the SketchRectangleViewModel class, so you need to turn your fields into properties:
public double X { get; set; }
I have done this by using static records in xaml and which works perfectly, here in this example i have added three levels of expander view, my entire xaml code is below
<toolkit:ExpanderView x:Name="India" Header="India" FontSize="40" >
<toolkit:ExpanderView.Items>
<toolkit:ExpanderView x:Name="Karnataka" Header="Karnataka" FontSize="40" >
<toolkit:ExpanderView.Items>
<toolkit:ExpanderView x:Name="Bangalore" Header="Bangalore" FontSize="40" >
<toolkit:ExpanderView.Items>
<TextBlock Text="RamamurthyNagar"></TextBlock>
<TextBlock Text="VasanthNagar"></TextBlock>
<TextBlock Text="CentralSilkBoard"></TextBlock>
</toolkit:ExpanderView.Items>
</toolkit:ExpanderView>
</toolkit:ExpanderView.Items>
</toolkit:ExpanderView>
<toolkit:ExpanderView x:Name="TamilNadu" Header="TamilNadu" FontSize="40" >
<toolkit:ExpanderView.Items>
<toolkit:ExpanderView x:Name="Coimbatore" Header="Coimbatore" FontSize="40" >
<toolkit:ExpanderView.Items>
<toolkit:ExpanderView x:Name="GandhiPuram" Header="Gandhipuram" FontSize="40" Width="200">
<toolkit:ExpanderView.Items>
<toolkit:ExpanderView x:Name="Sidhhapudhur" Header="Sidhhapudhur" FontSize="40" >
</toolkit:ExpanderView>
</toolkit:ExpanderView.Items>
</toolkit:ExpanderView>
<toolkit:ExpanderView x:Name="SaibabaColony" Header="SaibabaColony" FontSize="40" Width="200" >
</toolkit:ExpanderView>
</toolkit:ExpanderView.Items>
</toolkit:ExpanderView>
</toolkit:ExpanderView.Items>
</toolkit:ExpanderView>
</toolkit:ExpanderView.Items>
</toolkit:ExpanderView>
<toolkit:ExpanderView x:Name="Canada" Header="Canada" FontSize="40" >
<toolkit:ExpanderView.Items>
<toolkit:ExpanderView x:Name="BritishColombia" Header="BritishColombia" FontSize="40" >
<toolkit:ExpanderView.Items>
<TextBlock Text="Vancouver"></TextBlock>
</toolkit:ExpanderView.Items>
</toolkit:ExpanderView>
<toolkit:ExpanderView x:Name="Ontario" Header="Ontario" FontSize="40" >
<toolkit:ExpanderView.Items>
<TextBlock Text="Toronoto"></TextBlock>
</toolkit:ExpanderView.Items>
</toolkit:ExpanderView>
</toolkit:ExpanderView.Items>
</toolkit:ExpanderView>
<toolkit:ExpanderView x:Name="UnitedStates" Header="UnitedStates" FontSize="40" />
i want to bind the same details from code behind. Thanks in advance:)
You could do something like this:
//Header1 is the name of your Expander View
Header1.Header = "Expander Header";
stackpanel.Children.Add(Header1);
this.Header1.ItemsSource = new List<string>() { "Afganistan","Australia", "Bangladesh", "Ireland", "New Zealand", "South Africa", "Sri Lanka", "Zimbabwe" };
Reference : Defining ExpanderView Control using CSharp
For Data Binding, you could have a look at this.
Here the solution for my question.
Xaml Code:
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ListBox Grid.Row="0" x:Name="listCountries">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<toolkit:ExpanderView Header="{Binding}" IsExpanded="{Binding IsExpanded1, Mode=TwoWay}" HeaderTemplate="{StaticResource FirstLevelHeaderTemplate}">
<!--ItemsSource="{Binding states}" ItemTemplate="{StaticResource FirstLevelItemTemplate}"-->
<toolkit:ExpanderView.Items>
<Grid Height="Auto">
<ListBox x:Name="listStates" ItemsSource="{Binding states}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<toolkit:ExpanderView Header="{Binding}"
IsExpanded="{Binding IsExpanded2, Mode=TwoWay}"
HeaderTemplate="{StaticResource SecondLevelHeaderTemplate}">
<!-- ItemsSource="{Binding cities}" ItemTemplate="{StaticResource SecondLevelItemTemplate}"-->
<toolkit:ExpanderView.Items>
<Grid Height="Auto">
<ListBox x:Name="listCities" ItemsSource="{Binding cities}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<!--<TextBlock Text="{Binding City}"></TextBlock>-->
<toolkit:ExpanderView Header="{Binding}"
IsExpanded="{Binding IsExpanded3, Mode=TwoWay}"
HeaderTemplate="{StaticResource ThirdLevelHeaderTemplate}">
<!-- ItemsSource="{Binding cities}" ItemTemplate="{StaticResource SecondLevelItemTemplate}"-->
<toolkit:ExpanderView.Items>
<Grid Height="Auto">
<ListBox x:Name="listAreas" ItemsSource="{Binding areas}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="VerticalContentAlignment" Value="Stretch"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Area}"></TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</toolkit:ExpanderView.Items>
</toolkit:ExpanderView>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</toolkit:ExpanderView.Items>
</toolkit:ExpanderView>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</toolkit:ExpanderView.Items>
</toolkit:ExpanderView>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
CS Code:
public partial class SecondPage : PhoneApplicationPage
{
public SecondPage()
{
InitializeComponent();
List<Countries> objCountries = new List<Countries>()
{
new Countries() { Country = "India", states = new List<States>
{
new States() { State = "Karnataka" , cities = new List<Cities>
{
new Cities(){ City = "Bangalore", areas = new List<Areas>
{
new Areas(){Area="RamamurthyNagar"},
new Areas(){Area="VasanthNagar"},
new Areas(){Area="RamamurthyNagar"}}
},
}},
new States() { State = "TamilNadu", cities = new List<Cities>
{
new Cities(){ City = "Coimbatore", areas=null}}
},
}},
new Countries() { Country = "Canada", states = new List<States>
{
new States() { State = "BritishColombia" , cities = new List<Cities>
{
new Cities(){ City = "Vancouver" , areas = null}}
},
new States() { State = "Ontario", cities = new List<Cities>
{
new Cities(){ City = "Toronto", areas =null}}
},
}},
new Countries() { Country = "UnitedStates", states = new List<States>
{
new States() { State = "NewHampshire" , cities = new List<Cities>
{
new Cities(){ City = "Dover", areas= null}}
},
new States() { State = "NewJersy", cities = new List<Cities>
{
new Cities(){ City = "Jersycity", areas=null}}
},
}},
};
this.listCountries.ItemsSource = objCountries;
}
}
public class Countries : INotifyPropertyChanged
{
private bool isExpanded1;
public string Country { get; set; }
public IList<States> states { get; set; }
public bool IsExpanded1
{
get
{
return this.isExpanded1;
}
set
{
if (this.isExpanded1 != value)
{
this.isExpanded1 = value;
this.OnPropertyChanged("IsExpanded");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class States : INotifyPropertyChanged
{
public string State { get; set; }
public IList<Cities> cities { get; set; }
private bool isExpanded2;
public bool IsExpanded2
{
get
{
return this.isExpanded2;
}
set
{
if (this.isExpanded2 != value)
{
this.isExpanded2 = value;
this.OnPropertyChanged("IsExpanded");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class Cities : INotifyPropertyChanged
{
public string City { get; set; }
public IList<Areas> areas { get; set; }
private bool isExpanded3;
public bool IsExpanded3
{
get
{
return this.isExpanded3;
}
set
{
if (this.isExpanded3 != value)
{
this.isExpanded3 = value;
this.OnPropertyChanged("IsExpanded");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
public class Areas
{
public string Area { get; set; }
}