Canvas TwoWay Biding for Coordinates inside ItemsControl - c#

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
}
}

Related

DataContext binding in UserControl WPF

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;
}
}
}
}

WPF ListView Selection inside a treeview I Need to get all the selected ListView Items(when Holding CTRL key) in selection changed event

New to WPF
This is Xaml code Need to Get All Selected ListView Items onto selection changed event
I Tried to work with Multiselecttreeview nuget but the problem is both parent and child are same in that case .
Also TreeviewEX does the same .Any help would be great.
<Window x:Class="DemoApps.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:DemoApps"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TreeView x:Name="TreeViewList" Loaded="TreeViewList_Loaded">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type local:Group}" ItemsSource="{Binding Users}" >
<Grid Width="200" Height="auto">
<StackPanel>
<TextBlock Text="{Binding GroupName}"/>
</StackPanel>
</Grid>
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate DataType="{x:Type local:User}">
<ListView SelectionMode="Extended" SelectionChanged="ListView_SelectionChanged">
<Grid Width="150" Height="20">
<StackPanel>
<TextBlock Text="{Binding UserName}" />
</StackPanel>
</Grid>
</ListView>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
<Setter Property="IsExpanded" Value="{Binding IsExpanded}" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
</Grid>
</Window>
This is my code behind
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
using System.Windows.Controls;
namespace DemoApps
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public ObservableCollection<Group> GroupData { get; set; }
public MainWindow()
{
InitializeComponent();
GroupData = GetDummyData();
}
private ObservableCollection<Group> GetDummyData()
{
var _group = new ObservableCollection<Group>();
for (int i = 0; i < 20; i++)
{
_group.Add(new Group
{
GroupName = "Group name" + i,
Users = GetDummyUsers(i)
});
}
return _group;
}
private ObservableCollection<User> GetDummyUsers(int i)
{
var _user = new ObservableCollection<User>();
for (int j = 0; j < 10; j++)
{
_user.Add(new User
{
UserName = "User " + i + "-" + j
});
}
return _user;
}
private void TreeViewList_Loaded(object sender, RoutedEventArgs e)
{
TreeViewList.ItemsSource = GroupData;
}
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
//Need to get all selected items from List View.
//Here i need to get the selected items
}
}
public class Group : TreeViewItemBase
{
public string GroupName { get; set; }
public ObservableCollection<User> Users { get; set; }
}
public class User
{
public string UserName { get; set; }
}
public class TreeViewItemBase : INotifyPropertyChanged
{
private bool isSelected;
public bool IsSelected
{
get { return this.isSelected; }
set
{
this.isSelected = value;
NotifyPropertyChanged("IsSelected");
}
}
private bool isExpanded;
public bool IsExpanded
{
get { return this.isExpanded; }
set
{
this.isExpanded = value;
NotifyPropertyChanged("IsExpanded");
}
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
How can get all the selected Items of ListView into Selection changed event
The issue is with the data template below:
<HierarchicalDataTemplate.ItemTemplate>
<DataTemplate DataType="{x:Type local:User}">
<ListView SelectionMode="Extended" SelectionChanged="ListView_SelectionChanged">
<Grid Width="150" Height="20">
<StackPanel>
<TextBlock Text="{Binding UserName}" />
</StackPanel>
</Grid>
</ListView>
</DataTemplate>
</HierarchicalDataTemplate.ItemTemplate>
</HierarchicalDataTemplate>
It is creating a ListView per item so only one item can ever be selected.
Change the XAML to display all the users in a ListView
<Window x:Class="DemoApps.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"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<Grid>
<TreeView x:Name="TreeViewList" Loaded="TreeViewList_Loaded">
<TreeView.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding GroupName}"/>
<ListView SelectionMode="Extended" SelectionChanged="ListView_SelectionChanged"
ItemsSource="{Binding Users}">
</ListView>
</StackPanel>
</DataTemplate>
</TreeView.ItemTemplate>
<TreeView.ItemContainerStyle>
<Style TargetType="TreeViewItem">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
<Setter Property="IsExpanded" Value="{Binding IsExpanded}" />
</Style>
</TreeView.ItemContainerStyle>
</TreeView>
</Grid>
</Window>
Get the selected items using
private void ListView_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selectedItems = ((ListView) e.Source).SelectedItems;
}

Why do not bind to the properties Width and Height of Canvas?

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

Databound Flipview remove ViewModel.ItemAt(0) causes flip transition

I am making an article reading app (similar to the Bing News app) and I'm using a FlipView to go between the articles. The FlipView has its ItemsSource databound to an ObservableCollection<T> that holds the content of the article.
I only want to keep 3 articles in the ObservableCollection<T> for memory and performance reasons, so I subscribe to the flipView_SelectionChanged event and remove the item at Length - 1 for going back (to the right) and item at 0 for going forward (to the left)
The problem that I'm having is that when I remove the item at 0 after flipping using the touch gesture, the animation plays a second time.
How do I prevent the transition animation from playing a second time?
Here is an example. Create a new Blank Store App, and add the following:
public sealed partial class MainPage : Page
{
public ObservableCollection<int> Items { get; set; }
private Random _random = new Random(123);
public MainPage()
{
this.InitializeComponent();
Items = new ObservableCollection<int>();
Items.Add(1);
Items.Add(1);
Items.Add(1);
flipview.ItemsSource = Items;
}
private void flipview_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (this.flipview.SelectedIndex == 0)
{
Items.Insert(0, 1);
Items.RemoveAt(Items.Count - 1);
}
else if (this.flipview.SelectedIndex == this.flipview.Items.Count - 1)
{
Items.Add(1);
Items.RemoveAt(0);
}
}
}
and this in the .xaml
<Page.Resources>
<DataTemplate x:Key="DataTemplate">
<Grid>
<Grid.Background>
<LinearGradientBrush EndPoint="1,0.5" StartPoint="0,0.5">
<GradientStop Color="Black"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Grid.Background>
</Grid>
</DataTemplate>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<FlipView x:Name="flipview"
SelectionChanged="flipview_SelectionChanged"
ItemsSource="{Binding}" ItemTemplate="{StaticResource DataTemplate}"/>
</Grid>
The easiest solution is to use UseTouchAnimationsForAllNavigation so that items manipulation in the view model does not cause the animation to occur at all.
<FlipView UseTouchAnimationsForAllNavigation="False" />
If the animation is important to you, then you can simply bind the value for UseTouchAnimationsForAllNavigation in your view model, like this:
<FlipView UseTouchAnimationsForAllNavigation="{Binding ShowAnimations}" />
Does this make sense? If you do it in your view model (option 2) you can simply disable it while manipulating the collection, and then re-enable it so you get animations.
Here's a sample.
Using this code:
public class MyColorModel : BindableBase
{
Color _Color = default(Color);
public Color Color { get { return _Color; } set { SetProperty(ref _Color, value); } }
Visibility _Selected = Visibility.Collapsed;
public Visibility Selected { get { return _Selected; } set { SetProperty(ref _Selected, value); } }
public event EventHandler RemoveRequested;
public void RemoveMe()
{
if (RemoveRequested != null)
RemoveRequested(this, EventArgs.Empty);
}
public event EventHandler SelectRequested;
public void SelectMe()
{
if (SelectRequested != null)
SelectRequested(this, EventArgs.Empty);
}
}
public class MyViewModel : BindableBase
{
public MyViewModel()
{
this.Selected = this.Colors[1];
foreach (var item in this.Colors)
{
item.RemoveRequested += (s, e) =>
{
this.ShowAnimations = false;
this.Colors.Remove(s as MyColorModel);
this.ShowAnimations = true;
};
item.SelectRequested += (s, e) => this.Selected = s as MyColorModel;
}
}
ObservableCollection<MyColorModel> _Colors = new ObservableCollection<MyColorModel>(new[]
{
new MyColorModel{ Color=Windows.UI.Colors.Red },
new MyColorModel{ Color=Windows.UI.Colors.Green },
new MyColorModel{ Color=Windows.UI.Colors.Yellow },
new MyColorModel{ Color=Windows.UI.Colors.Blue },
new MyColorModel{ Color=Windows.UI.Colors.White },
new MyColorModel{ Color=Windows.UI.Colors.Brown },
new MyColorModel{ Color=Windows.UI.Colors.SteelBlue },
new MyColorModel{ Color=Windows.UI.Colors.Goldenrod },
});
public ObservableCollection<MyColorModel> Colors { get { return _Colors; } }
MyColorModel _Selected = default(MyColorModel);
public MyColorModel Selected
{
get { return _Selected; }
set
{
if (_Selected != null)
_Selected.Selected = Visibility.Collapsed;
value.Selected = Visibility.Visible;
SetProperty(ref _Selected, value);
}
}
bool _ShowAnimations = true;
public bool ShowAnimations { get { return _ShowAnimations; } set { SetProperty(ref _ShowAnimations, value); } }
}
public abstract class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void SetProperty<T>(ref T storage, T value, [System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
{
if (!object.Equals(storage, value))
{
storage = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
protected void RaisePropertyChanged([System.Runtime.CompilerServices.CallerMemberName] String propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Try this XAML:
<Page.DataContext>
<local:MyViewModel/>
</Page.DataContext>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="150" />
</Grid.RowDefinitions>
<FlipView ItemsSource="{Binding Colors}" SelectedItem="{Binding Selected, Mode=TwoWay}" UseTouchAnimationsForAllNavigation="{Binding ShowAnimations}">
<FlipView.ItemTemplate>
<DataTemplate>
<Rectangle>
<Rectangle.Fill>
<SolidColorBrush Color="{Binding Color}" />
</Rectangle.Fill>
</Rectangle>
</DataTemplate>
</FlipView.ItemTemplate>
</FlipView>
<ItemsControl ItemsSource="{Binding Colors}" Grid.Row="1" VerticalAlignment="Top" HorizontalAlignment="Center">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<Grid>
<Rectangle Height="100" Width="100" Margin="10">
<Rectangle.Fill>
<SolidColorBrush Color="{Binding Color}" />
</Rectangle.Fill>
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="PointerPressed">
<Core:CallMethodAction MethodName="SelectMe" TargetObject="{Binding}"/>
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</Rectangle>
<TextBlock Text="X" VerticalAlignment="Top" HorizontalAlignment="Right" FontSize="50" Margin="20,10" Foreground="Wheat">
<Interactivity:Interaction.Behaviors>
<Core:EventTriggerBehavior EventName="PointerPressed">
<Core:CallMethodAction MethodName="RemoveMe" TargetObject="{Binding}"/>
</Core:EventTriggerBehavior>
</Interactivity:Interaction.Behaviors>
</TextBlock>
</Grid>
<Rectangle Height="10" Width="100" Margin="10"
Fill="White" Visibility="{Binding Selected}" />
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
Will look like this:
Best of luck!

Data Binding Not Working Properly

In this program, I'm able to add one tab at a time by clicking the "Add Course" button. Ideally, the header of the tab should be the course name I entered and the text in the textbox , which is on the tab, should display the course name.
However, it's not functioning correctly. When I tried to add more than 1 tabs, each time it gives me this error message:
System.Windows.Data Error: 40 : BindingExpression path error: 'Text' property not found on 'object' ''MyHomeworkViewModel' (HashCode=33010577)'. BindingExpression:Path=Text; DataItem='MyHomeworkViewModel' (HashCode=33010577); target element is 'TextBox' (Name=''); target property is 'Text' (type 'String')
Also, it seems to "override" other tab's text (just text, not header). For example, if I add a tab with header "a", the text of that is also "a". Then if I add "B", both textboxes on two tabs become "B". However, if I print out the Text property of each tab (MyHomeworkModel in this case), they are "a" and "B", respectively.
I have been debugging this whole day but no luck. Any help would be appreciated!
My View (DataContext set to MyHomeworkViewModel):
<Window x:Class="MyHomework__MVVM_.MyHomeworkView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="My Homework" Height="450" Width="800" ResizeMode="CanMinimize">
<Grid Margin="0,0,10,10">
<TabControl HorizontalAlignment="Left" Height="330" VerticalAlignment="Top" Width="764" Margin="10,10,0,0" ItemsSource="{Binding AllTabs}" SelectedItem="{Binding SelectedTab}">
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem">
<Setter Property="Header" Value="{Binding Header}"/>
<Setter Property="Content">
<Setter.Value>
<Grid>
<TextBox Text="{Binding Text}" FontSize="16" AcceptsReturn="True" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
</TextBox>
</Grid>
</Setter.Value>
</Setter>
<Setter Property="FontSize" Value="20"/>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
<Button Content="Add Course" HorizontalAlignment="Left" VerticalAlignment="Top" Width="105" Margin="10,351,0,0" Height="50" Command="{Binding AddCourseCommand}"/>
<Button Content="Drop Course" HorizontalAlignment="Left" VerticalAlignment="Top" Width="76" Margin="138,379,0,0" Height="22" Command="{Binding DropCourseCommand, UpdateSourceTrigger=PropertyChanged}"/>
<Button Content="Save HW" HorizontalAlignment="Left" VerticalAlignment="Top" Width="105" Margin="669,351,0,0" Height="50"/>
</Grid>
</Window>
My Model:
using System.ComponentModel;
namespace MyHomework__MVVM_
{
class MyHomeworkModel : INotifyPropertyChanged
{
private string header, text;
public event PropertyChangedEventHandler PropertyChanged;
public string Header
{
get
{
return header;
}
set
{
header = value;
OnPropertyChanged("Header");
}
}
public string Text
{
get
{
return text;
}
set
{
text = value;
OnPropertyChanged("Text");
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
My ViewModel:
using MyHomework;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Input;
namespace MyHomework__MVVM_
{
class MyHomeworkViewModel : INotifyPropertyChanged
{
private ObservableCollection<MyHomeworkModel> allTabs;
private MyHomeworkModel selectedTab;
private MyHomeworkView mainWindow;
public event PropertyChangedEventHandler PropertyChanged;
public MyHomeworkViewModel(MyHomeworkView mainWindow)
{
allTabs = new ObservableCollection<MyHomeworkModel>();
this.mainWindow = mainWindow;
AddCourseCommand = new AddCourseCommand(this);
DropCourseCommand = new DropCourseCommand(this);
}
public ObservableCollection<MyHomeworkModel> AllTabs
{
get
{
return allTabs;
}
set
{
allTabs = value;
OnPropertyChanged("AllTabs");
}
}
public MyHomeworkModel SelectedTab
{
get
{
return selectedTab;
}
set
{
selectedTab = value;
OnPropertyChanged("SelectedTab");
}
}
public ICommand AddCourseCommand
{
get;
private set;
}
public ICommand DropCourseCommand
{
get;
private set;
}
public void AddNewTab()
{
NewCourseName ncn = new NewCourseName();
ncn.Owner = mainWindow;
ncn.ShowDialog();
if (ncn.courseName != null)
{
MyHomeworkModel newTab = new MyHomeworkModel();
newTab.Header = ncn.courseName;
newTab.Text = ncn.courseName;
AllTabs.Add(newTab);
SelectedTab = newTab;
}
foreach (MyHomeworkModel item in AllTabs)
{
Console.WriteLine(item.Text);
}
}
public bool CanDrop()
{
return SelectedTab != null;
}
public void RemoveTab()
{
DropCourseConfirmation dcc = new DropCourseConfirmation();
dcc.Owner = mainWindow;
dcc.ShowDialog();
if (dcc.drop == true)
{
int index = AllTabs.IndexOf(SelectedTab);
AllTabs.Remove(SelectedTab);
if (AllTabs.Count > 0)
{
if (index == 0)
{
SelectedTab = AllTabs[0];
}
else
{
SelectedTab = AllTabs[--index];
}
}
else
{
SelectedTab = null;
}
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Please let me know if you need more codes to help me.
Change your
<Setter Property="Content">
<Setter.Value>
<Grid>
<TextBox Text="{Binding Text}" FontSize="16" AcceptsReturn="True" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
</TextBox>
</Grid>
</Setter.Value>
</Setter>
for this:
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Grid>
<TextBox Text="{Binding Text}" FontSize="16" AcceptsReturn="True" HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
</TextBox>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
Your DataContext isn't what you think it is. Read the error there. It states that "Text" is not a valid property on MyHomeworkViewModel which is true (As opposed to your MyHomeworkModel).
What you need to be modifying instead of the ItemContainerStyle is instead the ItemTemplate and the ContentTemplate which uses the appropriate object within your ItemsSource as its DataContext.
Additionally, the binding in your TextBox needs to be Text="{Binding Text, Mode=TwoWay}" or it won't modify the property in your model.

Categories

Resources