Reputation: 585
I have tried numerous Stack Overflow answers but I just cannot find a straight-forward easy example of what I am hoping to achieve.
I am attempting to fill a combo box with distinct results from a table (dimDate) and also change that combo box selection when the grid row is selected (from safety table).
Needless to say what I kept trying does not work as expected. What am I doing wrong, and how can I get around it with the simplest working example?
MainWindow.xaml
<Label Content="Year" Margin="0,0,10,0" Grid.Row="0" Grid.Column="0"/>
<ComboBox x:Name="txtyr" Grid.Row="0" Grid.Column="0"
Width="115" SelectedIndex="0" DisplayMemberPath="Year"
ItemsSource="{Binding Years}"
SelectedValue="{Binding Path=years_SelectedValue}"
SelectedValuePath="value">
</ComboBox>
<Label Content="Quarter" Margin="0,0,10,0" Grid.Row="0" Grid.Column="1"/>
<ComboBox x:Name="txtqt" Grid.Row="0" Grid.Column="1"
Width="115" SelectedIndex="0" DisplayMemberPath="Year"
ItemsSource="{Binding Quarters}"
SelectedValue="{Binding Path=quarters_SelectedValue}"
SelectedValuePath="value"/>
<Label Content="Name" Margin="0,0,10,0" Grid.Row="0" Grid.Column="2"/>
<ComboBox x:Name="txtnm" Grid.Row="0" Grid.Column="2"
Width="115" SelectedIndex="0" DisplayMemberPath="Year"
ItemsSource="{Binding Names}"
SelectedValue="{Binding Path=names_SelectedValue}"
SelectedValuePath="value"/>
<Label Content="Safery Score:" Margin="0,0,10,0" Grid.Row="1" Grid.Column="0"/>
<Controls:NumericUpDown x:Name="txtssc" Grid.Row="2" Grid.Column="0"
Controls:TextBoxHelper.HasText="True" Width="115" Height="20"
Controls:TextBoxHelper.Watermark="Score"
UpDownButtonsWidth="25" Maximum="10" Minimum="-10" HasDecimals="False"
InterceptArrowKeys="True" InterceptMouseWheel="True"
/>
<DataGrid Grid.Row="1" Margin="10,10,0,0" AutoGenerateColumns="False" VirtualizingPanel.IsVirtualizingWhenGrouping="True" EnableColumnVirtualization="True" EnableRowVirtualization="True" VirtualizingPanel.IsVirtualizing="true" ColumnHeaderStyle="{StaticResource lowCase}" x:Name="dtGrid" HorizontalAlignment="Left" CanUserResizeRows="False" ItemsSource="{Binding}" GridLinesVisibility="All" HorizontalContentAlignment="Stretch" CanUserAddRows="false" SelectedCellsChanged="dtGrid_SelectedCellsChanged" VerticalAlignment="Top" Grid.ColumnSpan="2" DataContext="{Binding Mode=OneWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type local:MainWindow}}}" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Year}" Header="Year"/>
<DataGridTextColumn Binding="{Binding Quarter}" Header="Quarter"/>
<DataGridTextColumn Binding="{Binding Name}" Header="Name"/>
<DataGridTextColumn Binding="{Binding SafetyScore}" Header="Safety Score"/>
<DataGridTextColumn Binding="{Binding ID}" Header="ID" Visibility="Collapsed"/>
</DataGrid.Columns>
</DataGrid>
MainWindow.cs
namespace Safety
{
public partial class MainWindow : MetroWindow
{
public MainWindow()
{
InitializeComponent();
DataContext = new SafetyViewModel();
string connectionString = "data Source=xxx; initial catalog=xxx; user id=xxx; password=xxx";
SqlConnection connection = new SqlConnection(connectionString);
SqlCommand cmd = new SqlCommand("Select ID, Year, Quarter, Name, SaferyScore from MATRIX.dbo.Safety", connection);
try
{
connection.Open();
DataTable dt = new DataTable();
dt.Load(cmd.ExecuteReader());
dtGrid.DataContext = dt;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
connection.Close();
}
private void dtGrid_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
{
DataRowView row_selected = dtGrid.SelectedItem as DataRowView;
if (row_selected == null) return;
txtyr.ItemsSource = row_selected["Year"].ToString();
txtqt.Text = row_selected["Quarter"].ToString();
txtnm.Text = row_selected["Name"].ToString();
txtscc.Value = Convert.ToInt16(row_selected["SafetyScore"].ToString());
txtID.Text = row_selected["ID"].ToString();
}
}
}
SafetyViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ComponentModel;
using System.Runtime.CompilerServices;
using Safety.Model;
using System.Windows;
using Safety;
using System.Collections.ObjectModel;
namespace Safety.ViewModel
{
public class SafetyViewModel : INotifyPropertyChanged
{
public SafetyViewModel()
{
this.loadyearlist();
this.loadnamelist();
this.loadquarterslist();
}
public void loadyearlist()
{
using (MATRIXEntities db = new MATRIXEntities())
{
var yrs = (from a in db.dimDate
select a).Distinct()
.ToList();
Years = new ObservableCollection<dimDate>(yrs);
}
}
public void loadquarterslist()
{
using (MATRIXEntities db = new MATRIXEntities())
{
var qts = (from a in db.dimDate
select a).Distinct()
.ToList();
Quarters = new ObservableCollection<dimDate>(qts);
}
}
public void loadnamelist()
{
using (MATRIXEntities db = new MATRIXEntities())
{
var nms = (from a in db.Employees
select a)
.ToList();
Employees = new ObservableCollection<Employees>(nms);
}
}
private ObservableCollection<dimDate> years;
public ObservableCollection<dimDate> Years
{
get { return years; }
set
{
years = value;
OnPropertyChanged("Years");
}
}
public string _years_SelectedValue;
private string years_SelectedValue
{
get { return _years_SelectedValue; }
set
{
_years_SelectedValue = value;
OnPropertyChanged("years_SelectedValue");
}
}
private ObservableCollection<dimDate> quarters;
public ObservableCollection<dimDate> Quarters
{
get { return quarters; }
set
{
quarters = value;
OnPropertyChanged("Quarters");
}
}
public string _quarters_SelectedValue;
private string quarters_SelectedValue
{
get { return _quarters_SelectedValue; }
set
{
_quarters_SelectedValue = value;
OnPropertyChanged("quarters_SelectedValue");
}
}
private ObservableCollection<Employees> employees;
public ObservableCollection<Employees> Employees
{
get { return employees; }
set
{
employees = value;
OnPropertyChanged("Employees");
}
}
public string _names_SelectedValue;
private string names_SelectedValue
{
get { return _names_SelectedValue; }
set
{
_names_SelectedValue = value;
OnPropertyChanged("names_SelectedValue");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName]string propertyName = null)
=> PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Model
namespace Safety.Model
{
using System;
using System.Collections.Generic;
public partial class dimDate
{
public int DateID { get; set; }
public System.DateTime Date { get; set; }
public int Year { get; set; }
public int Month { get; set; }
public int Day { get; set; }
public int Quarter { get; set; }
}
}
namespace Safety.Model
{
using System;
using System.Collections.Generic;
public partial class Safety
{
public int ID { get; set; }
public int Year { get; set; }
public int Quarter { get; set; }
public string Name { get; set; }
public int SafetyScore { get; set; }
}
}
namespace Safety.Model
{
using System;
using System.Collections.Generic;
public partial class Employees
{
public int ID { get; set; }
public string Name { get; set; }
public string Position { get; set; }
}
}
Upvotes: 2
Views: 675
Reputation: 10849
Your code has lot of problems. Few are listed below:
Text
of ComboBox instead of SelectedValue
I've written below code which demonstrates the requirement using MVVM design pattern.
MainWindow.xaml
<Window x:Class="WpfApp1.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:WpfApp1"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.DataContext>
<local:MainWindowViewModel />
</Window.DataContext>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<StackPanel Margin="5" Grid.Column="0">
<Label Content="Year" />
<ComboBox x:Name="txtyr"
ItemsSource="{Binding Years}"
SelectedValue="{Binding Path=years_SelectedValue}" />
</StackPanel>
<StackPanel Margin="5" Grid.Column="1">
<Label Content="Quarter"/>
<ComboBox x:Name="txtqt"
ItemsSource="{Binding Quarters}"
SelectedValue="{Binding Path=quarters_SelectedValue}" />
</StackPanel>
<StackPanel Margin="5" Grid.Column="2">
<Label Content="Name" />
<ComboBox x:Name="txtnm"
ItemsSource="{Binding Names}"
SelectedValue="{Binding Path=names_SelectedValue}" />
</StackPanel>
</Grid>
<DataGrid Grid.Row="1" x:Name="dtGrid" ItemsSource="{Binding Safeties}" SelectedItem="{Binding SelectedSafety}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Year}" Header="Year"/>
<DataGridTextColumn Binding="{Binding Quarter}" Header="Quarter"/>
<DataGridTextColumn Binding="{Binding Name}" Header="Name"/>
<DataGridTextColumn Binding="{Binding SafetyScore}" Header="Safety Score"/>
<DataGridTextColumn Binding="{Binding ID}" Header="ID" Visibility="Collapsed"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
For demo, I've defined the ViewModel in View.
MainViewModel.Cs
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
namespace WpfApp1
{
public class MainWindowViewModel : INotifyPropertyChanged
{
public MainWindowViewModel()
{
var data = Model.GetData();
Safeties.AddRange(data);
Years.AddRange(data.Select(d => d.Year).Distinct());
Quarters.AddRange(data.Select(d => d.Quarter).Distinct());
Names.AddRange(data.Select(d => d.Name).Distinct());
}
private ObservableCollection<int> years = new ObservableCollection<int>();
public ObservableCollection<int> Years
{
get { return years; }
}
private int _years_SelectedValue;
public int years_SelectedValue
{
get { return _years_SelectedValue; }
set
{
_years_SelectedValue = value;
OnPropertyChanged("years_SelectedValue");
}
}
private ObservableCollection<int> quarters = new ObservableCollection<int>();
public ObservableCollection<int> Quarters
{
get { return quarters; }
}
private int _quarters_SelectedValue;
public int quarters_SelectedValue
{
get { return _quarters_SelectedValue; }
set
{
_quarters_SelectedValue = value;
OnPropertyChanged("quarters_SelectedValue");
}
}
private ObservableCollection<string> names = new ObservableCollection<string>();
public ObservableCollection<string> Names
{
get { return names; }
}
private string _names_SelectedValue;
public string names_SelectedValue
{
get { return _names_SelectedValue; }
set
{
_names_SelectedValue = value;
OnPropertyChanged("names_SelectedValue");
}
}
private ObservableCollection<Safety> safeties = new ObservableCollection<Safety>();
public ObservableCollection<Safety> Safeties
{
get { return safeties; }
}
private Safety selectedSafety;
public Safety SelectedSafety
{
get { return selectedSafety; }
set
{
selectedSafety = value;
this.OnPropertyChanged(nameof(SelectedSafety));
if (this.selectedSafety != null)
{
this.years_SelectedValue = selectedSafety.Year;
this.quarters_SelectedValue = selectedSafety.Quarter;
this.names_SelectedValue = selectedSafety.Name;
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
}
With above code incorporation, the below gif demos how the feature works:
Additional code
To demo, I've created a class to get the Data
public static class Model
{
public static List<Safety> GetData()
{
return new List<Safety>()
{
new Safety() { ID = 1, Name = "First", Quarter = 1, SafetyScore = 1, Year = 2001 },
new Safety() { ID = 2, Name = "Second", Quarter = 2, SafetyScore = 1, Year = 2001 },
new Safety() { ID = 3, Name = "Third", Quarter = 3, SafetyScore = 1, Year = 2001 },
new Safety() { ID = 4, Name = "Fourth", Quarter = 4, SafetyScore = 1, Year = 2001 },
new Safety() { ID = 5, Name = "First", Quarter = 1, SafetyScore = 1, Year = 2002 },
new Safety() { ID = 6, Name = "Second", Quarter = 2, SafetyScore = 1, Year = 2002 },
new Safety() { ID = 7, Name = "Third", Quarter = 3, SafetyScore = 1, Year = 2002 },
new Safety() { ID = 8, Name = "Fourth", Quarter = 4, SafetyScore = 1, Year = 2002 },
};
}
}
And extension for AddRange for ObservableCollection
. However, you can use the ObservableRangeCollection introduced in C#7.0
public static class ObservableCollectionExtensions
{
public static void AddRange<T>(this ObservableCollection<T> observableCollection, IEnumerable<T> data)
{
foreach (T item in data)
{
observableCollection.Add(item);
}
}
}
Safety.cs
public class Safety : INotifyPropertyChanged
{
private int id;
public int ID
{
get
{
return id;
}
set
{
id = value;
this.RaisePropertyChange(nameof(ID));
}
}
private int year;
public int Year
{
get
{
return year;
}
set
{
year = value;
this.RaisePropertyChange(nameof(ID));
}
}
private int quarter;
public int Quarter
{
get
{
return quarter;
}
set
{
quarter = value;
this.RaisePropertyChange(nameof(ID));
}
}
private string name;
public string Name
{
get
{
return name;
}
set
{
name = value;
this.RaisePropertyChange(nameof(ID));
}
}
private int safetyScore;
public int SafetyScore
{
get
{
return safetyScore;
}
set
{
safetyScore = value;
this.RaisePropertyChange(nameof(ID));
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChange(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Upvotes: 3