WPF binding to nested List , Listbox not updating - c#

I'm currently trying to bind my list to a list that is in another object from another list. But in the second listbox the Items just doesn't appear.
XAML
<Window x:Class="Managing_program.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:Managing_program"
mc:Ignorable="d"
Title="Dashboard" Height="416.701" Width="867.828">
<Grid>
<TabControl Margin="0,26,0,0">
<TabItem Header="Tables">
<Grid Background="White" Margin="0,0,-2,0">
<Button Content="Add" Margin="10,0,0,10" Height="20" VerticalAlignment="Bottom" HorizontalAlignment="Left" Width="75" Command="{Binding AddTableCommand}"/>
<Button Content="Remove" Margin="90,0,0,10" HorizontalAlignment="Left" Width="75" Height="20" VerticalAlignment="Bottom" Command="{Binding RemoveTableCommand}"/>
<GroupBox Header="Fields" HorizontalAlignment="Left" Margin="204,0,0,35" Width="192" FontWeight="Bold">
<Grid ShowGridLines="False">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<ListBox Grid.Row="0" BorderThickness="2" ItemsSource="{Binding SelectedTable.Fields}" SelectedItem="{Binding SelectedField}" Margin="0 4" FontWeight="Normal">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="test"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<TextBox Grid.Row="1" Margin="2" Text="{Binding FieldNameToAdd, Mode=TwoWay , UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</GroupBox>
<GroupBox Header="Tables" HorizontalAlignment="Left" Width="199" Margin="0,0,0,35" FontWeight="Bold">
<Grid ShowGridLines="False">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<TextBox x:Name="tbAddTable" Grid.Row="1" Margin="2" Text="{Binding Path=TableNameToAdd, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<ListBox Grid.Row="0" ItemsSource="{Binding Tables}" SelectedItem="{Binding SelectedTable}" BorderThickness="2" Margin="0 4" FontWeight="Normal">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Label Content="{Binding Name}"/>
<Label Content= "{Binding Fields.Count, StringFormat='Fields: {0}'}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
</GroupBox>
<Button Content="Add" Margin="207,0,0,10" Height="20" VerticalAlignment="Bottom" HorizontalAlignment="Left" Width="75" Command="{Binding AddFieldCommand}"/>
<Button Content="Remove" Margin="287,0,0,10" HorizontalAlignment="Left" Width="75" Height="20" VerticalAlignment="Bottom" Command="{Binding RemoveFieldCommand}"/>
<GroupBox Header="Field properties" Margin="401,0,10,35" FontWeight="Bold">
<Grid Margin="10,10,-2,5" ShowGridLines="True" DataContext="{Binding SelectedField}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="28"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<Label Grid.Row="0" Grid.Column="0" Content="Name:"/>
<Label Grid.Row="1" Grid.Column="0" Content="Label:"/>
<Label Grid.Row="2" Grid.Column="0" Content="Type:"/>
<Label Grid.Row="3" Grid.Column="0" Content="Reference table:"/>
<Label Grid.Row="4" Grid.Column="0" Content="Visible:"/>
<Label Grid.Row="0" Grid.Column="1" Content="{Binding Name}" Margin="10,0,0,0"/>
<TextBox Grid.Row="1" Grid.Column="1" Text="{Binding Label}" Margin="5,2,0,2"/>
<ComboBox Grid.Column="1" Margin="5,5,0,5" Grid.Row="2" BorderBrush="White" RenderTransformOrigin="1.029,0.62">
</ComboBox>
<ComboBox Grid.Column="1" Margin="5,5,-0,121" Grid.Row="3" BorderBrush="White" RenderTransformOrigin="1.029,0.62">
</ComboBox>
</Grid>
</GroupBox>
</Grid>
</TabItem>
<TabItem Header="TabItem">
</TabItem>
</TabControl>
</Grid>
</Window>
MainViewModel:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace Managing_program.model
{
class MainWindowViewModel: ViewModelBase
{
public MainWindowViewModel()
{
// registering all commands
Tables = new ObservableCollection<Table>();
AddTableCommand = new DelegateCommand(ExecuteAddTableCommand, CanExecuteAddTableCommand);
RemoveTableCommand = new DelegateCommand(ExecuteRemoveTableCommand, CanExecuteRemoveTableCommand);
AddFieldCommand = new DelegateCommand(ExecuteAddFieldCommand, CanExecuteAddFieldCommand);
}
String _fieldNameToAdd = String.Empty;
public String FieldNameToAdd {
get { return _fieldNameToAdd; }
set {
_fieldNameToAdd = value;
OnPropertyChanged("FieldNameToAdd");
}
}
String _tableNameToAdd = string.Empty;
public String TableNameToAdd {
get {return _tableNameToAdd;}
set {
_tableNameToAdd = value;
OnPropertyChanged("TableNameToAdd");
}
}
#region allCommands:
// Add Table Command
public ICommand AddTableCommand { get; set; }
private void ExecuteAddTableCommand(object parameter)
{
Tables.Add(new Table(TableNameToAdd));
TableNameToAdd = String.Empty;
}
private bool CanExecuteAddTableCommand(object paramater)
{
if (TableNameToAdd.Length < 1)
return false;
if (TableNameToAdd.Contains(" "))
return false;
foreach (Table table in Tables)
{
if (table.Name.ToLower() == TableNameToAdd)
return false;
}
return true;
}
// Remove Table Command
public ICommand RemoveTableCommand { get; set; }
private void ExecuteRemoveTableCommand(object paramater){
Tables.Remove(SelectedTable);
}
private bool CanExecuteRemoveTableCommand(object parameter)
{
return SelectedTable != null;
}
// AddFieldCommand
public ICommand AddFieldCommand { get; set; }
private void ExecuteAddFieldCommand(object parameter)
{
SelectedTable.Fields.Add(new Field("test"));
}
private bool CanExecuteAddFieldCommand(object parameter)
{
if (SelectedTable == null)
return false;
if (FieldNameToAdd.Length < 1)
return false;
if (FieldNameToAdd.Contains(" "))
return false;
foreach (Field field in SelectedTable.Fields)
{
if (field.Name.ToLower() == FieldNameToAdd.ToLower())
return false;
}
return true;
}
#endregion
public ObservableCollection<Table> Tables { get; private set; }
public Table SelectedTable { get; set; }
public Field SelectedField { get; set; }
}
}
Table class:
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Managing_program.model
{
class Table
{
public Table(String name)
{
Name = name;
Fields = new ObservableCollection<Field>();
}
public String Name { get; private set; }
public ObservableCollection<Field> Fields { get; private set; }
}
}
The second listbox just doesn't update. Please help!

Okay I found the problem! I had to implement OnPropertyChanged() in the SelectedTable property. It start to work right away after that!

Related

WPF MVVM properties binding not working properly

I have created a view model which has a single property for a student model, that I am then binding to a control in my XAML. But nothing is appearing when I execute the application.
I am setting data context in my app.xaml.cs as follows:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Registrationformusinemvvm.MainWindow window = new MainWindow();
VMUser VM = new VMUser();
window.DataContext = VM;
window.Show();
}
Why is the binding not working?
This is my view model:
public class VMUser:BaseClass
{
private student _currentStudent;
public student CurrentStudent
{
get { return _currentStudent; }
set {
_currentStudent = value;
OnPropertyChanged("CurrentStudent");
}
}
}
My Student model class:
public class student:BaseClass
{
private string name="sumit";
public string Name
{
get { return name; }
set { name = value; OnPropertyChanged("Name"); }
}
private int rollNum;
public int RollNum
{
get { return rollNum; }
set { rollNum = value;OnPropertyChanged("RollNum"); }
}
private int phNum;
public int PhNum
{
get { return phNum; }
set { phNum = value;OnPropertyChanged("PhNum"); }
}
private string sub;
public string Sub
{
get { return sub; }
set { sub = value;OnPropertyChanged("Sub"); }
}
}
My XAML:
<Window x:Class="Registrationformusinemvvm.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:Registrationformusinemvvm"
xmlns:vm="clr-namespace:Registrationformusinemvvm.ViewModel"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525">
<!--<Window.DataContext>
<vm:VMUser/>
</Window.DataContext>-->
<Window.Resources>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
<RowDefinition Height="50"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="200"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="Name" Grid.Column="0" Grid.Row="0" FontSize="14"
FontWeight="Bold" VerticalAlignment="Center"
HorizontalAlignment="Center"/>
<TextBlock Text="Roll Number" Grid.Column="0" Grid.Row="1" FontSize="14"
FontWeight="Bold" VerticalAlignment="Center"
HorizontalAlignment="Center"/>
<TextBlock Text="Subject" Grid.Column="0" Grid.Row="2" FontSize="14"
FontWeight="Bold" VerticalAlignment="Center"
HorizontalAlignment="Center"/>
<TextBlock Text="Phone Number" Grid.Column="0" Grid.Row="3"
FontSize="14" FontWeight="Bold" VerticalAlignment="Center"
HorizontalAlignment="Center"/>
<TextBox Name="tbName" Text="{Binding CurrentStudent.Name,Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" Grid.Column="1" Grid.Row="0"
Width="120" Height="30" HorizontalAlignment="Center"
VerticalAlignment="Center"/>
<TextBox Name="tbRollnum" Text="{Binding CurrentStudent.RollNum}"
Grid.Column="1" Grid.Row="1" Width="120" Height="30"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBox Name="tbSub" Text="{Binding CurrentStudent.Sub}"
Grid.Column="1" Grid.Row="2" Width="120" Height="30"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
<TextBox Name="tbPh" Text="{Binding CurrentStudent.PhNum}"
Grid.Column="1" Grid.Row="3" Width="120" Height="30"
HorizontalAlignment="Center" VerticalAlignment="Center"/>
<Button Name="tbSubmit" Content="Submit" Grid.ColumnSpan="3"
Grid.Row="4" Height="30" Width="100" HorizontalAlignment="Center"/>
</Grid>
</Window>
My guess is that your binding isn't working because your _currentStudent is null by default. Initialize your _currentStudent if null.
public student CurrentStudent
{
get { return _currentStudent = (_currentStudent ?? new student()); }
set
{
_currentStudent = value; OnPropertyChanged("CurrentStudent");
}
}
You need to add OnPropertyChanged in your model class.
void OnPropertyChanged(string prop)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
public event PropertyChangedEventHandler PropertyChanged;
As per your above code you cant assigned the value to CurrentStudent Property so
can you please check do you have the value to CurrentStudent property.
Thank you for your question
Remove StartupUri="YourXamlFile.xaml" from App.Xaml

Is not updated selected item on listview (wpf mvvm) if field of item changed

I'm writing my first program with the use wpf and mvvm.
Have a problem, that in the case of changes data in fields of selecteditem, selecting a new item becomes impossible.
If add to listview attribute SelectionMode="Single", then if many times try pick item, exeption:
The element with the same key has already been added.
image: https://www.dropbox.com/s/ey7izl19803uuhc/1.png
If this attribute is removed, then pick another item, simply does not occur.
Image: https://dl.dropboxusercontent.com/u/32438899/2.png
If the data has not changed, the navigation works fine.
XAML code:
<?xml version="1.0" encoding="utf-8" ?>
<Window x:Class="MHConfigurator.Views.MainView"
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"
mc:Ignorable="d"
d:DataContext="{Binding Path=MainViewModel}"
Title="Редактирование шаблонов писем" Height="Auto" Width="1000" MinWidth="1000" MinHeight="400">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" Margin="7">
<TextBlock DockPanel.Dock="Left" Margin="5,5,2,5" HorizontalAlignment="Left">Поиск:</TextBlock>
<TextBox DockPanel.Dock="Right" Margin="2,2,2,2" HorizontalAlignment="Stretch" Text="{Binding SearchString, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
</DockPanel>
<ListView Grid.Row="1" ItemsSource="{Binding Path=MailProperties, Mode=TwoWay}" Width="Auto" HorizontalAlignment="Stretch"
VerticalAlignment="Stretch" Margin="5" SelectedItem="{Binding Path=CurrentProperty, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
<ListView.View>
<GridView>
<GridViewColumn DisplayMemberBinding="{Binding ButtonID, Mode=TwoWay}" Header="ID шаблона" Width="Auto" />
<GridViewColumn DisplayMemberBinding="{Binding Description, Mode=TwoWay}" Header="Описание" Width="Auto" />
</GridView>
</ListView.View>
</ListView>
<Grid VerticalAlignment="Center" Grid.Row="2" DataContext="{Binding CurrentProperty}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="250"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" Grid.Column="0">
<TextBlock DockPanel.Dock="Left" Margin="5,5,2,5" VerticalAlignment="Center" HorizontalAlignment="Left">ID шаблона:</TextBlock>
<TextBox DockPanel.Dock="Right" Margin="2,2,2,2" VerticalAlignment="Center" HorizontalAlignment="Stretch"
VerticalContentAlignment="Center" Text="{Binding ButtonID}"></TextBox>
</DockPanel>
<DockPanel Grid.Row="1" Grid.Column="0">
<CheckBox DockPanel.Dock="Left" HorizontalAlignment="Left" Margin="5" VerticalAlignment="Center" IsChecked="{Binding FillSubject}">Заполнять тему</CheckBox>
</DockPanel>
<DockPanel Grid.Row="2" Grid.Column="0">
<CheckBox DockPanel.Dock="Left" HorizontalAlignment="Left" Margin="5" VerticalAlignment="Center" IsChecked="{Binding FillTO}">Заполнять адресатов</CheckBox>
</DockPanel>
<DockPanel Grid.Row="3" Grid.Column="0">
<CheckBox DockPanel.Dock="Left" HorizontalAlignment="Left" Margin="5" VerticalAlignment="Center" IsChecked="{Binding FillCopy}">Заполнять копию</CheckBox>
</DockPanel>
<DockPanel Grid.Row="4" Grid.Column="0">
<CheckBox DockPanel.Dock="Left" HorizontalAlignment="Left" Margin="5" VerticalAlignment="Center" IsChecked="{Binding FillHideCopy}">Заполнять скрытую копию</CheckBox>
</DockPanel>
<DockPanel Grid.Row="5" Grid.Column="0">
<CheckBox DockPanel.Dock="Left" HorizontalAlignment="Left" Margin="5" VerticalAlignment="Center" IsChecked="{Binding HighImportance}">Высокая важность</CheckBox>
</DockPanel>
<DockPanel Grid.Row="0" Grid.Column="1">
<TextBlock DockPanel.Dock="Left" VerticalAlignment="Center" Margin="10,5,2,5" Width="115" >Описание шаблона:</TextBlock>
<TextBox DockPanel.Dock="Left" Margin="2,2,5,2" TextWrapping="Wrap" VerticalContentAlignment="Center" Text="{Binding Description}"></TextBox>
</DockPanel>
<DockPanel Grid.Row="1" Grid.Column="1">
<TextBlock DockPanel.Dock="Left" VerticalAlignment="Center" Margin="10,5,2,5" Width="115">Тема письма:</TextBlock>
<TextBox DockPanel.Dock="Left" Margin="2,2,5,2" TextWrapping="Wrap" VerticalContentAlignment="Center" Text="{Binding Subject}"></TextBox>
</DockPanel>
<DockPanel Grid.Row="2" Grid.Column="1">
<TextBlock DockPanel.Dock="Left" VerticalAlignment="Center" Margin="10,5,2,5" Width="115">Адресаты:</TextBlock>
<TextBox DockPanel.Dock="Left" Margin="2,2,5,2" TextWrapping="Wrap" VerticalContentAlignment="Center" Text="{Binding TO}"></TextBox>
</DockPanel>
<DockPanel Grid.Row="3" Grid.Column="1">
<TextBlock DockPanel.Dock="Left" VerticalAlignment="Center" Margin="10,5,2,5" Width="115">Копия:</TextBlock>
<TextBox DockPanel.Dock="Left" Margin="2,2,5,2" TextWrapping="Wrap" VerticalContentAlignment="Center" Text="{Binding Copy}"></TextBox>
</DockPanel>
<DockPanel Grid.Row="4" Grid.Column="1">
<TextBlock DockPanel.Dock="Left" VerticalAlignment="Center" Margin="10,5,2,5" Width="115">Скрытая копия:</TextBlock>
<TextBox DockPanel.Dock="Left" Margin="2,2,5,2" TextWrapping="Wrap" VerticalContentAlignment="Center" Text="{Binding HideCopy}"></TextBox>
</DockPanel>
<DockPanel Grid.Row="5" Grid.Column="1">
<CheckBox DockPanel.Dock="Left" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="5">Напоминание</CheckBox>
<TextBlock DockPanel.Dock="Left" VerticalAlignment="Center" Margin="4">Время напоминания:</TextBlock>
<TextBox DockPanel.Dock="Left" Margin="2,2,5,2" VerticalContentAlignment="Center"></TextBox>
</DockPanel>
<DockPanel Grid.Row="6" Grid.Column="1">
<CheckBox DockPanel.Dock="Left" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="5">Заполнять текст письма</CheckBox>
</DockPanel>
<DockPanel Grid.Row="7" Grid.Column="0" Grid.ColumnSpan="2">
<Button DockPanel.Dock="Left" Margin="15" HorizontalAlignment="Left" VerticalAlignment="Center" Width="150" Height="40">Добавить новый шаблон</Button>
<Button DockPanel.Dock="Left" Margin="15" HorizontalAlignment="Left" VerticalAlignment="Center" Width="150" Height="40">Сохранить</Button>
<Button DockPanel.Dock="Left" Margin="15" HorizontalAlignment="Left" VerticalAlignment="Center" Width="150" Height="40">Отмена</Button>
<TextBlock DockPanel.Dock="Left" Margin="0,4,4,4" VerticalAlignment="Center">Заметка:</TextBlock>
<TextBox DockPanel.Dock="Right" Margin="5" TextWrapping="Wrap" AcceptsReturn="True" Height="70" HorizontalAlignment="Stretch" VerticalScrollBarVisibility="Visible"
SpellCheck.IsEnabled="True" Language="ru-ru"></TextBox>
</DockPanel>
</Grid>
</Grid>
</Window>
In viewmodel problems can not be. It is very easy, PropertyChanged called whenever necessary.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using MugenMvvmToolkit;
using MugenMvvmToolkit.ViewModels;
using MugenMvvmToolkit.Models;
using System.Windows.Input;
using MHConfigurator.Models;
using System.Windows;
namespace MHConfigurator.ViewModels
{
class MainViewModel : ViewModelBase
{
public MainViewModel()
{
_mailProperties = new ObservableCollection<MailProperty>(DAL.GetDAL().MailPropertys);
//MailsTemplates = DAL.GetDAL().GetEmptyMailTemplates();
}
#region Fields
#region Backing Fields
private ObservableCollection<MailProperty> _mailProperties;
private MailProperty _currentProperty;
private string _searchString;
private MailProperty _originalCurrentProperty;
private bool _currentPropertyChanged = false;
private List<MailsTemplate> _mailsTemplates;
private int _selectedMailTemplate;
#endregion
public string SearchString
{
get
{
if (_searchString.IsNullOrEmpty()) return "";
return _searchString;
}
set
{
bool stringContains = _searchString != null && value.Contains(_searchString);
_searchString = value;
CurrentProperty = null;
//Not matter
}
}
public ObservableCollection<MailProperty> MailProperties
{
get { return _mailProperties; }
set
{
_mailProperties = value;
//OnPropertyChanged(new PropertyChangedEventArgs("MailProperties"));
OnPropertyChanged();
}
}
public MailProperty CurrentProperty
{
get { return _currentProperty; }
set
{/*
if ((_originalCurrentProperty != null)&&(_currentPropertyChanged)&&(_originalCurrentProperty!=_currentProperty)) //Если выбран другой объект и есть несохранённые изменения
{
MessageBoxResult result = MessageBox.Show("Есть несохранённые изменения. Сохранить?", "Несохранённые изменения", MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes)
{
}
else
{
MailProperties[MailProperties.IndexOf(_currentProperty)] = _originalCurrentProperty; //Находим в коллекции изменённый объект и заменить его оригиналом
}
}
_originalCurrentProperty = Helper.DeepClone(_currentProperty); //Делаем резервную копию
SelectedMailTemplate = value.BodyID; //выставляем id шаблона
*/
_currentProperty = value;
// OnPropertyChanged(new PropertyChangedEventArgs("CurrentProperty"));
OnPropertyChanged();
}
}
public List<MailsTemplate> MailsTemplates
{
get { return _mailsTemplates; }
set
{
_mailsTemplates = value;
OnPropertyChanged();
}
}
public int SelectedMailTemplate
{
get { return _selectedMailTemplate; }
set
{
_selectedMailTemplate = value;
CurrentProperty.BodyID = value;
OnPropertyChanged();
}
}
#endregion
#region Commands
public ICommand NewCommand { get; private set; }
public ICommand SaveCommand { get; private set; }
public ICommand CancelCommand { get; private set; }
#region Execute
#endregion
#region CanExecute
#endregion
#endregion
}}
I have no idea.
I will be glad to any ideas.
P.S.: Sorry for my bad english.

Get index of data binding from ItemsControl

Is there any way to get the index of the data which is binded to the ItemsControl, when a button is clicked?
<ItemsControl x:Name="ic_schranke" DataContext="{Binding Schranke}" >
<ItemsControl.ItemTemplate>
<DataTemplate >
<Button Height="80" x:Name="btn_open" Click="btn_open_Click" HorizontalAlignment="Stretch" Style="{StaticResource TransparentStyle}">
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*"/>
<RowDefinition Height="30*"/>
<RowDefinition Height="1*"/>
</Grid.RowDefinitions>
<Rectangle x:Name="rect" Grid.ColumnSpan="3" Grid.Row="1" Opacity="0.65" Grid.Column="0" Fill="#FFCEEAFF"/>
<Border CornerRadius="25" Height="50" Width="50" HorizontalAlignment="Center" Grid.Column="0" Grid.Row="1">
<Border.Background>
<ImageBrush ImageSource="/Assets/schranken.jpg" />
</Border.Background>
</Border>
<TextBlock Name="schranken_name" Grid.Column="1" Grid.Row="1" Text="{Binding name}" VerticalAlignment="Center" HorizontalAlignment="Center" FontWeight="ExtraLight" Foreground="Black" />
</Grid>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Here is the sample data which is binded to the IC:
List<Schranke> elements;
public UserPage()
{
this.InitializeComponent();
elements = new List<Schranke>();
for (var i = 1; i < 15; i++)
elements.Add(new Schranke() { name = $"Schranke NR:{i}" });
this.ic_schranke.ItemsSource = elements;
}
And here is the code in the button click event:
private async void btn_open_Click(object sender, RoutedEventArgs e)
{
Grid grid = (sender as Button).Content as Grid;
//Debug.WriteLine($"content {tb.Text} clicked");
var tmp_background = grid.Background;
grid.Background = new SolidColorBrush(new Windows.UI.Color() { A = 255, R = 78, G = 170, B = 44 });
VibrationDevice testVibrationDevice = VibrationDevice.GetDefault();
testVibrationDevice.Vibrate(System.TimeSpan.FromMilliseconds(150));
await Task.Delay(3000);
grid.Background = tmp_background;
}
Maybe something along the lines of this:
the presenter - put this in the data context
public class SchrankePresenter : INotifyPropertyChanged
{
private List<Schranke> _elements;
public List<Schranke> Elements
{
get { return _elements; }
set
{
_elements = value;
OnPropertyChanged("Elements");
}
}
public ICommand ClickCommand { get; set; }
private void OnPropertyChanged(string propName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
public event PropertyChangedEventHandler PropertyChanged;
public SchrankePresenter()
{
var elements = new List<Schranke>();
for (var i = 1; i < 15; i++)
elements.Add(new Schranke() { Name = $"Schranke NR:{i}" });
Elements = elements;
ClickCommand = new DelegateCommand(ClickAction);
}
public void ClickAction(Schranke item)
{
VibrationDevice.GetDefault().Vibrate(TimeSpan.FromMilliseconds(150));
}
}
public class Schranke
{
public string Name { get; set; }
}
the template:
<ListView ItemsSource="{Binding Elements}">
<ListView.ItemTemplate>
<DataTemplate>
<Button Height="80"
HorizontalAlignment="Stretch"
Command="{Binding ClickCommand}"
Style="{StaticResource TransparentStyle}">
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="2*" />
<ColumnDefinition Width="1*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1*" />
<RowDefinition Height="30*" />
<RowDefinition Height="1*" />
</Grid.RowDefinitions>
<Rectangle x:Name="rect"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="3"
Fill="#FFCEEAFF"
Opacity="0.65" />
<Border Grid.Row="1"
Grid.Column="0"
Width="50"
Height="50"
HorizontalAlignment="Center"
CornerRadius="25">
<Border.Background>
<ImageBrush ImageSource="/Assets/schranken.jpg" />
</Border.Background>
</Border>
<TextBlock Name="schranken_name"
Grid.Row="1"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontWeight="ExtraLight"
Foreground="Black"
Text="{Binding Name}" />
</Grid>
</Button>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
you can do the background animation using a storyboard/visualstate
EDIT : regarding the DelegateCommand, it's a simple implementation I use for my WPF apps -
internal class DelegateCommand<T> : ICommand
{
private Action<T> _clickAction;
public DelegateCommand(Action<T> clickAction)
{
_clickAction = clickAction;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
_clickAction((T)parameter);
}
}

WPF display content based on treeview selected item

I was able to retrieve selected item value from my treeview. Now I want to change the content in ScrollViewer control based on that value. I'm trying to display property values of Facility object which contains Containments which contains Tanks.
I'm new to programming sorry if my explanation is not clear enough.
Thank you.
MyWindow.xaml
<Window.DataContext>
<local:VisioFacilityExportViewModel/>
</Window.DataContext>
<Window.Resources>
<DataTemplate x:Key="FacilityTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<Label Content="Facility ID:"/>
<TextBox Grid.Row="0" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].FacilityID}"/>
<Label Content="Address:" Grid.Row="1"/>
<TextBox Grid.Row="1" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].Address}"/>
<Label Content="Description:" Grid.Row="2"/>
<TextBox Grid.Row="2" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].Description}"/>
<Label Content="Layout And Drainage:" Grid.Row="3"/>
<TextBox Grid.Row="3" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].LayoutAndDrainage}"/>
<Label Content="Latitude:" Grid.Row="4"/>
<TextBox Grid.Row="4" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].Latitude}"/>
<Label Content="Longitude:" Grid.Row="5"/>
<TextBox Grid.Row="5" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].Longitude}"/>
<Label Content="City:" Grid.Row="6"/>
<TextBox Grid.Row="6" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].City.Name}"/>
<Label Content="County:" Grid.Row="7"/>
<TextBox Grid.Row="7" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].County.Name}"/>
<Label Content="Name:" Grid.Row="8"/>
<TextBox Grid.Row="8" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].Name}"/>
<Label Content="Run:" Grid.Row="9"/>
<TextBox Grid.Row="9" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].Run}"/>
<Label Content="Surface Flow Direction:" Grid.Row="10"/>
<TextBox Grid.Row="10" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].SurfaceFlowDirection}"/>
<Label Content="API Number:" Grid.Row="11"/>
<TextBox Grid.Row="11" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].APINumber}"/>
<Label Content="Distance To Navigable Waters:" Grid.Row="12"/>
<TextBox Grid.Row="12" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].DistanceToNavigableWaters}"/>
<Label Content="Project Phase:" Grid.Row="13"/>
<TextBox Grid.Row="13" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].APINumber}"/>
<Label Content="Unique ID:" Grid.Row="14"/>
<TextBox Grid.Row="14" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].UniqueID}"/>
<Label Content="Operational Field:" Grid.Row="15"/>
<TextBox Grid.Row="15" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].OperationalField}"/>
<Label Content="Facility Type:" Grid.Row="16"/>
<TextBox Grid.Row="16" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].FacilityType}"/>
<Label Content="Project Number:" Grid.Row="17"/>
<TextBox Grid.Row="17" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].ProjectNumber}"/>
<Label Content="Project Phase:" Grid.Row="18"/>
<TextBox Grid.Row="18" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].ProjectPhase}"/>
<Label Content="Report Number:" Grid.Row="19"/>
<TextBox Grid.Row="19" Grid.Column="1" Height="20" Text="{Binding ListFacilities[0].ReportNumber}"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="ContainmentTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<Label Content="Name:"/>
<TextBox Grid.Column="1" Height="20" Text="{Binding ListContainments[0].Name}"/>
<Label Content="Height:" Grid.Row="1"/>
<TextBox Grid.Row="1" Grid.Column="1" Height="20" Text="{Binding ListContainments[0].Height}"/>
<Label Content="Diameter:" Grid.Row="2"/>
<TextBox Grid.Row="2" Grid.Column="1" Height="20" Text="{Binding ListContainments[0].TankPadHeight}"/>
<Label Content="County:" Grid.Row="3"/>
<TextBox Grid.Row="3" Grid.Column="1" Height="20" Text="{Binding ListContainments[0].TankPadWidth}"/>
<Label Content="Inner Length:" Grid.Row="4"/>
<TextBox Grid.Row="4" Grid.Column="1" Height="20" Text="{Binding ListContainments[0].TankPadLength}"/>
<Label Content="Inner Width:" Grid.Row="5"/>
<TextBox Grid.Row="5" Grid.Column="1" Height="20" Text="{Binding ListContainments[0].InnerLength}"/>
<Label Content="Top Length:" Grid.Row="6"/>
<TextBox Grid.Row="6" Grid.Column="1" Height="20" Text="{Binding ListContainments[0].InnerWidth}"/>
<Label Content="Top Width:" Grid.Row="7"/>
<TextBox Grid.Row="7" Grid.Column="1" Height="20" Text="{Binding ListContainments[0].TopLength}"/>
<Label Content="Construction:" Grid.Row="8"/>
<TextBox Grid.Row="8" Grid.Column="1" Height="20" Text="{Binding ListContainments[0].TopWidth}"/>
<Label Content="Fill Materials:" Grid.Row="9"/>
<TextBox Grid.Row="9" Grid.Column="1" Height="20" Text="{Binding ListContainments[0].Construction}"/>
<Label Content="Fill Material Height (Top):" Grid.Row="10"/>
<TextBox Grid.Row="10" Grid.Column="1" Height="20" Text="{Binding ListContainments[0].Diameter}"/>
</Grid>
</DataTemplate>
<DataTemplate x:Key="TanksTemplate">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="200"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<Label Content="Name:"/>
<TextBox Grid.Column="1" Height="20" Text="{Binding ListTanks[0].Name}"/>
<Label Content="Height:" Grid.Row="1"/>
<TextBox Grid.Row="1" Grid.Column="1" Height="20" Text="{Binding ListTanks[0].Height}"/>
<Label Content="Diameter:" Grid.Row="2"/>
<TextBox Grid.Row="2" Grid.Column="1" Height="20" Text="{Binding ListTanks[0].Diameter}"/>
<Label Content="Is in Service:" Grid.Row="3"/>
<TextBox Grid.Row="3" Grid.Column="1" Height="20" Text="{Binding ListTanks[0].IsInService}"/>
<Label Content="Is on Pad:" Grid.Row="4"/>
<CheckBox Grid.Row="4" Grid.Column="1" Height="20" IsChecked="{Binding ListTanks[0].IsOnPad}"/>
<Label Content="Contents:" Grid.Row="5"/>
<TextBox Grid.Row="5" Grid.Column="1" Height="20" Text="{Binding ListTanks[0].Contents}"/>
<Label Content="Year Constructed:" Grid.Row="6"/>
<TextBox Grid.Row="6" Grid.Column="1" Height="20" Text="{Binding ListTanks[0].YearConstructed}"/>
<Label Content="Material:" Grid.Row="7"/>
<TextBox Grid.Row="7" Grid.Column="1" Height="20" Text="{Binding ListTanks[0].Material}"/>
<Label Content="Is Portable:" Grid.Row="8"/>
<CheckBox Grid.Row="8" Grid.Column="1" Height="20" IsChecked="{Binding ListTanks[0].IsPortable}"/>
<Label Content="Is Isolated:" Grid.Row="9"/>
<CheckBox Grid.Row="9" Grid.Column="1" Height="20" IsChecked="{Binding ListTanks[0].IsIsolated}"/>
<Label Content="Quantity:" Grid.Row="10"/>
<TextBox Grid.Row="10" Grid.Column="1" Height="20" Text="{Binding ListTanks[0].Quantity}"/>
<Label Content="Orientation:" Grid.Row="11"/>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid Margin="5" Height="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<TreeView Name="trvFacility" ItemsSource="{Binding MenuItems}" VirtualizingStackPanel.IsVirtualizing="True" Padding="10" Margin="5" Width="220">
<i:Interaction.Behaviors>
<local:BindableSelectedItemBehavior SelectedItem="{Binding SelectedItem,Mode=TwoWay}"/>
</i:Interaction.Behaviors>
<TreeView.ItemTemplate>
<HierarchicalDataTemplate ItemsSource="{Binding Children}" DataType="{x:Type entities:MenuItemCust}">
<TextBlock Text="{Binding Name}"/>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
<ScrollViewer Grid.Row="0" Grid.Column="1" Margin="5" VerticalScrollBarVisibility="Visible">
DataTemplate here
</ScrollViewer>
<Button Content="Save" Grid.Row="1" Grid.ColumnSpan="2" Grid.Column="0" Width="60" Height="25" Margin="0,0,5,0" HorizontalAlignment="Right"/>
</Grid>
MyWindowViewModel.cs
public class VisioFacilityExportViewModel : INotifyPropertyChanged
{
public VisioFacilityExportViewModel()
{
_lstFacilities = new ObservableCollection<Facility>(DatabaseHandler.GetFacilities().Where(f => f.FacilityID == 2015012314001933));
_lstContainments = _lstFacilities[0].Containment;
_lstTanks = _lstContainments[0].Tanks;
List<MenuItemCust> lstRoot = new List<MenuItemCust>();
List<MenuItemCust> lstContainment = new List<MenuItemCust>();
List<MenuItemCust> lstTanks = new List<MenuItemCust>();
MenuItems = new List<MenuItemCust>();
foreach (var f in _lstFacilities)
{
foreach (var c in f.Containment)
{
lstTanks.AddRange(c.Tanks.Select(t => new MenuItemCust { Name = t.Name, Info = "Tanks"}));
lstContainment.Add(new MenuItemCust { Name = c.Name, Children = lstTanks, Info = "Containment" });
}
lstRoot.Add(new MenuItemCust { Name = "Containment", Children = lstContainment, Info = "Facility"});
MenuItems.Add(new MenuItemCust { Name = "Facility", Children = lstRoot });
}
}
private ObservableCollection<Tank> _lstTanks;
public ObservableCollection<Tank> ListTanks
{
get { return _lstTanks; }
set
{
_lstTanks = value;
NotifyPropertyChanged("ListTanks");
}
}
private ObservableCollection<Containment> _lstContainments;
public ObservableCollection<Containment> ListContainments
{
get { return _lstContainments; }
set
{
_lstContainments = value;
NotifyPropertyChanged("ListContainments");
}
}
private ObservableCollection<Facility> _lstFacilities;
public ObservableCollection<Facility> ListFacilities
{
get { return _lstFacilities; }
set
{
_lstFacilities = value;
NotifyPropertyChanged("ListFacilities");
}
}
private List<MenuItemCust> _menuItems;
public List<MenuItemCust> MenuItems
{
get { return _menuItems; }
set
{
_menuItems = value;
NotifyPropertyChanged("MenuItems");
}
}
private static object _selectedItem = null;
public static object SelectedItem
{
get { return _selectedItem; }
private set
{
if (_selectedItem != value)
{
_selectedItem = value;
OnSelectedItemChanged();
}
}
}
public static void OnSelectedItemChanged()
{
var item = (MenuItemCust) SelectedItem;
if (item.Name == "Facility")
{
}
else if (item.Info == "Containment")
{
}
else if (item.Info == "Tanks")
{
}
}
#region iNotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
#endregion
}
I think what you want to do is display informations in a UserControl which will contains a grid with differents controls like textblock and textbox.
So you are going to create a new UserControl named FacilityView and FacilityViewModel and the FacilityView will contains a grid with ur Facility Ojbect property
First of all, I would create an property in MyWindowViewModel called
private Facility currentFacility;
public Facility CurrentFacility
{
get{return currentFacility;}
set{ currentFacility = value ;
notifyPropertyChange("CurrentFacility")
}
In your MainWindowsView (xaml)
<DataTemplate DataType="{x:Type localAdder:FacilityViewModel}">
<localAdder:FacilityView />
</DataTemplate>
<UserControl Grid.Row="1" Grid.Column="2"
Content="{Binding CurrentFacility,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged,NotifyOnSourceUpdated=True}">
</UserControl>
And in your SelectedItem you would
if (selectedItem is Facility)
{
CurrentFacility = new FacilityViewModel(selectedItem as Facility);
}
Sry for the bad indentation im not use to this writing code.
EDIT2:
Okay, you have 2 choice, first of all u can create a BaseViewModel which will implements INotifyPropertyChanged.
FacilityViewModel : BaseViewModel
TankViewModel : BaseViewModel
ContainmentViewModel : BaseViewModel
This way you will change CurrentFacilityViewModel to CurrentBaseViewModel
private BaseViewModel currentBaseXXX;
public BaseViewModel CurrentBaseXXX
{
get{return currentBaseXXX;}
set{ currentBaseXXX= value ;
notifyPropertyChange("CurrentBaseXXX")
}
And in your SelectedItem you would
if (selectedItem is Facility)
{
CurrentBaseXXX= new FacilityViewModel(selectedItem as Facility);
}
if (selectedItem is Tank)
{
CurrentBaseXXX= new TankViewModel(selectedItem as Tank);
}
if (selectedItem is Containers)
{
CurrentBaseXXX= new ContainersViewModel(selectedItem as Containers);
}
Options2
You would have 3 current so
CurrentFacilityVM
CurrentTankVM
CurrentContainerVM
And in your SelectedItem you would
CurrentFacilityVM = null;
CurrentTankVM =null;
CurrentContainerVM =null;
if (selectedItem is Facility)
{
CurrentFacilityVM = new FacilityViewModel(selectedItem as Facility);
}
if (selectedItem is Tank)
{
CurrentTankVM = new TankViewModel(selectedItem as Tank);
}
if (selectedItem is Containers)
{
CurrentContainerVM = new ContainersViewModel(selectedItem as Containers);
}
You can dynamically Load the data template based on Type. Refer the below. You can toggle the template by change the instance of the SelectedItem to a Different class.
<Window.Resources>
<DataTemplate DataType="{x:Type entities:Facility}">
<Grid>
<TextBlock Text="{Binding FacilityName}" />
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type entities:Containment}">
<Grid>
<TextBlock Text="{Binding ContainmentName}"/>
</Grid>
</DataTemplate>
<DataTemplate DataType="{x:Type entities:Tank}">
<Grid>
<TextBlock Text="{Binding TankName}"></TextBlock>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid Margin="5" Height="Auto">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<ScrollViewer Grid.Row="0" Grid.Column="1" Margin="5" VerticalScrollBarVisibility="Visible" DataContext="{Binding SelectedItem}">
<ContentControl Content="{Binding }"/>
</ScrollViewer>
<Button Content="Save" Grid.Row="1" Grid.ColumnSpan="2" Grid.Column="0" Width="60" Height="25" Margin="0,0,5,0" HorizontalAlignment="Right"/>
</Grid>
public partial class Download : Window
{
public Download()
{
InitializeComponent();
this.DataContext = new VisioFacilityExportViewModel();
}
}
public class VisioFacilityExportViewModel : INotifyPropertyChanged
{
public VisioFacilityExportViewModel()
{
//SelectedItem = new Facility() { FacilityName = "Facility" };
//SelectedItem = new Tank() { TankName = "Tank" };
SelectedItem = new Containment() { ContainmentName = "Containment" };
}
private object _selectedItem = null;
public object SelectedItem
{
get { return _selectedItem; }
private set
{
if (_selectedItem != value)
{
_selectedItem = value;
NotifyPropertyChanged("SelectedItem");
}
}
}
#region iNotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyPropertyChanged(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
#endregion
}
public class Tank
{
private string myVar;
public string TankName
{
get { return myVar; }
set { myVar = value; }
}
}
class Facility
{
private string myVar;
public string FacilityName
{
get { return myVar; }
set { myVar = value; }
}
}
class Containment
{
private string myVar;
public string ContainmentName
{
get { return myVar; }
set { myVar = value; }
}
}

Stop ContentTemplate Selector invoke on TabControl selection change event

I have a TabControl. Each tab Item i.e newly added Tab is rendered using content template selector. But each time i switch between the tabs, content template selector is getting called.
I wanted to stop this. This is because, In the in Tabcontrol, User have provided actions to change the layout, since in the selection change event of the tab Item content template is getting called, it is getting difficult for me to retain the layout what user has changed during Tab Item Selection change Event.
Following is code i am using for the TabControl
<TabControl Grid.Column="2" x:Name="MainTabControl" HorizontalAlignment="Stretch" Margin="0,12,0,7"
SelectedItem="{Binding SelectedTabItem}"
Visibility="{Binding TabsVisible}"
ItemsSource="{Binding ToolsList,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
ItemTemplate="{StaticResource ToolDisplayDataTemplate}"
commands:FrameworkUICommandList.TabItemChangedCommand="{Binding Path=TabItemChangedCommand}"
>
<TabControl.ContentTemplate>
<DataTemplate>
<ContentControl Content="{Binding}" ContentTemplateSelector="{DynamicResource toolTabDataItemTemplateSelector}"/>
</DataTemplate>
</TabControl.ContentTemplate>
<TabControl.ItemContainerStyle>
<Style TargetType="TabItem" BasedOn="{StaticResource TabItemStyle}">
<Setter Property="AutomationProperties.AutomationId" Value="{Binding ToolID}" />
<Setter Property="ToolTip" Value="{Binding ToolID,Converter={StaticResource ResourceKey=tabItemTooltipConverter}}"/>
</Style>
</TabControl.ItemContainerStyle>
</TabControl>
UPDATE : I have created a sample project to explain my problem. Could not share the project anywhere, so updating the code snippet in main question. sorry for that.
In the sample project, user can add TabItem dynamically to TabControl. Eash Tabcontent can show two Grid panel and they are separated by Grid Splitter. Display of second grid is Based on some flag (in this example ShowSecondPanel). If user click on "Show / Hide Second Panel" button, then second panel content will be shown for the current selected tab.
The Problem is, user can re-size the panels using Grid Splitter, but when user navigates to some other tab and comes back to previous one, the grid splitter position changes to original position.
Hope I am clear on describing the problem.
Code for MainWindow.xaml
<Window x:Class="TabControlTestApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TabControlTestApp"
Title="MainWindow" Height="500" Width="700">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Window.Resources>
<local:TabContentTemplateSelector x:Key="tabContentTemplateSelector" />
<DataTemplate x:Key="TabitemDataTemplate">
<StackPanel Width="50" Height="50">
<TextBlock Text="{Binding TabFirstPanel.Name}"></TextBlock>
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="TabContentWithFirstPanel">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0">Name :</TextBlock>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding TabFirstPanel.Name}"></TextBlock>
</Grid>
</DataTemplate>
<DataTemplate x:Key="TabContentWithBothPanel">
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"></ColumnDefinition>
<ColumnDefinition Width="5"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid Grid.Row="0" Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0">Name :</TextBlock>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding TabFirstPanel.Name}"></TextBlock>
</Grid>
<GridSplitter Grid.Row="0" Grid.Column="1"
VerticalAlignment="Stretch"
Width="5"
Height="Auto"
ResizeDirection="Columns"
ResizeBehavior="PreviousAndNext"
></GridSplitter>
<Grid Grid.Row="0" Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0">Additional Detail :</TextBlock>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding TabSecondPanel.AdditionalDetails}"></TextBlock>
</Grid>
</Grid>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="50"></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="150"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<StackPanel Grid.Row="1" Grid.Column="0">
<Button Content="Load Tab : 1" Name="LoadTab1" Width="100" Height="50" Command="{Binding LoadTabCommand}" CommandParameter="{Binding ElementName=LoadTab1}"></Button>
<Button Content="Load Tab : 2" Name="LoadTab2" Width="100" Height="50" Command="{Binding LoadTabCommand}" CommandParameter="{Binding ElementName=LoadTab2}"></Button>
<Button Content="Load Tab : 3" Name="LoadTab3" Width="100" Height="50" Command="{Binding LoadTabCommand}" CommandParameter="{Binding ElementName=LoadTab3}"></Button>
</StackPanel>
<Button Content="Close All tab" Width="100" Height="40" x:Name="CloseAllTab"></Button>
<Button Content="Show / Hide Second Panel" x:Name="ShowHideSecondPanelInTab"
Grid.Row="0" Grid.Column="1" Width="150"
Command="{Binding LoadTabCommand}" CommandParameter="{Binding ElementName=ShowHideSecondPanelInTab}"></Button>
<TabControl Grid.Row="1" Grid.Column="1"
ItemsSource="{Binding TabContentList,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
SelectedItem="{Binding SelectedTabItem}"
ItemTemplate="{StaticResource ResourceKey=TabitemDataTemplate}"
ContentTemplateSelector="{StaticResource ResourceKey=tabContentTemplateSelector}"
>
</TabControl>
</Grid>
</Window>
Code For MainWindowViewModel
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace TabControlTestApp
{
class MainWindowViewModel : INotifyPropertyChanged
{
public MainWindowViewModel()
{
this.loadTabCommand = new LoadTabCommand(this);
tabContentList = new ObservableCollection<TabContent>();
}
public ICommand LoadTabCommand
{
get
{
return this.loadTabCommand;
}
set
{
this.loadTabCommand = value;
}
}
public ObservableCollection<TabContent> TabContentList
{
get
{
return tabContentList;
}
set
{
tabContentList = value;
OnPropertyChanged("TabContentList");
}
}
public TabContent SelectedTabItem
{
get
{
return selectedTabItem;
}
set
{
selectedTabItem = value;
OnPropertyChanged("SelectedTabItem");
}
}
public void LoadFirstTab()
{
TabFirstPanel firstPanel = new TabFirstPanel() { Name = "John-1", Age = "31" };
TabSecondPanel secondPanel = new TabSecondPanel() { AdditionalDetails="Some Details for First Tab" };
TabContent firstTabContent = new TabContent() { TabFirstPanel = firstPanel, TabSecondPanel = secondPanel, ShowSecondPanel = false };
tabContentList.Add(firstTabContent);
SelectedTabItem = firstTabContent;
}
public void LoadSecondTab()
{
TabFirstPanel firstPanel = new TabFirstPanel() { Name = "John-2", Age = "31" };
TabSecondPanel secondPanel = new TabSecondPanel() { AdditionalDetails = "Some Details for second Tab" };
TabContent secondTabContent= new TabContent() { TabFirstPanel = firstPanel, TabSecondPanel = secondPanel, ShowSecondPanel=false };
tabContentList.Add(secondTabContent);
SelectedTabItem = secondTabContent;
}
public void LoadThirdTab()
{
TabFirstPanel firstPanel = new TabFirstPanel() { Name = "John-3", Age = "31" };
TabSecondPanel secondPanel = new TabSecondPanel() { AdditionalDetails = "Some Details for Third Tab" };
TabContent ThirdTabContent = new TabContent() { TabFirstPanel = firstPanel, TabSecondPanel = secondPanel, ShowSecondPanel = false };
tabContentList.Add(ThirdTabContent);
SelectedTabItem = ThirdTabContent;
}
public void ShowHideSecondPanelInTab()
{
TabContent currentTabContent = SelectedTabItem;
int currentIndex = tabContentList.IndexOf(SelectedTabItem);
if (currentTabContent.ShowSecondPanel)
{
currentTabContent.ShowSecondPanel = false;
}
else
{
currentTabContent.ShowSecondPanel = true;
}
TabContentList.RemoveAt(currentIndex);
TabContentList.Insert(currentIndex, currentTabContent);
OnPropertyChanged("TabContentList");
SelectedTabItem = currentTabContent;
}
private TabContent selectedTabItem = null;
private ObservableCollection<TabContent> tabContentList = null;
private ICommand loadTabCommand = null;
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
Code for TabContentTemplateSelector.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls;
using System.Windows;
namespace TabControlTestApp
{
class TabContentTemplateSelector : DataTemplateSelector
{
public override System.Windows.DataTemplate SelectTemplate(object item, System.Windows.DependencyObject container)
{
FrameworkElement element = container as FrameworkElement;
if (element != null && item != null)
{
TabContent tabContent = (TabContent)item;
if (tabContent.ShowSecondPanel)
{
return element.FindResource("TabContentWithBothPanel") as DataTemplate;
}
else
{
return element.FindResource("TabContentWithFirstPanel") as DataTemplate;
}
}
else
{
return base.SelectTemplate(item, container);
}
}
}
}
Code for TabContent Data Object
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace TabControlTestApp
{
class TabFirstPanel
{
public string Name { get; set; }
public string Age { get; set; }
}
class TabSecondPanel
{
public string AdditionalDetails { get; set; }
}
class TabContent
{
public TabFirstPanel TabFirstPanel { get; set; }
public TabSecondPanel TabSecondPanel { get; set; }
public bool ShowSecondPanel { get; set; }
}
}
Code for LoadTabCommand Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
namespace TabControlTestApp
{
class LoadTabCommand : ICommand
{
MainWindowViewModel mainWindowViewModel = null;
public LoadTabCommand(MainWindowViewModel mainWindowViewModel)
{
this.mainWindowViewModel = mainWindowViewModel;
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
public void Execute(object parameter)
{
System.Windows.Controls.Button btn = (System.Windows.Controls.Button)parameter;
switch (btn.Name)
{
case "LoadTab1":
mainWindowViewModel.LoadFirstTab();
break;
case "LoadTab2":
mainWindowViewModel.LoadSecondTab();
break;
case "LoadTab3":
mainWindowViewModel.LoadThirdTab();
break;
case "ShowHideSecondPanelInTab":
mainWindowViewModel.ShowHideSecondPanelInTab();
break;
}
}
}
}
I took a look at your example. Thanks for posting code. Now I understand your issue completely. Before I suggested to change DynamicResource to StaticResource I thought you wanted to only look up once for your DataTemplate.
Now I see you wish to keep the instance of DataTemplate alive so TabControl doesn't destroy it when changing tabs.
Here is the solution:
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Window.Resources>
<local:TabContentTemplateSelector x:Key="tabContentTemplateSelector" />
<DataTemplate x:Key="TabitemDataTemplate">
<StackPanel Width="50" Height="50">
<TextBlock Text="{Binding TabFirstPanel.Name}"></TextBlock>
</StackPanel>
</DataTemplate>
<Grid x:Key="template1">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0">Name :</TextBlock>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding TabFirstPanel.Name}"></TextBlock>
</Grid>
<Grid x:Key="template2">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="200"></ColumnDefinition>
<ColumnDefinition Width="5"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid Grid.Row="0" Grid.Column="0">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0">Name :</TextBlock>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding TabFirstPanel.Name}"></TextBlock>
</Grid>
<GridSplitter Grid.Row="0" Grid.Column="1"
VerticalAlignment="Stretch"
Width="5"
Height="Auto"
ResizeDirection="Columns"
ResizeBehavior="PreviousAndNext"/>
<Grid Grid.Row="0" Grid.Column="2">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0">Additional Detail :</TextBlock>
<TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding TabSecondPanel.AdditionalDetails}"></TextBlock>
</Grid>
</Grid>
<DataTemplate x:Key="TabContentWithFirstPanel">
<ContentPresenter Content="{StaticResource template1}"/>
</DataTemplate>
<DataTemplate x:Key="TabContentWithBothPanel">
<ContentPresenter Content="{StaticResource template2}"/>
</DataTemplate>
If you just copy past that you will do fine.
By the way, just as sidenote for you, destroying and building up DataTemplates is very important in wpf for releasing unmanaged memory.
I don't believe you may avoid this behavior, unless you use another control other than the TabControl.
The TabControl is a ItemControl-derived component: if its content is created via DataTemplate (as you done), the actual content is generated every time upon the current selection.
Another option is to fill the TabControl with direct content, and the inner controls should be preserved across the selection.
Have a look here:
http://msdn.microsoft.com/en-us/library/system.windows.controls.tabcontrol(v=vs.110).aspx
UPDATE: First off, I am not sure to have understand what you want, but here is a solution based on what I mean.
The fundamental trick is hosting directly the tab-contents, instead of creating via data-templating. That is:
<TabControl>
<TabItem Header="Tab1">
<TextBlock
Text="Page 1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</TabItem>
<TabItem Header="Tab2">
<CheckBox
Content="check me"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</TabItem>
<TabItem Header="Tab3">
<TextBlock
Text="Page 3"
HorizontalAlignment="Center"
VerticalAlignment="Center"
/>
</TabItem>
</TabControl>
The above snippet should "persist" the checkbox value across the tabs flipping.
But you need (or desire) some kind of templating, or a dynamic way to create the right content upon a certain data added to the tabcontrol.
The following trick should solve your problem:
<TabControl>
<TabItem
Header="Tab1"
>
<ContentControl
Content="{Binding Path=A}"
ContentTemplateSelector="{StaticResource sel}"
/>
</TabItem>
<TabItem
Header="Tab2"
>
<ContentControl
Content="{Binding Path=B}"
ContentTemplateSelector="{StaticResource sel}"
/>
</TabItem>
<TabItem
Header="Tab3"
>
<ContentControl
Content="{Binding Path=C}"
ContentTemplateSelector="{StaticResource sel}"
/>
</TabItem>
</TabControl>
This is using a trivial set of templates as follows:
<Window.Resources>
<local:MySelector x:Key="sel" />
<DataTemplate x:Key="dtplA">
<StackPanel Margin="30,30">
<TextBlock Text="A: " />
<TextBox />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="dtplB">
<StackPanel Margin="30,30">
<TextBlock Text="B: " />
<TextBox />
</StackPanel>
</DataTemplate>
<DataTemplate x:Key="dtplC">
<StackPanel Margin="30,30">
<TextBlock Text="C: " />
<TextBox />
</StackPanel>
</DataTemplate>
</Window.Resources>
And the behind-code is even more trivial:
public class VM
{
public A A { get; set; }
public B B { get; set; }
public C C { get; set; }
}
public class A { }
public class B { }
public class C { }
public class MySelector : DataTemplateSelector
{
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item != null)
{
return (DataTemplate)((FrameworkElement)container).FindResource("dtpl" + item.GetType().Name);
}
else
{
return base.SelectTemplate(item, container);
}
}
}
If you try this small example, the text typed into the textboxes will persist across the tabs flipping. That is, the templates will be called once only.
Let me know.

Categories