WPF Data Binding with Subclass - c#

I trying to get into WPF so for that I was trying to get a good example for myself to start with. After a few tries I started to get a bit confused about how the databinding in WFP works.
Here my example:
Lets say i got a 3 classes in my project:
Product
Category
Tags
So for that I got these classes here:
Category:
public class Category
{
private int id;
private string name;
public int Id { get => id; set => id = value; }
public string Name { get => name; set => name = value; }
}
Tags:
public class Tag
{
private int id;
private string name;
public int Id { get => id; set => id = value; }
public string Name { get => name; set => name = value; }
}
Product:
public class Product
{
private int id;
private string name;
private double price;
private int categoryID;
private ICollection<Tag> tags;
public int Id { get => id; set => id = value; }
public string Name { get => name; set => name = value; }
public double Price { get => price; set => price = value; }
public int CategoryID { get => categoryID; set => categoryID = value; }
public ICollection<Tag> Tags { get => tags; set => tags = value; }
public Category Category
{
get => default(Category);
set{}
}
}
Lets say I got the data from somewhere to fill these classes:
List<Product> products = new List<Product>();
products.Add(new Product() { Id = 1, Name = "Toy Car", Price = 14.99,
CategoryID = 2, Tags = new List<Tag>() { new Tag { Id = 1, Name = "Toy" },
new Tag { Id = 2, Name = "Kids" } } });
products.Add(new Product() { Id = 1, Name = "Water", Price = 14.99,
CategoryID = 2, Tags = new List<Tag>() { new Tag { Id = 3, Name = "Food" } }
});
List<Category> categories = new List<Category>();
categories.Add(new Category() { Id = 1, Name = "Food" });
categories.Add(new Category() { Id = 2, Name = "Toys" });
My UI looks like this:
MainWindow.xaml:
<Window
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:wfp_test"x:Class="wfp_test.MainWindow"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800" Loaded="Window_Loaded">
<Window.Resources>
<CollectionViewSource x:Key="productViewSource" d:DesignSource="{d:DesignInstance {x:Type local:Product}, CreateList=True}"/>
<CollectionViewSource x:Key="productTagsViewSource" Source="{Binding Tags, Source={StaticResource productViewSource}}"/>
</Window.Resources>
<Grid DataContext="{StaticResource productTagsViewSource}" >
<Grid x:Name="grid1" DataContext="{StaticResource productViewSource}" HorizontalAlignment="Left" Margin="13,154,0,0" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Content="Category:" Grid.Column="0" HorizontalAlignment="Left" Margin="3" Grid.Row="0" VerticalAlignment="Center"/>
<ComboBox x:Name="categoryComboBox" Grid.Column="1" DisplayMemberPath="Category" HorizontalAlignment="Left" Height="Auto" ItemsSource="{Binding}" Margin="3" Grid.Row="0" VerticalAlignment="Center" Width="120">
<ComboBox.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel/>
</ItemsPanelTemplate>
</ComboBox.ItemsPanel>
</ComboBox>
<Label Content="Id:" Grid.Column="0" HorizontalAlignment="Left" Margin="3" Grid.Row="2" VerticalAlignment="Center"/>
<TextBox x:Name="idTextBox" Grid.Column="1" HorizontalAlignment="Left" Height="23" Margin="3" Grid.Row="2" Text="{Binding Id, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" VerticalAlignment="Center" Width="120"/>
<Label Content="Name:" Grid.Column="0" HorizontalAlignment="Left" Margin="3" Grid.Row="3" VerticalAlignment="Center"/>
<TextBox x:Name="nameTextBox" Grid.Column="1" HorizontalAlignment="Left" Height="23" Margin="3" Grid.Row="3" Text="{Binding Name, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" VerticalAlignment="Center" Width="120"/>
<Label Content="Price:" Grid.Column="0" HorizontalAlignment="Left" Margin="3" Grid.Row="4" VerticalAlignment="Center"/>
<TextBox x:Name="priceTextBox" Grid.Column="1" HorizontalAlignment="Left" Height="23" Margin="3" Grid.Row="4" Text="{Binding Price, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}" VerticalAlignment="Center" Width="120"/>
</Grid>
<DataGrid x:Name="tagsDataGrid" AutoGenerateColumns="False" EnableRowVirtualization="True" ItemsSource="{Binding}" Margin="382,155,10,64" RowDetailsVisibilityMode="VisibleWhenSelected">
<DataGrid.Columns>
<DataGridTextColumn x:Name="idColumn" Binding="{Binding Id}" Header="Id" Width="SizeToHeader"/>
<DataGridTextColumn x:Name="nameColumn" Binding="{Binding Name}" Header="Name" Width="SizeToHeader"/>
</DataGrid.Columns>
</DataGrid>
<ListView x:Name="productListView" ItemsSource="{Binding Source={StaticResource productViewSource}}" Margin="10,10,10,296" SelectionMode="Single">
<ListView.ItemContainerStyle>
<Style>
<Setter Property="Control.HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Control.VerticalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn x:Name="categoryColumn" Header="Category" Width="80">
<GridViewColumn.CellTemplate>
<DataTemplate>
<ComboBox Margin="6,-1,-6,-1">
<ComboBoxItem Content="{Binding Category}"/>
</ComboBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn x:Name="categoryIDColumn" Header="Category ID" Width="80">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Margin="-6,-1" Text="{Binding CategoryID, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn x:Name="idColumn1" Header="Id" Width="80">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Margin="-6,-1" Text="{Binding Id, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn x:Name="nameColumn1" Header="Name" Width="80">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Margin="-6,-1" Text="{Binding Name, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn x:Name="priceColumn" Header="Price" Width="80">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Margin="-6,-1" Text="{Binding Price, Mode=TwoWay, NotifyOnValidationError=true, ValidatesOnExceptions=true}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
MainWindow.xaml.cs:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
List<Product> products = new List<Product>();
products.Add(new Product() { Id = 1, Name = "Toy Car", Price = 14.99, CategoryID = 2, Tags = new List<Tag>() { new Tag { Id = 1, Name = "Toy" }, new Tag { Id = 2, Name = "Kids" } } });
products.Add(new Product() { Id = 1, Name = "Water", Price = 14.99, CategoryID = 1, Tags = new List<Tag>() { new Tag { Id = 3, Name = "Food" } } });
List<Category> categories = new List<Category>();
categories.Add(new Category() { Id = 1, Name = "Food" });
categories.Add(new Category() { Id = 2, Name = "Toys" });
System.Windows.Data.CollectionViewSource productViewSource = ((System.Windows.Data.CollectionViewSource)(this.FindResource("productViewSource")));
categoryComboBox.ItemsSource = categories;
productViewSource.Source = products;
}
}
My first problem here the categoryComboBox is always empty. My second problem ist how do I set the ValueMember and DisplayMember of this box and where can I fill it with the Data from categories?

The DisplayMemberPath should be set to "Name". You may also set SelectedValuePath to "Id":
<ComboBox x:Name="categoryComboBox" DisplayMemberPath="Name" SelectedValuePath="Id" ... />

Related

WPF how to clear the texbox content in list view using MVVM

I am using below code and MVVM observable, but "Clear" button not clears the content of text boxes.
What I need to do here?
MainWindow.xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30*"/>
<RowDefinition Height="10*"/>
</Grid.RowDefinitions>
<Grid>
<ListView ItemsSource="{Binding Credentials, Mode=OneWay}">
<ListView.View>
<GridView>
<GridViewColumn Header="User Name">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding UserName}" Width="100"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Header="Password">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Password}" Width="100"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
</Grid>
<Grid Grid.Row="1" >
<Button Grid.Column="0" Content="Clear" Command="{Binding ClearTextBoxCommand}" Width="150"/>
</Grid>
</Grid>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MyViewModel();
}
}
MyViewModel.cs
public class MyViewModel : INotifyPropertyChanged
{
private ObservableCollection<Credential> _credentials;
public RelayCommand ClearTextBoxCommand { get; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
{
var handler = PropertyChanged;
handler?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public ObservableCollection<Credential> Credentials
{
get => _credentials;
set
{
if (value == _credentials)
return;
_credentials = value;
OnPropertyChanged();
}
}
public MyViewModel()
{
var data = new List<Credential> {new Credential {UserName = "user1", Password = "password1"}};
_credentials = new ObservableCollection<Credential>(data);
ClearTextBoxCommand = new RelayCommand(ClearTextBox);
}
private void ClearTextBox()
{
var data = new List<Credential> { new Credential { UserName = "", Password = "" } };
_credentials = new ObservableCollection<Credential>(data);
MessageBox.Show("clear done!");
}
}
public class Credential
{
public string UserName { get; set; }
public string Password { get; set; }
}
Credentials property has notification about change in setter:
public ObservableCollection<Credential> Credentials
{
get => _credentials;
set
{
if (value == _credentials)
return;
_credentials = value;
OnPropertyChanged();
}
}
so if you need to change that collection, use property, not _credentials field
not _credentials = new ObservableCollection<Credential>(data);, but
private void ClearTextBox()
{
var data = new List<Credential> { new Credential { UserName = "", Password = "" } };
Credentials = new ObservableCollection<Credential>(data);
MessageBox.Show("clear done!");
}
Using the Credentials property instead of _credentials field is mandatory. But you do not have to assign a complete new ObservableCollection. Just clear the old one and add a new Credentials object.
private void ClearTextBox()
{
Credentials.Clear();
Credentials.Add(new Credential { UserName = "", Password = "" });
MessageBox.Show("clear done!");
}
This is working at my Application
View:
<UserControl x:Class="PRWSolution.UI.View.MachineView"
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:PRWSolution.UI.View"
xmlns:language="clr-namespace:PRWSolution.UI.Properties.Langs"
xmlns:setting="clr-namespace:PRWSolution.UI.Properties"
mc:Ignorable="d"
DataContext="{Binding Machine, Source={StaticResource Locator}}"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:ie="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:cmd="http://www.galasoft.ch/mvvmlight"
TextElement.Foreground="{DynamicResource MaterialDesignBody}"
Background="{DynamicResource MaterialDesignPaper}"
TextElement.FontWeight="Medium"
TextElement.FontSize="14"
FontFamily="{materialDesign:MaterialDesignFont}">
<Grid>
<StackPanel Orientation="Vertical">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Grid.ColumnSpan="2">
<!--Toolbar for interaction-->
<ToolBarTray Grid.Column="0" Margin="0,5,0,0">
<ToolBar Style="{DynamicResource MaterialDesignToolBar}" ClipToBounds="False">
<Button x:Name="SaveMachine" ToolTip="Save" Command="{Binding SaveCommand, Mode=OneWay}">
<materialDesign:PackIcon Kind="ContentSave" />
</Button>
<Button x:Name="EditeMachine" ToolTip="Edit" Command="{Binding UpdateCommand, Mode=OneWay}">
<materialDesign:PackIcon Kind="Edit" />
</Button>
<Button x:Name="DeleteMachine" ToolTip="Delete" Command="{Binding DeleteCommand, Mode=OneWay}">
<materialDesign:PackIcon Kind="Delete" />
</Button>
<Separator />
<Button x:Name="ClearForm" ToolTip="Clear Form">
<materialDesign:PackIcon Kind="LayersClear" />
</Button>
</ToolBar>
</ToolBarTray>
<ToolBarTray Grid.Column="1" HorizontalAlignment="Right" Margin="0,5,0,0">
<ToolBar Style="{DynamicResource MaterialDesignToolBar}" ClipToBounds="False">
<Button x:Name="ExportMachine" ToolTip="Export" Click="ExportMachine_Click">
<materialDesign:PackIcon Kind="TableExport" />
</Button>
</ToolBar>
</ToolBarTray>
</Grid>
</Grid>
<!--Card for input database information-->
<materialDesign:Card Margin="5">
<StackPanel>
<!--Expander general machine information-->
<Expander Header="{x:Static language:Lang.ExpMachineData }" FontSize="11" HorizontalAlignment="Stretch" Margin="5,5,5,0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<StackPanel Orientation="Vertical">
<TextBox x:Name="txtMachineID" Text="{Binding MachineID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" materialDesign:HintAssist.Hint="{x:Static language:Lang.DgMachineID}" materialDesign:TextFieldAssist.HasClearButton="True" Style="{StaticResource MaterialDesignFloatingHintTextBox}" VerticalAlignment="Center" Margin="5"/>
<TextBox x:Name="txtCustomerID" Text="{Binding CustomerID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" materialDesign:HintAssist.Hint="{x:Static language:Lang.DgCustomerId}" materialDesign:TextFieldAssist.HasClearButton="True" Style="{StaticResource MaterialDesignFloatingHintTextBox}" VerticalAlignment="Center" Margin="5"/>
</StackPanel>
</Grid>
<Grid Grid.Column="1">
<StackPanel Orientation="Vertical">
<ComboBox x:Name="cmbCustomerName" Width="300" HorizontalAlignment="Left" SelectedValuePath="ClientName" SelectedValue="{Binding CustomerName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding MachineCustomer.ClientDataContext, Source={StaticResource Locator}}" DisplayMemberPath="ClientName" materialDesign:HintAssist.Hint="Customer Name" Margin="5"/>
<TextBox x:Name="txtCity" Text="{Binding City, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" materialDesign:HintAssist.Hint="{x:Static language:Lang.DgCity}" materialDesign:TextFieldAssist.HasClearButton="True" Style="{StaticResource MaterialDesignFloatingHintTextBox}" VerticalAlignment="Center" Margin="5"/>
<TextBox x:Name="txtCountry" Text="{Binding Country, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" materialDesign:HintAssist.Hint="{x:Static language:Lang.DgCountry}" materialDesign:TextFieldAssist.HasClearButton="True" Style="{StaticResource MaterialDesignFloatingHintTextBox}" VerticalAlignment="Center" Margin="5"/>
</StackPanel>
</Grid>
</Grid>
</Expander>
<Border Background="{DynamicResource MaterialDesignDivider}" Height="1" HorizontalAlignment="Stretch" SnapsToDevicePixels="True" />
<!--Expander general part info-->
<Expander Header="Part Information" FontSize="11" HorizontalAlignment="Stretch" Margin="5,0,5,0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<StackPanel Orientation="Vertical">
<TextBox x:Name="txtPartName" Text="{Binding PartName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" materialDesign:HintAssist.Hint="Part Name" materialDesign:TextFieldAssist.HasClearButton="True" Style="{StaticResource MaterialDesignFloatingHintTextBox}" VerticalAlignment="Center" Margin="5"/>
<TextBox x:Name="txtPartNumber" Text="{Binding PartNumber, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" materialDesign:HintAssist.Hint="Part Number" materialDesign:TextFieldAssist.HasClearButton="True" Style="{StaticResource MaterialDesignFloatingHintTextBox}" VerticalAlignment="Center" Margin="5"/>
<TextBox x:Name="txtProject" Text="{Binding Project, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" materialDesign:HintAssist.Hint="Project" materialDesign:TextFieldAssist.HasClearButton="True" Style="{StaticResource MaterialDesignFloatingHintTextBox}" VerticalAlignment="Center" Margin="5"/>
</StackPanel>
</Grid>
</Grid>
</Expander>
<Border Background="{DynamicResource MaterialDesignDivider}" Height="1" HorizontalAlignment="Stretch" SnapsToDevicePixels="True" />
<!--Expander for Machine Serials-->
<Expander Header="{x:Static language:Lang.ExpMachineSerials}" FontSize="11" HorizontalAlignment="Stretch" Margin="5,0,5,0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<StackPanel Orientation="Vertical">
<TextBox x:Name="txtSpindleC1" Text="{Binding SpindleC1, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" materialDesign:HintAssist.Hint="{x:Static language:Lang.DgSpindleC1}" materialDesign:TextFieldAssist.HasClearButton="True" Style="{StaticResource MaterialDesignFloatingHintTextBox}" VerticalAlignment="Center" Margin="5"/>
<TextBox x:Name="txtSpindleC2" Text="{Binding SpindleC2, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" materialDesign:HintAssist.Hint="{x:Static language:Lang.DgSpindleC2}" materialDesign:TextFieldAssist.HasClearButton="True" Style="{StaticResource MaterialDesignFloatingHintTextBox}" VerticalAlignment="Center" Margin="5"/>
<TextBox x:Name="txtHoningHead" Text="{Binding HoningHead, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" materialDesign:HintAssist.Hint="{x:Static language:Lang.DgHoningHead}" materialDesign:TextFieldAssist.HasClearButton="True" Style="{StaticResource MaterialDesignFloatingHintTextBox}" VerticalAlignment="Center" Margin="5"/>
</StackPanel>
</Grid>
</Grid>
</Expander>
<Border Background="{DynamicResource MaterialDesignDivider}" Height="1" HorizontalAlignment="Stretch" SnapsToDevicePixels="True" />
<!--Expander for Softwareversion-->
<Expander Header="{x:Static language:Lang.ExpSoftwareVersion}" FontSize="11" HorizontalAlignment="Stretch" Margin="5,0,5,5">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<StackPanel Orientation="Vertical">
<TextBox x:Name="txtNCVersion" Text="{Binding NCVersion, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" materialDesign:HintAssist.Hint="{x:Static language:Lang.DgNCVersion}" materialDesign:TextFieldAssist.HasClearButton="True" Style="{StaticResource MaterialDesignFloatingHintTextBox}" VerticalAlignment="Center" Margin="5"/>
<TextBox x:Name="txtHMIVersion" Text="{Binding HMIVersion, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" materialDesign:HintAssist.Hint="{x:Static language:Lang.DgHMIVersion}" materialDesign:TextFieldAssist.HasClearButton="True" Style="{StaticResource MaterialDesignFloatingHintTextBox}" VerticalAlignment="Center" Margin="5"/>
<TextBox x:Name="txtHRIVersion" Text="{Binding HRIVersion, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" materialDesign:HintAssist.Hint="{x:Static language:Lang.DgHRIVersion}" materialDesign:TextFieldAssist.HasClearButton="True" Style="{StaticResource MaterialDesignFloatingHintTextBox}" VerticalAlignment="Center" Margin="5"/>
<TextBox x:Name="txtAHSVersion" Text="{Binding AHSVersion, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" materialDesign:HintAssist.Hint="{x:Static language:Lang.DgAHSVersion}" materialDesign:TextFieldAssist.HasClearButton="True" Style="{StaticResource MaterialDesignFloatingHintTextBox}" VerticalAlignment="Center" Margin="5"/>
</StackPanel>
</Grid>
</Grid>
</Expander>
</StackPanel>
</materialDesign:Card>
<!--Database datagrid-->
<DataGrid x:Name="MachineDataGrid" Margin="5" AutoGenerateColumns="False" MaxHeight="700" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Auto" ItemsSource="{Binding DataContext, Mode=TwoWay}" SelectedItem="{Binding Path=MachineSelectedItem, Mode=TwoWay}">
<ie:Interaction.Triggers>
<ie:EventTrigger EventName="SelectionChanged">
<ie:InvokeCommandAction Command="{Binding SelectionChangedCommand}" CommandParameter="{Binding ElementName=MachineDataGrid, Path=SelectedItem}"/>
</ie:EventTrigger>
</ie:Interaction.Triggers>
<DataGrid.Columns>
<materialDesign:DataGridTextColumn Header="{x:Static language:Lang.DgMachineID}" Binding="{Binding MachineID, Mode=TwoWay}"/>
<materialDesign:DataGridTextColumn Header="{x:Static language:Lang.DgCustomerId}" Binding="{Binding CustomerID}"/>
<materialDesign:DataGridTextColumn Header="{x:Static language:Lang.DgCustomerName}" Binding="{Binding CustomerName}"/>
<materialDesign:DataGridTextColumn Header="{x:Static language:Lang.DgCity}" Binding="{Binding City}"/>
<materialDesign:DataGridTextColumn Header="{x:Static language:Lang.DgCountry}" Binding="{Binding Country}"/>
<materialDesign:DataGridTextColumn Header="Part Name" Binding="{Binding PartName}"/>
<materialDesign:DataGridTextColumn Header="Part Number" Binding="{Binding PartNumber}"/>
<materialDesign:DataGridTextColumn Header="Project" Binding="{Binding Project}"/>
<materialDesign:DataGridTextColumn Header="{x:Static language:Lang.DgSpindleC1}" Binding="{Binding SpindleC1}"/>
<materialDesign:DataGridTextColumn Header="{x:Static language:Lang.DgSpindleC2}" Binding="{Binding SpindleC2}"/>
<materialDesign:DataGridTextColumn Header="{x:Static language:Lang.DgHoningHead}" Binding="{Binding HoningHead}"/>
<materialDesign:DataGridTextColumn Header="{x:Static language:Lang.DgNCVersion}" Binding="{Binding NCVersion}"/>
<materialDesign:DataGridTextColumn Header="{x:Static language:Lang.DgHMIVersion}" Binding="{Binding HMIVersion}"/>
<materialDesign:DataGridTextColumn Header="{x:Static language:Lang.DgHRIVersion}" Binding="{Binding HRIVersion}"/>
<materialDesign:DataGridTextColumn Header="{x:Static language:Lang.DgAHSVersion}" Binding="{Binding AHSVersion}"/>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Grid>
ViewModel:
public class MachineViewModel : ViewModelBase
{
#region public statments Textbox text
//Public statments to get textbox text
public string MachineID { get; set; }
public string CustomerID { get; set; }
public string CustomerName { get; set; }
public string City { get; set; }
public string Country { get; set; }
public string PartName { get; set; }
public string PartNumber { get; set; }
public string Project { get; set; }
public string SpindleC1 { get; set; }
public string SpindleC2 { get; set; }
public string HoningHead { get; set; }
public string NCVersion { get; set; }
public string HMIVersion { get; set; }
public string HRIVersion { get; set; }
public string AHSVersion { get; set; }
#endregion
//DialogService
private IDialogService _dialogService;
//Operation button for save, update, delete, selection changed and print
public ICommand SaveCommand { get; private set; }
public ICommand UpdateCommand { get; private set; }
public ICommand DeleteCommand { get; private set; }
public ICommand ClearCommand { get; private set; }
public ICommand SelectionChangedCommand { get; set; }
//observable collection for machine model
public ObservableCollection<Machine> DataContext { get; set; }
private Machine machineSelectedItem;
public Machine MachineSelectedItem
{
get { return machineSelectedItem; }
set { machineSelectedItem = value; }
}
public object MachineDataGrid { get; set; }
public List<MachineClient> cmbclientDetails;
//PRWContext for general use
private PRWContext context = new PRWContext();
public MachineViewModel()
{
//Commands for save, update, delete and print
SaveCommand = new RelayCommand(() => ExecuteSaveCommand());
UpdateCommand = new RelayCommand(() => ExecuteUpdateCommand());
DeleteCommand = new RelayCommand(() => ExecuteDeleteCommand());
ClearCommand = new RelayCommand(() => ExecuteClearCommand());
SelectionChangedCommand = new RelayCommand(() => ExecuteSelectionChangedCommand());
//Load the data from PRW Database to datagrid
LoadData();
//Normelly done with dependency injection
_dialogService = new DialogService();
}
//execute save
private void ExecuteSaveCommand()
{
Machine machine = new Machine
{
//Machine data
MachineID = MachineID,
CustomerID = CustomerID,
CustomerName = CustomerName,
City = City,
Country = Country,
//Part data
PartName = PartName,
PartNumber = PartNumber,
Project = Project,
//Serial data
SpindleC1 = SpindleC1,
SpindleC2 = SpindleC2,
HoningHead = HoningHead,
//Softwareversion data
NCVersion = NCVersion,
HMIVersion = HMIVersion,
HRIVersion = HRIVersion,
AHSVersion = AHSVersion
};
context.Machines.Add(machine);
context.SaveChanges();
ClearContent();
}
//execute update
private void ExecuteUpdateCommand()
{
Machine machine = context.Machines.FirstOrDefault(w => w.MachineID == MachineID);
machine.CustomerID = CustomerID;
machine.CustomerName = CustomerName;
machine.City = City;
machine.Country = Country;
machine.PartName = PartName;
machine.PartNumber = PartNumber;
machine.Project = Project;
machine.SpindleC1 = SpindleC1;
machine.SpindleC2 = SpindleC2;
machine.HoningHead = HoningHead;
machine.NCVersion = NCVersion;
machine.HMIVersion = HMIVersion;
machine.HRIVersion = HRIVersion;
machine.AHSVersion = AHSVersion;
context.Machines.AddOrUpdate(machine);
context.SaveChanges();
ClearContent();
}
//execute delete
private void ExecuteDeleteCommand()
{
if (machineSelectedItem != null)
{
var dialog = new YesNoDialogViewModel("Question", "Your are Sure to Delete the Record?");
var result = _dialogService.OpenDialog(dialog);
if (result == DialogResults.Yes)
{
Machine machine = context.Machines.FirstOrDefault(w => w.MachineID == MachineID);
context.Machines.Remove(machine);
context.SaveChanges();
ClearContent();
}
}
else
{
var dialog = new AlertDialogViewModel("Attention", "Please select a Record!");
var result = _dialogService.OpenDialog(dialog);
Console.WriteLine(result);
}
}
//execute clear
private void ExecuteClearCommand()
{
ClearContent();
}
// Execute selection changed
private void ExecuteSelectionChangedCommand()
{
if(machineSelectedItem != null)
{
MachineID = machineSelectedItem.MachineID?.ToString() ?? "";
CustomerID = machineSelectedItem.CustomerID?.ToString() ?? "";
CustomerName = machineSelectedItem.CustomerName?.ToString() ?? "";
City = machineSelectedItem.City?.ToString() ?? "";
Country = machineSelectedItem.Country?.ToString() ?? "";
PartName = machineSelectedItem.PartName?.ToString() ?? "";
PartNumber = machineSelectedItem.PartNumber?.ToString() ?? "";
Project = machineSelectedItem.Project?.ToString() ?? "";
SpindleC1 = machineSelectedItem.SpindleC1?.ToString() ?? "";
SpindleC2 = machineSelectedItem.SpindleC2?.ToString() ?? "";
HoningHead = machineSelectedItem.HoningHead?.ToString() ?? "";
NCVersion = machineSelectedItem.NCVersion?.ToString() ?? "";
HMIVersion = machineSelectedItem.HMIVersion?.ToString() ?? "";
HRIVersion = machineSelectedItem.HRIVersion?.ToString() ?? "";
AHSVersion = machineSelectedItem.AHSVersion?.ToString() ?? "";
}
else
{
machineSelectedItem = null;
}
}
//Load data from database to grid
private void LoadData()
{
context.Machines.Load();
this.DataContext = context.Machines.Local;
}
//Clear textboxes
private void ClearContent()
{
MachineID = string.Empty;
CustomerID = string.Empty;
CustomerName = string.Empty;
City = string.Empty;
PartName = string.Empty;
PartNumber = string.Empty;
Project = string.Empty;
Country = string.Empty;
SpindleC1 = string.Empty;
SpindleC2 = string.Empty;
HoningHead = string.Empty;
NCVersion = string.Empty;
HMIVersion = string.Empty;
HRIVersion = string.Empty;
AHSVersion = string.Empty;
}
}

wpf binding to selectedItem

In my example I'm binding to a selectedItem from a ListBox. I was wondering how can i set the binding in the stack panel so i don't have to then individually bind to each control.
Can I just bind the stack panel and then the sub controls just get bound like so (pseudo code)
<StackPanel Grid.Column="2" Content="{Binding SelectedItem.Name, ElementName=ItemList}"/>
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Text="{Binding Kids, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Text="{Binding Age, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
Code
<ListBox Grid.Column="0"
x:Name="ItemList"
Background="AliceBlue"
ItemsSource="{Binding VNodes}"
SelectedItem="{Binding SelectedVNode, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListBox.ItemTemplate>
<DataTemplate>
<WrapPanel>
<TextBlock Text="Name: " />
<TextBlock Text="{Binding Name}" FontWeight="Bold" />
</WrapPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" />
<StackPanel Grid.Column="2">
<TextBox Text="{Binding SelectedItem.Name, ElementName=ItemList, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Text="{Binding SelectedItem.Kids, ElementName=ItemList, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Text="{Binding SelectedItem.Age, ElementName=ItemList, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
In WPF, every Item has a DataContext for Bindings, You can set the DataContext of Stackpanel to
{Binding ElementName=ItemList, Path=SelectedItem},
And simply put
<TextBox Text="{Binding Age, UpdateSourceTrigger=PropertyChanged}"/>
inside the StackPanel as You wanted ;)
We have this class:
public class Jobs
{
public string Name { get; set; }
public List<string> Titles { get; set; }
}
MainViewModel (u need to make 2 property (fill these props random values)):
public MainViewModel()
{
ListJobs = new List<Jobs>();
ListJobs.Add(new Jobs() { Name = "Job1", Titles = new List<string>() {"Job1Title1","Job1Title2","Job1Title3" } });
ListJobs.Add(new Jobs() { Name = "Job2", Titles = new List<string>() {"Job2Title1","Job2Title2","Job2Title3" } });
ListJobs.Add(new Jobs() { Name = "Job3", Titles = new List<string>() {"Job3Title1","Job3Title2","Job3Title3" } });
}
private List<Jobs> listJobs;
public List<Jobs> ListJobs
{
get { return listJobs; }
set
{
if (value != listJobs)
{
listJobs = value;
OnPropertyChanged(nameof(ListJobs));
}
}
}
private Jobs selectedJob;
public Jobs SelectedJob
{
get { return selectedJob; }
set
{
if (value != selectedJob)
{
selectedJob = value;
OnPropertyChanged(nameof(SelectedJob));
}
}
}
XAML:
<Window.DataContext>
<local:MainViewModel/>
</Window.DataContext>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<DataGrid x:Name="Jobs" Grid.Column="0" AutoGenerateColumns="False" SelectedItem="{Binding SelectedJob, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding ListJobs, UpdateSourceTrigger=PropertyChanged}">
<DataGrid.Columns >
<DataGridTextColumn Binding="{Binding Path=Name, UpdateSourceTrigger=PropertyChanged}" Header="Job" Width="200*" IsReadOnly="False"/>
</DataGrid.Columns>
</DataGrid>
<DataGrid x:Name="JobTitles" Grid.Column="1" AutoGenerateColumns="False" ItemsSource="{Binding SelectedJob.Titles, UpdateSourceTrigger=PropertyChanged}">
<DataGrid.Columns >
<DataGridTextColumn Header="JobTitle" Width="200*" IsReadOnly="False" Binding="{Binding}"/>
</DataGrid.Columns>
</DataGrid>
</Grid>

WPF C# datagrid combobox on new row won't update datasource (updated!)

This is an example exhibiting the behaviour I'm having trouble with. I have a datagrid which is bound to an observable collection of records in a viewmodel. In the datagrid I have a DataGridTemplateColumn holding a combobox which is populated from a list in the viewmodel. The datagrid also contains text columns. There are some textboxes at the bottom of the window to show the record contents.
<Window x:Class="Customer.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Customer"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<local:SelectedRowConverter x:Key="selectedRowConverter"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="8*"/>
<RowDefinition Height="3*"/>
</Grid.RowDefinitions>
<DataGrid x:Name="dgCustomers" AutoGenerateColumns="False"
ItemsSource="{Binding customers}" SelectedItem="{Binding SelectedRow,
Converter={StaticResource selectedRowConverter}, Mode=TwoWay}"
CanUserAddRows="True" Grid.Row="0" SelectionChanged="dgCustomers_SelectionChanged">
<DataGrid.Columns>
<DataGridTemplateColumn Width="Auto" Header="Country">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox x:Name="cmbCountry" ItemsSource="{Binding DataContext.countries,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
DisplayMemberPath="name" SelectedValuePath="name" Margin="5"
SelectedItem="{Binding DataContext.SelectedCountry,
RelativeSource={RelativeSource AncestorType={x:Type Window}}, Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" SelectionChanged="cmbCountry_SelectionChanged" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Name" Binding="{Binding name}" Width="1*"/>
<DataGridTextColumn Header="Phone" Binding="{Binding phone}" Width="1*"/>
</DataGrid.Columns>
</DataGrid>
<Grid x:Name="grdDisplay" DataContext="{Binding ElementName=dgCustomers}" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="2" Content="Country:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<Label Grid.Column="4" Content="Code:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<BulletDecorator Grid.Column="0">
<BulletDecorator.Bullet>
<Label Content="Name:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
</BulletDecorator.Bullet>
<TextBox x:Name="txtId" Text="{Binding ElementName=dgCustomers, Path=SelectedItem.name}" Margin="5,5,5,5"/>
</BulletDecorator>
<BulletDecorator Grid.Column="1">
<BulletDecorator.Bullet>
<Label Content="Code:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
</BulletDecorator.Bullet>
<TextBox x:Name="txtCode" Text="{Binding ElementName=dgCustomers, Path=SelectedItem.countryCode}" Margin="5,5,5,5"/>
</BulletDecorator>
<BulletDecorator Grid.Column="2">
<BulletDecorator.Bullet>
<Label Content="Phone:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
</BulletDecorator.Bullet>
<TextBox x:Name="txtPhone" Text="{Binding ElementName=dgCustomers, Path=SelectedItem.phone}" Margin="5,5,5,5"/>
</BulletDecorator>
</Grid>
</Grid>
</Window>
Initially there are no records so the datagrid is empty and shows just one line containing the combobox. If the user enters data into the text columns first then a record is added to the collection and the combobox value can be added to the record. However, if the user selects the combobox value first, then the value disappears when another column is selected. How do I get the combobox data added to the record if it is selected first?
Codebehind:
public partial class MainWindow : Window
{
public GridModel gridModel { get; set; }
public MainWindow()
{
InitializeComponent();
gridModel = new GridModel();
//dgCustomers.DataContext = gridModel;
this.DataContext = gridModel;
}
private void cmbCountry_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ComboBox c = sender as ComboBox;
Debug.Print("ComboBox selection changed, index is " + c.SelectedIndex + ", selected item is " + c.SelectedItem);
}
}
The Record class:
public class Record : ViewModelBase
{
private string _name;
public string name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("name");
}
}
private string _phone;
public string phone
{
get { return _phone; }
set
{
_phone = value;
OnPropertyChanged("phone");
}
}
private int _countryCode;
public int countryCode
{
get { return _countryCode; }
set
{
_countryCode = value;
OnPropertyChanged("countryCode");
}
}
}
Country class:
public class Country : ViewModelBase
{
private string _name;
public string name
{
get { return _name; }
set
{
_name = value;
OnPropertyChanged("name");
}
}
private int _id;
public int id
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged("id");
}
}
private int _code;
public int code
{
get { return _code; }
set
{
_code = value;
OnPropertyChanged("code");
}
}
public override string ToString()
{
return _name;
}
}
GridModel:
public class GridModel : ViewModelBase
{
public ObservableCollection<Record> customers { get; set; }
public List<Country> countries { get; set; }
public GridModel()
{
customers = new ObservableCollection<Record>();
countries = new List<Country> { new Country { id = 1, name = "England", code = 44 }, new Country { id = 2, name = "Germany", code = 49 },
new Country { id = 3, name = "US", code = 1}, new Country { id = 4, name = "Canada", code = 11 }};
}
private Country _selectedCountry;
public Country SelectedCountry
{
get
{
return _selectedCountry;
}
set
{
_selectedCountry = value;
_selectedRow.countryCode = _selectedCountry.code;
OnPropertyChanged("SelectedRow");
}
}
private Record _selectedRow;
public Record SelectedRow
{
get
{
return _selectedRow;
}
set
{
_selectedRow = value;
Debug.Print("Datagrid selection changed");
OnPropertyChanged("SelectedRow");
}
}
}
Converters:
class Converters
{
}
public class SelectedRowConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is Record)
return value;
return new Customer.Record();
}
}
ViewModelBase:
public class ViewModelBase : INotifyPropertyChanged
{
public ViewModelBase()
{
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
}
The behaviour you are seeing is as expected. The reason behind it is that the ComboBox ItemsSource as well as SelectedItem both are bound to Properties of the Window's DataContext while the other columns are bound to your DataGrid's ItemsSource. Hence when you modify the columns other than the dropdown the data is added to the observable collection.
What you can do is after a value is selected from the drop down you need to add a record yourself (possibly by calling a function from your SelectedCountry property)
EDIT
Based on your code I made a working model making as little changes as possible to your existing code. I could not use the converter as I did not have the details of the class Customer
Xaml
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="8*"/>
<RowDefinition Height="3*"/>
</Grid.RowDefinitions>
<StackPanel Grid.Row="0">
<Button HorizontalAlignment="Right" Content="Add User" Margin="0,2,2,2" Command="{Binding AddUserCommand}"/>
<DataGrid x:Name="dgCustomers"
AutoGenerateColumns="False"
ItemsSource="{Binding customers}"
SelectedItem="{Binding SelectedRow}"
SelectionUnit="FullRow"
CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTemplateColumn Width="Auto" Header="Country">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox Focusable="False"
ItemsSource="{Binding DataContext.countries, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
DisplayMemberPath="name"
SelectedValuePath="code"
SelectedValue="{Binding countryCode, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Name" Binding="{Binding name, UpdateSourceTrigger=PropertyChanged}" Width="1*"/>
<DataGridTextColumn Header="Phone" Binding="{Binding phone, UpdateSourceTrigger=PropertyChanged}" Width="1*"/>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
<Grid x:Name="grdDisplay" DataContext="{Binding ElementName=dgCustomers}" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Label Grid.Column="2" Content="Country:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<Label Grid.Column="4" Content="Code:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
<BulletDecorator Grid.Column="0">
<BulletDecorator.Bullet>
<Label Content="Name:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
</BulletDecorator.Bullet>
<TextBox x:Name="txtId" Text="{Binding ElementName=dgCustomers, Path=SelectedItem.name}" Margin="5,5,5,5"/>
</BulletDecorator>
<BulletDecorator Grid.Column="1">
<BulletDecorator.Bullet>
<Label Content="Code:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
</BulletDecorator.Bullet>
<TextBox x:Name="txtCode" Text="{Binding ElementName=dgCustomers, Path=SelectedItem.countryCode}" Margin="5,5,5,5"/>
</BulletDecorator>
<BulletDecorator Grid.Column="2">
<BulletDecorator.Bullet>
<Label Content="Phone:" VerticalAlignment="Center" HorizontalAlignment="Right"/>
</BulletDecorator.Bullet>
<TextBox x:Name="txtPhone" Text="{Binding ElementName=dgCustomers, Path=SelectedItem.phone}" Margin="5,5,5,5"/>
</BulletDecorator>
</Grid>
</Grid>
Your GridModel class
public class GridModel : ViewModelBase
{
public ObservableCollection<Record> customers { get; set; }
public ObservableCollection<Country> countries
{
get;
private set;
}
public GridModel()
{
customers = new ObservableCollection<Record> { };
AddUserCommand = new RelayCommand(AddNewUser);
countries = new ObservableCollection<Country>
{
new Country { id = 1, name = "England", code = 44 },
new Country { id = 2, name = "Germany", code = 49 },
new Country { id = 3, name = "US", code = 1},
new Country { id = 4, name = "Canada", code = 11 }
};
}
private void AddNewUser()
{
customers.Add(new Record());
}
public ICommand AddUserCommand { get; set; }
private Record _selectedRow;
public Record SelectedRow
{
get
{
return _selectedRow;
}
set
{
_selectedRow = value;
Debug.Print("Datagrid selection changed");
OnPropertyChanged("SelectedRow");
}
}
}
I have used MVVMLight toolkit which contains RelayCommand. You can also define your own ICommand implementation and use it instead of the toolkit
EDIT 2
Fixed the bug introduced by me which would prevent the combobox from displaying the Country if the data comes from the data base. The improved code does not require any converter either

Selected Value in Multi-Column ComboBox

I have a table named Groups in my Database as follows:
Groups
|--GroupID
|--GroupName
|--ParentID
|--NatureOfGroupID
|--EffectID
Here is the relationship diagram for above table:
I have a combobox in a window in which I have two columns. Here is the xaml:
<ComboBox ItemsSource="{Binding DataContext.GroupNamesWithCorrespondingEffects, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Page}}}"
SelectedValue="{Binding ParentID}" SelectedValuePath="GroupID">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding GroupName}" Width="200" />
<TextBlock Text="{Binding CorrespondingEffect}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Actually i want to show GroupName and its corresponding Effect but when user selects any Item then I want to get SelectedValue as ParentID.
Here is the implementation of GroupNameWithCorrespondingEffect.cs
public class GroupNameWithCorrespondingEffect : MainViewModel
{
private string _groupName;
public string GroupName
{
get
{
return _groupName;
}
set
{
_groupName = value;
OnPropertyChanged("GroupName");
}
}
private string _correspondingEffect;
public string CorrespondingEffect
{
get
{
return _correspondingEffect;
}
set
{
_correspondingEffect = value;
OnPropertyChanged("CorrespondingEffect");
}
}
}
Here is the code for groupsViewModel.cs
public class GroupsViewModel : MainViewModel
{
public GroupsViewModel()
{
using (Entities db = new Entities())
{
GroupNamesWithCorrespondingEffects = (from g in db.Groups
select new GroupNameWithCorrespondingEffect
{
GroupName = g.GroupName,
CorrespondingEffect = g.Master_Effects.Effect
}).ToList();
NaturesOfGroup = (from m in db.Master_NatureOfGroup
select m).ToList();
}
SaveChangesCommand = new RelayCommand(SaveGroup);
CurrentGroup = new Group();
}
private Group _currentGroup;
public Group CurrentGroup
{
get
{
return _currentGroup;
}
set
{
_currentGroup = value;
OnPropertyChanged("CurrentGroup");
}
}
private List<GroupNameWithCorrespondingEffect> _groupNamesWithCorrespondingEffects;
public List<GroupNameWithCorrespondingEffect> GroupNamesWithCorrespondingEffects
{
get
{
return _groupNamesWithCorrespondingEffects;
}
set
{
_groupNamesWithCorrespondingEffects = value;
OnPropertyChanged("GroupNamesWithCorrespondingEffects");
}
}
private List<Master_NatureOfGroup> _naturesOfGroup;
public List<Master_NatureOfGroup> NaturesOfGroup
{
get
{
return _naturesOfGroup;
}
set
{
_naturesOfGroup = value;
OnPropertyChanged("NaturesOfGroup");
}
}
public ICommand SaveChangesCommand { get; set; }
private void SaveGroup(object obj)
{
Group cGroup = new Group()
{
GroupName = CurrentGroup.GroupName,
**ParentID = CurrentGroup.ParentID,**
NatureOfGroupID = CurrentGroup.NatureOfGroupID,
EffectID = CurrentGroup.EffectID
};
using (Entities db = new Entities())
{
db.Groups.Add(cGroup);
db.SaveChanges();
GroupNamesWithCorrespondingEffects = (from g in db.Groups
select new GroupNameWithCorrespondingEffect
{
GroupName = g.GroupName,
CorrespondingEffect = g.Master_Effects.Effect
}).ToList();
}
}
}
When I debug and put a breakpoint on the line marked with ** .... ** I always get CurrentGroup.ParentID = null.
Update:
<Page.DataContext>
<vm:GroupsViewModel />
</Page.DataContext>
<Grid DataContext="{Binding CurrentGroup}">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="0.6*" />
<ColumnDefinition Width="0.6*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="1" Grid.Column="1" Text="Name" HorizontalAlignment="Left"/>
<TextBlock Grid.Row="1" Grid.Column="1" Text=" : " HorizontalAlignment="Right"/>
<TextBox Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="2" Text="{Binding GroupName}"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text="Under" HorizontalAlignment="Left"/>
<TextBlock Grid.Row="2" Grid.Column="1" Text=" : " HorizontalAlignment="Right"/>
<ComboBox x:Name="cbUnder" Grid.Row="2" Grid.Column="2" Grid.ColumnSpan="2"
ItemsSource="{Binding DataContext.GroupNamesWithCorrespondingEffects, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Page}}}"
SelectedValue="{Binding ParentID}" SelectedValuePath="GroupID">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding GroupName}" Width="200" />
<TextBlock Text="{Binding CorrespondingEffect}" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
<TextBlock Grid.Row="3" Grid.Column="1" Text="Nature of Group" HorizontalAlignment="Left" Visibility="{Binding Visibility, ElementName=cbNature}"/>
<TextBlock Grid.Row="3" Grid.Column="1" Text=" : " HorizontalAlignment="Right" Visibility="{Binding Visibility, ElementName=cbNature}"/>
<ComboBox x:Name="cbNature" Grid.Row="3" Grid.Column="2" Visibility="{Binding SelectedIndex, Converter={StaticResource selectedIndexToVisibilityConverter}, ElementName=cbUnder}"
ItemsSource="{Binding DataContext.NaturesOfGroup, RelativeSource={RelativeSource AncestorType={x:Type Page}}}" DisplayMemberPath="Nature"
SelectedValue="{Binding NatureOfGroupID}" SelectedValuePath="NatureOfGroupID"/>
<StackPanel Grid.Row="5" Grid.Column="4" Orientation="Horizontal">
<Button Content="Save" Command="{Binding DataContext.SaveChangesCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Page}}}"/>
</StackPanel>
</Grid>
I can see two issues in your code.
First of all you mentioned
Actually i want to show GroupName and its corresponding Effect but
when user selects any Item then I want to get SelectedValue as
ParentID.
But in combobox declaration, you set SelectedValuePath="GroupID". It should be set to ParentID - SelectedValuePath="ParentID".
Second, even if you set SelectedValuePath to ParentID, it won't work since combobox ItemsSource is a list of GroupNameWithCorrespondingEffect which doesn't have any property ParentID in it.
For SelectedValuePath to work underlying model class should have that property, so create a property and filled it from database like other two properties.
GroupNamesWithCorrespondingEffects = (from g in db.Groups
select new GroupNameWithCorrespondingEffect
{
GroupName = g.GroupName,
CorrespondingEffect = g.Master_Effects.Effect,
ParentID = g.ParentId
}).ToList();
So, for your solution to work, you have to fix both these issues.

TreeView has stop working when click on the root of the treeview

the error happen when click on the root of the treeview.
how to fix this problem.
I upload this project on 2shared.com
this project on 2shared.com
download here
this is my XAML
<Window x:Class="ExTreeview.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ExTreeview"
Title="MainWindow" Height="550" Width="525">
<Window.Resources>
<HierarchicalDataTemplate x:Key="LocationTemplate" ItemsSource="{Binding SubLocation}">
<StackPanel>
<Label Content="{Binding name}"/>
</StackPanel>
</HierarchicalDataTemplate>
<DataTemplate DataType="{x:Type local:MyLocation}">
<TextBlock>for root</TextBlock>
</DataTemplate>
<DataTemplate DataType="{x:Type local:house}" > <!-- object from house class use this DataTemplate-->
<StackPanel Margin="0,70,0,0">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="60" />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center"> ชื่อ</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="0">
ความจุ(ตัว)
</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="0"> สถานะ</TextBlock>
<TextBlock Grid.Row="3" Grid.Column="0"> หมายเหตุ</TextBlock>
<TextBox Grid.Column="1" Grid.Row="0" Height="20" x:Name="txtName" Text="{Binding Path=name}"></TextBox>
<TextBox Grid.Column="1" Grid.Row="1" Width="70" HorizontalAlignment="Left" x:Name="txtCapacity" Text="{Binding Path=capacity}">
</TextBox>
<ComboBox Grid.Column="1" Grid.Row="2" Width="70" HorizontalAlignment="Left" x:Name="cmbStatus" >
<ComboBoxItem Content="{Binding status}" />
</ComboBox>
<TextBox Grid.Column="1" Grid.Row="3" x:Name="txtComment" Text="{Binding Path=comment}"></TextBox>
</Grid>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Location}" > <!-- object from Location class use this DataTemplate-->
<StackPanel Margin="0,70,0,0" >
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="60" />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center"> ชื่อ</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="0"> สถานะ</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="0"> หมายเหตุ</TextBlock>
<TextBox Grid.Column="1" Grid.Row="0" Height="20" x:Name="txtName" Text="{Binding Path=name}"></TextBox>
<ComboBox Grid.Column="1" Grid.Row="1" Width="70" HorizontalAlignment="Left" x:Name="cmbStatus" >
<ComboBoxItem Content="{Binding status}" />
</ComboBox>
<TextBox Grid.Column="1" Grid.Row="2" x:Name="txtComment" Text="{Binding Path=comment}"></TextBox>
</Grid>
</StackPanel>
</DataTemplate>
<DataTemplate DataType="{x:Type local:zone}"> <!-- object from zone class use this DataTemplate-->
<StackPanel Margin="0,70,0,0" DataContext="{Binding ElementName=TreeView1, Path=SelectedItem}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="60" />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="70" />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" VerticalAlignment="Center"> ชื่อ</TextBlock>
<TextBlock Grid.Row="1" Grid.Column="0"> สถานะ</TextBlock>
<TextBlock Grid.Row="2" Grid.Column="0"> หมายเหตุ</TextBlock>
<TextBox Grid.Column="1" Grid.Row="0" Height="20" x:Name="txtName" Text="{Binding Path=name}"></TextBox>
<ComboBox Grid.Column="1" Grid.Row="1" Width="70" HorizontalAlignment="Left" x:Name="cmbStatus" >
<ComboBoxItem Content="{Binding status}" />
</ComboBox>
<TextBox Grid.Column="1" Grid.Row="2" x:Name="txtComment" Text="{Binding Path=comment}"></TextBox>
</Grid>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="450" />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TreeView Grid.Column="0" Grid.Row="0" Name="TreeView1" >
<TreeViewItem Header="ทั้งหมด" Name="TreeviewRootNode" ItemTemplate="{DynamicResource LocationTemplate}">
</TreeViewItem>
</TreeView>
<StackPanel Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="นำไปใช้งาน" Height="30" Width="80" />
<Button Content="ยกเลิก" Height="30" Margin="15" Width="50" Click="Button_Click" />
</StackPanel>
<ContentControl Content="{Binding ElementName=TreeView1, Path=SelectedItem}" Grid.Column="1" Grid.Row="0">
</ContentControl>
</Grid>
here cs file of the xaml
namespace ExTreeview
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MyLocation locationList = new MyLocation();
Location location1 = new Location();
location1.name = "ฟาร์มชัยภูมิ";
location1.id = "01";
location1.comment = "คอมเม้น";
location1.status = "ใช้งาน";
location1.SubLocation = new List<zone>();
List<house> sub1 = new List<house>();
sub1.Add(new house() { name = "โรงเรือน ช11", capacity = "50,000", status = "ใช้งาน", comment = "comment" });
sub1.Add(new house() { name = "โรงเรือน ช12", capacity = "30,000", status = "ใช้งาน", comment = "comment" });
location1.SubLocation.Add(new zone() { name = "โซน ช1", SubLocation = sub1, status = "ใช้งาน", comment = "comment" });
List<house> sub2 = new List<house>();
sub2.Add(new house() { name = "โรงเรือน ช21", capacity = "80,000", status = "ใช้งาน", comment = "comment" });
sub2.Add(new house() { name = "โรงเรือน ช22", capacity = "50,000", status = "ใช้งาน", comment = "comment" });
location1.SubLocation.Add(new zone() { name = "โซน ช2", SubLocation = sub2, status = "ใช้งาน", comment = "comment" });
Location location2 = new Location();
location2.name = "ฟาร์มนครนายก";
location2.SubLocation = new List<zone>();
List<house> sub3 = new List<house>();
List<house> sub4 = new List<house>();
List<house> sub6 = new List<house>();
sub3.Add(new house() { name = "โรงเรือนย่อย นย1", capacity = "50,000", status = "ใช้งาน", comment = "comment" });
sub3.Add(new house() { name = "โรงเรือนย่อย นย2", capacity = "60,000", status = "ใช้งาน", comment = "comment" });
sub4.Add(new house() { name = "โซนย่อย นย1", SubLocation = sub3, status = "ใช้งาน", comment = "comment" });
sub4.Add(new house() { name = "โรงเรือน น11", capacity = "50,000", status = "ใช้งาน", comment = "comment" });
sub4.Add(new house() { name = "โรงเรือน น12", capacity = "40,000", status = "ใช้งาน", comment = "comment" });
location2.SubLocation.Add(new zone() { name = "โซน น1", SubLocation=sub4, status = "ใช้งาน", comment = "comment" });
List<house> sub5 = new List<house>();
sub5.Add(new house() { name = "โรงเรือน น21", capacity = "30,000", status = "ใช้งาน", comment = "comment" });
location2.SubLocation.Add(new zone() { name = "โซน น2", SubLocation = sub5, status = "ใช้งาน", comment = "comment" });
locationList.Add(location1);
locationList.Add(location2);
TreeviewRootNode.ItemsSource = locationList;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
this.Close();
}
}
}
here Location class
namespace ExTreeview
{
public class Location
{
public string name { set; get; }
public string id { set; get; }
public string status { set; get; }
public string comment { set; get; }
public List<zone> SubLocation { set; get; }
public override string ToString()
{
return (this.name);
}
}
public class zone : Location
{
public string capacity { set; get; }
public List<house> SubLocation { set; get; }
public List<house> SubLocationZ { set; get; }
}
public class house : Location
{
public string capacity { set; get; }
public List<house> SubLocation { set; get; }
}
public class MyLocation : List<Location> { }
}
help me please.
I'm try to fix this over 2 day.
thank for help me.
Your problem is caused by the explicit root TreeViewItem and by the ContentControl which is bound to the TreeView's SelectedItem.
When you select the root, the binding tries to make the treeview's root item (which is TreeViewItem) as a logical child of the ContentControl. Because in WPF an element can be only a logical child of one another element and because your root item is already the child of the treeview you get the exception:
Specified element is already the logical child of another element.
Disconnect it first.
If you select any other node it works because in these cases the SelectedItem is not a TreeViewItem but an instance of your Location class.
To make it work you should create your root node in code instead of XAML:
public class Root
{
public string name { get; set; }
public MyLocation Locations { get; set; }
}
Instead of TreeviewRootNode.ItemsSource = locationList;:
var root = new Root() { Locations = locationList, name = "ทั้งหมด" };
TreeView1.ItemsSource = new List<Root> { root };
XAML:
<TreeView Grid.Column="0" Grid.Row="0" Name="TreeView1"
ItemTemplate="{DynamicResource RootTemplate}" />
DataTemplates:
<HierarchicalDataTemplate x:Key="RootTemplate" ItemsSource="{Binding Locations}"
ItemTemplate="{StaticResource LocationTemplate}">
<StackPanel>
<Label Content="{Binding name}"/>
</StackPanel>
</HierarchicalDataTemplate>
<!--Empty DataTemplate to display the root when selected-->
<DataTemplate DataType="{x:Type local:Root}" />

Categories