change data grid based on combobox selection WPF - wpf

I am trying to populate a datagrid based on the selection from a combobox, I am new to wpf so I am not sure if my issue is the the databinding or what. Here is my VM:
namespace WasteTracker
{
public class MenuItemViewModel : INotifyPropertyChanged
{
DataSource data = new DataSource();
private ObservableCollection<StationList> stationlistcb = new
ObservableCollection<StationList>();
private ObservableCollection<MenuItem> menuitemdg = new
ObservableCollection<MenuItem>();
//methods to populate combobox of stations and datagrid of menu items
for that station
#region Load Stations and Menu Methods
public void LoadStationList()
{
stationlistcb = new ObservableCollection<StationList>
(data.Stations());
}
public void LoadMenuItems(int stationid)
{
menuitemdg = new ObservableCollection<MenuItem>
(from onemenuitem in data.MenuItems()
where onemenuitem.StationId.Equals(stationid)
select onemenuitem);
OnPropertyChanged("MenuItemData");
}
#endregion
public ObservableCollection<StationList> StationList
{
get
{
return stationlistcb;
}
}
public ObservableCollection<MenuItem> MenuItemList
{
get
{
return menuitemdg;
}
}
/// <summary>
/// Called when [property changed].
/// </summary>
/// <param name="PropertyName">Name of the property.</param>
protected void OnPropertyChanged(string PropertyName)
{
VerifyPropertyName(PropertyName);
if (PropertyChanged != null)
{
PropertyChanged(this, new
PropertyChangedEventArgs(PropertyName));
}
}
#region Propertychange Methods
/// <summary>
/// Warns the developer if this object does not have
/// a public property with the specified name. This
/// method does not exist in a Release build.
/// </summary>
[Conditional("DEBUG")]
[DebuggerStepThrough]
private void VerifyPropertyName(string propertyName)
{
// Verify that the property name matches a real,
// public, instance property on this object.
if (TypeDescriptor.GetProperties(this)[propertyName] == null)
{
string msg = "Invalid property name: " + propertyName;
throw new Exception(msg);
}
}
#endregion
public event PropertyChangedEventHandler PropertyChanged;
}
}
And here is the XAML for the page:
<TextBlock Grid.Row="0" Grid.ColumnSpan="3" FontSize="35"
HorizontalAlignment="Center" VerticalAlignment="Top" Text="Microsoft
SVMT Waste Tracker" Height="50" Width="480" Margin="44,0" />
<TextBlock Grid.Row="1" Grid.ColumnSpan="3" FontSize="30"
HorizontalAlignment="Center" VerticalAlignment="Top" Text="Waste
Entry" Height="50" Width="166" Margin="10,0,44,0" />
<TextBlock Grid.Row="2" Grid.Column="0" Text="Station" FontSize="30"
HorizontalAlignment="Center" VerticalAlignment="top" />
<TextBlock Grid.Row="2" Grid.Column="3" Text="Date" FontSize="30"
HorizontalAlignment="Center" VerticalAlignment="top" />
<DatePicker Grid.Row="3" Grid.Column="2" Grid.RowSpan="2" FontSize="30"
HorizontalAlignment="Center" VerticalAlignment="Center"
Width="290" Height="42" Margin="0,19,10,22" />
<ComboBox Name="StationBox" Grid.Row="3" Grid.Column="0"
Grid.RowSpan="2" FontSize="30" ItemsSource="{Binding StationList}"
DisplayMemberPath="StationName" HorizontalAlignment="Center"
VerticalAlignment="Center" Width="300" Height="42"
Margin="0,19,0,23"
SelectionChanged="Station_SelectionChanged" />
<DataGrid ItemsSource="{Binding MenuItemList}"
DisplayMemberPath="MenuItemText" AutoGenerateColumns="False"
Grid.Row="5" Grid.ColumnSpan="3" CanUserAddRows="True"
AlternatingRowBackground="AliceBlue"
IsSynchronizedWithCurrentItem="True" >
<DataGrid.Columns>
<DataGridTextColumn Header="Menu Item" Binding="{Binding
MenuItemText}" HeaderStyle="{StaticResource CenterGridHeader}"
CellStyle="{StaticResource CenterCellText}"
IsReadOnly="True" CanUserResize="False"
FontSize="36" Width="*"/>
<DataGridTextColumn Header="UoM" Binding="{Binding UoM}"
HeaderStyle="{StaticResource CenterGridHeader}" CellStyle="
{StaticResource CenterCellText}"
IsReadOnly="True" FontSize="36"
CanUserResize="False" Width="*" />
<DataGridTextColumn Header="Left Over" Binding="{Binding
UserInput}" HeaderStyle="{StaticResource CenterGridHeader}"
CellStyle="{StaticResource CenterCellText}"
FontSize="36" IsReadOnly="False"
CanUserResize="False" Width="*"/>
<DataGridTextColumn Header="UoM" Binding="{Binding UoM}"
HeaderStyle="{StaticResource CenterGridHeader}" CellStyle="
{StaticResource CenterCellText}"
IsReadOnly="True" FontSize="36"
CanUserResize="False" Width="*" />
<DataGridTextColumn Header="Par" Binding="{Binding Par}"
HeaderStyle="{StaticResource CenterGridHeader}" CellStyle="
{StaticResource CenterCellText}"
IsReadOnly="True" FontSize="36"
CanUserResize="False" Width="*"/>
<DataGridTextColumn Header="UoM" Binding="{Binding UoM}"
HeaderStyle="{StaticResource CenterGridHeader}" CellStyle="
{StaticResource CenterCellText}"
IsReadOnly="True" FontSize="36"
CanUserResize="False" Width="*" />
</DataGrid.Columns>
</DataGrid>
</Grid>
this is the xaml.cs:
public MenuItemViewModel _objViewModel = new MenuItemViewModel();
public EnterWaste()
{
InitializeComponent();
SourceContext();
InitialOperations();
}
private void Station_SelectionChanged(object sender,
SelectionChangedEventArgs e)
{
_objViewModel.LoadMenuItems((StationBox.SelectedItem as
MenuItem).StationId);
}
With this code currently the combobox is currently populated with the correct info, however when I make a selection from the combobox the datagrid will not display the correct info. Being new to wpf I have done a lot googling however the closest thing I can find in cascading comboboxes, and that solution really didn't help. Any suggestions would be appreciated.

Related

How to bind XamDataGrids available on different panels with different collections on a WPF page, using pure MVVM concepts & dependency injection.

I have a WPF XAML page, having 3 sections separated by DockPanels. One panel contains an INFRAGITICS XamDataGrid control to be bound with a collection.
I would like to bind XamDataGrid control using DataContext/DataSource property in pure MVVM way.
Also, would be delighted to understand if binding is done through dependency injection.
I have tried different approaches but didn't get success. I have pasted my code below for reference. Kindly help.
XAML Page:
<Window x:Class="UserInterface.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:UserInterface"
xmlns:igDP="clr-namespace:Infragistics.Windows.DataPresenter;assembly=InfragisticsWPF.DataPresenter"
xmlns:igEditors="clr-namespace:Infragistics.Windows.Editors;assembly=InfragisticsWPF.Editors"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:dc ="clr-namespace:UserInterface.ViewModel"
xmlns:diag="clr-namespace:System.Diagnostics;assembly=WindowsBase"
mc:Ignorable="d"
Title="MainWindow">
<Window.Resources>
<dc:GraphicViewModel x:Key="dataContext"/>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height=".5*"/>
<RowDefinition Height=".5*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width=".5*"/>
<ColumnDefinition Width=".5*"/>
</Grid.ColumnDefinitions>
<DockPanel Grid.Column="0" Grid.Row="0">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<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>
</Grid>
<!--<StackPanel Orientation="Vertical" DockPanel.Dock="Top">
<StackPanel Orientation="Horizontal" Margin="5" Grid.Row="0">
<DockPanel>
<TextBlock Text="*.cfg File" Grid.Column="0" DockPanel.Dock="Left"/>
<Button Content="Browse..." Grid.Column="2" DockPanel.Dock="Right"/>
<TextBox FontStyle="Italic" FontWeight="Light" Text="Browse *.cfg file..." Grid.Column="1" DockPanel.Dock="Right"/>
</DockPanel>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="5" Grid.Row="1">
<TextBlock Text="*.ps File " Grid.Column="0"/>
<TextBox FontStyle="Italic" FontWeight="Light" Text="Browse *.ps file..." Grid.Column="1"/>
<Button Content="Browse..." Grid.Column="2"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="5" Grid.Row="2">
<TextBlock Text="*.pic File " Grid.Column="0"/>
<TextBox FontStyle="Italic" FontWeight="Light" Text="Browse *.pic file..." Grid.Column="1"/>
<Button Content="Browse..." Grid.Column="2"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="5" Grid.Row="3">
<TextBlock Text="*.xlsx File" Grid.Column="0"/>
<TextBox FontStyle="Italic" FontWeight="Light" Text="Browse *.xlsx file..." Grid.Column="1"/>
<Button Content="Browse..." Grid.Column="2"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="5" Grid.Row="4">
<TextBlock Text="*.xlsx File"/>
<TextBox FontStyle="Italic" FontWeight="Light" Text="Browse *.xlsx file..." Grid.Column="1"/>
<Button Content="Browse..." Grid.Column="2"/>
</StackPanel>
</StackPanel>-->
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Vertical" Margin="5" Grid.Row="0">
<TextBlock MinHeight="20.5" Text="*.cfg File" Grid.Column="0"/>
<TextBlock MinHeight="20.5" Text="*.ps File " Grid.Column="0"/>
<TextBlock MinHeight="20.5" Text="*.pic File " Grid.Column="0"/>
<TextBlock MinHeight="20.5" Text="*.xlsx File" Grid.Column="0"/>
<TextBlock MinHeight="20.5" Text="*.xlsx File" Grid.Column="0"/>
</StackPanel>
<StackPanel Orientation="Vertical" Margin="5" Grid.Row="1">
<TextBox FontStyle="Italic" FontWeight="Light" Text="Browse *.cfg file..." Grid.Column="1" MinHeight="20.5"/>
<TextBox FontStyle="Italic" FontWeight="Light" Text="Browse *.ps file..." Grid.Column="1" MinHeight="20.5"/>
<TextBox FontStyle="Italic" FontWeight="Light" Text="Browse *.pic file..." Grid.Column="1" MinHeight="20.5"/>
<TextBox FontStyle="Italic" FontWeight="Light" Text="Browse Model mapping file..." Grid.Column="1" MinHeight="20.5"/>
<TextBox FontStyle="Italic" FontWeight="Light" Text="Browse Parameter mapping file..." Grid.Column="1" MinHeight="20.5"/>
</StackPanel>
<StackPanel Orientation="Vertical" Margin="5" Grid.Row="2">
<Button Content="Browse..." Grid.Column="2"/>
<Button Content="Browse..." Grid.Column="2"/>
<Button Content="Browse..." Grid.Column="2"/>
<Button Content="Browse..." Grid.Column="2"/>
<Button Content="Browse..." Grid.Column="2"/>
</StackPanel>
</StackPanel>
</DockPanel>
<DockPanel Grid.Column="1" Grid.Row="0">
<igDP:XamDataGrid x:Name="ItemsSource" DataContext="{Binding Source={StaticResource dataContext}, Path=ItemsSource, Mode=TwoWay}" Grid.Row="0" Margin="10" AutoFit="true">
<igDP:XamDataGrid.ViewSettings>
<igDP:GridViewSettings/>
</igDP:XamDataGrid.ViewSettings>
<igDP:XamDataGrid.FieldSettings>
<igDP:FieldSettings LabelTextAlignment="Left" AllowRecordFiltering="true" FilterOperandUIType="ExcelStyle" FilterStringComparisonType="CaseInsensitive" FilterOperatorDefaultValue="Contains"
LabelClickAction="SortByOneFieldOnlyTriState" SortComparisonType="Default"/>
</igDP:XamDataGrid.FieldSettings>
<igDP:XamDataGrid.FieldLayoutSettings>
<igDP:FieldLayoutSettings DataErrorDisplayMode="ErrorIconAndHighlight" SupportDataErrorInfo="RecordsAndCells" SelectionTypeRecord ="Single"
AutoGenerateFields="False" FilterUIType="FilterRecord"/>
</igDP:XamDataGrid.FieldLayoutSettings>
<igDP:XamDataGrid.FieldLayouts>
<igDP:FieldLayout>
<igDP:FieldLayout.Fields>
<igDP:Field Name="IsSelected" Label="Select" HorizontalContentAlignment="Left" Width="Auto" VerticalContentAlignment="Center">
<igDP:Field.Settings>
<igDP:FieldSettings DataItemUpdateTrigger="OnCellValueChange">
<igDP:FieldSettings.LabelPresenterStyle>
<Style TargetType="{x:Type igDP:LabelPresenter}">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<CheckBox Checked="CheckBox_Checked" Unchecked="CheckBox_Unchecked" Content="" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</igDP:FieldSettings.LabelPresenterStyle>
</igDP:FieldSettings>
</igDP:Field.Settings>
</igDP:Field>
<igDP:Field Label="Name" Name="Name" AllowEdit="False" HorizontalContentAlignment="Left" Width="Auto">
</igDP:Field>
<igDP:Field Label="Type" Name="Type" AllowEdit="False" HorizontalContentAlignment="Left" Width="*"/>
<igDP:Field Label="Background" Name="Background" AllowEdit="False" HorizontalContentAlignment="Left" Width="Auto"/>
<igDP:Field Label="Width" Name="Width" AllowEdit="False" HorizontalContentAlignment="Right" Width="Auto"/>
<igDP:Field Label="Height" Name="Height" AllowEdit="False" HorizontalContentAlignment="Right" Width="Auto"/>
</igDP:FieldLayout.Fields>
</igDP:FieldLayout>
</igDP:XamDataGrid.FieldLayouts>
</igDP:XamDataGrid>
</DockPanel>
<DockPanel Grid.Column="0" Grid.Row="1" Grid.RowSpan="2">
<StackPanel Orientation="Vertical" DockPanel.Dock="Bottom">
<TextBox Text="Sample Text1"/>
<TextBox Text="Sample Text2"/>
<TextBox Text="Sample Text3"/>
<TextBox Text="Sample Text4"/>
</StackPanel>
</DockPanel>
<!--</StackPanel>-->
</Grid>
</Window>
Xaml page code behind:
namespace UserInterface
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
//GraphicViewModel obj = new GraphicViewModel();
//ItemsSource.DataSource = obj.ItemsSource;
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#endregion INotifyPropertyChanged Members
public GraphicViewModel GraphicViewModel
{
get { return this.DataContext as GraphicViewModel; }
set
{
this.DataContext = value;
if (this.DataContext != null)
NotifyPropertyChanged("GraphicViewModel");
}
}
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
}
private void CheckBox_Unchecked(object sender, RoutedEventArgs e)
{
}
}
}
Model Class:
namespace UserInterface.Model
{
public class GraphicsModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyOfPropertyChange(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
private bool _isSelected;
public bool IsSelected
{
get { return _isSelected; }
set
{
if (_isSelected == value) return;
_isSelected = value;
NotifyOfPropertyChange("IsSelected");
}
}
private string _name = string.Empty;
public string Name
{
get { return _name; }
set
{
if (_name != value)
_name = value;
NotifyOfPropertyChange("Name");
}
}
private string _type = string.Empty;
public string Type
{
get { return _type; }
set
{
if (_type != value)
_type = value;
NotifyOfPropertyChange("Type");
}
}
private string _width = string.Empty;
public string Width
{
get { return _width; }
set
{
if (_width != value)
_width = value;
NotifyOfPropertyChange("Width");
}
}
private string _height = string.Empty;
public string Height
{
get { return _height; }
set
{
if (_height != value)
_height = value;
NotifyOfPropertyChange("Height");
}
}
}
}
ViewModel class:
namespace UserInterface.ViewModel
{
public class GraphicViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void NotifyOfPropertyChange(string property)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
private ObservableCollection<GraphicsModel> _itemsSource = new ObservableCollection<GraphicsModel>();
// MainWindow _view = null;
public ObservableCollection<GraphicsModel> ItemsSource
{
get { return _itemsSource; }
set
{
if (_itemsSource == value) return;
_itemsSource = value;
NotifyOfPropertyChange("ItemsSource");
}
}
public GraphicViewModel()
{
//_view = view;
_itemsSource = new ObservableCollection<GraphicsModel>() { new GraphicsModel() { Name = "sdasdad", Type = "Model", IsSelected = false, Height = "1000", Width = "1370" } ,
new GraphicsModel() { Name = "sdsa", Type = "Model", IsSelected = false, Height = "1000", Width = "1370" } ,new GraphicsModel() { Name = "sdasdad", Type = "Model", IsSelected = false, Height = "1000", Width = "1370" } ,new GraphicsModel() { Name = "asas", Type = "Model", IsSelected = false, Height = "1000", Width = "1370" } ,new GraphicsModel() { Name = "rewwe", Type = "Model", IsSelected = false, Height = "1000", Width = "1370" } ,};
//view.GraphicViewModel = this;
}
}
}
When you write
<Window.Resources>
<dc:GraphicViewModel x:Key="dataContext"/>
</Window.Resources>
You are saying "create a property of type GraphicViewModel and name it dataContext". It's almost the exact same as doing something like this in the code-behind :
private GraphicViewModel dataContext = new GraphicViewModel();
When you write
<igDP:XamDataGrid DataContext="{Binding Source={StaticResource dataContext}, Path=ItemsSource, Mode=TwoWay}" .. />
You are saying "bind the igDP:XamDataGrid.DataContext property to dataContext.ItemsSource".
Now for why the Grid won't load, the .DataContext property doesn't actually do anything. It's just a holder for the data that sits behind the control. So if you were to write
<igDP:XamDataGrid ItemsSource="{Binding MyProperty}" />
It would be to tell WPF "set XamDataGrid.ItemsSource property equal to XamDataGrid.DataContext.MyProperty".
Since in the code above you set the DataContext equal to dataContext.ItemsSource, it would try to set the value to dataContext.ItemsSource.MyProperty (which of course does not exist).
What you actually need is something like
<igDP:XamDataGrid ItemsSource="{Binding Source={StaticResource dataContext}, Path=ItemsSource}" .. />
This binds the .ItemsSource property of the grid (which is the property it builds its data rows from) to the static resource created in <Window.Resources> named "dataContext", and the property on that object called ItemsSource.
Second issue here though is you seem to have multiple copies of your ViewModel in your code. Personally I recommend never creating objects in the XAML like you did for your GraphicViewModel. Instead, I would recommend something like this :
public MainWindow()
{
InitializeComponent();
this.DataContext = new GraphicViewModel();
}
Now it should be noted that this creates a new instance of the GraphicViewModel, but doesn't store it anywhere. If you want to access this object in other code-behind without casting this.DataContext to GraphicViewModel, then you should probably store the value somewhere.
Once the Window.DataContext is set, you can write the bindings without specifying a custom Source property for the binding.
<igDP:XamDataGrid ItemsSource="{Binding ItemsSource}" .. />
This will work because WPF controls will look up the DataContext from their parent by default if it is not specifically set. So in this case, the .DataContext property for XamDataGrid is null so it will travel up the visual tree looking for something with a DataContext until it runs into Window.DataContext which you've set in the constructor to a new instance of your GraphicViewModel.
For people looking to understand the DataContext property, I usually send them to this SO answer to avoid having to retype the same thing all the time. I'd recommend reading it if you're still working to understand what the DataContext is or how it's used.
Also that said, Dependency Injection is something different and wouldn't be used here at all.
Thanks a lot #Rachel, #Daniel Filipov & #quetzalcoatl, I got my code working now. The changes done in below files:
MainWindow.xaml.cs
public MainWindow()
{
InitializeComponent();
ItemsSource.DataContext= new GraphicViewModel();
}
MainWindow.xaml:
<igDP:XamDataGrid x:Name="ItemsSource" DataSource="{Binding ItemsSource, Mode=TwoWay}" .../>

WPF MVVM DataGrid RowDetails with different ItemsSource

I have three DataGrids set up as individual UserControls with each ItemsSource bound to a different DependencyProperty in my ViewModel.
When a row is selected in the first DataGrid, the other two populate with information relevant to the selected row. Though this works fine, I want the second and third DataGrid to be displayed in the RowDetailsTemplate of the first DataGrid.
The issue I am having is that the ItemsSource of the parent DataGrid is overriding that of the other two, so they are not populating. I have tried solutions posted on many other similar questions but none have solved my problem. My code is below, I'm hoping that I've just missed something obvious but any help would be appreciated!
Main Datagrid
<DataGrid x:Name="PostDataGrid"
ItemsSource="{Binding WSEDriverList}"
SelectedItem="{Binding SelectedWSEDriverPOST}"
Style="{DynamicResource MainDataGridStyle}"
Margin="0,0,0,0"
Grid.Row="1" >
<DataGrid.Columns>
<DataGridTextColumn Header="HesId" Binding="{Binding HESID, Mode=OneWay}" Width="50"/>
<DataGridTextColumn Header="WseId" Binding="{Binding WSEID, Mode=OneWay}" Width="50"/>
<DataGridTextColumn Header="API" Binding="{Binding API, Mode=OneWay}" Width="198" />
<DataGridTextColumn Header="Request Date" Binding="{Binding Path=REQUEST_DATE, Mode=OneWay, StringFormat=dd/MM/yyyy HH:mm:ss}" Width="125"/>
<DataGridTextColumn Header="Result Date" Binding="{Binding Path=RESULT_DATE, Mode=OneWay, StringFormat=dd/MM/yyyy HH:mm:ss}" Width="125"/>
<DataGridTextColumn Header="Return Code" Binding="{Binding RETURN_CODE, Mode=OneWay, StringFormat=0}" Width="80" />
<DataGridTextColumn Header="Status" Binding="{Binding STATUS, Mode=OneWay, StringFormat=0}" Width="70" />
</DataGrid.Columns>
<DataGrid.RowDetailsTemplate>
<DataTemplate DataType="{x:Type viewmodel:WSEAuditViewModel}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Expander Header="NOTIFICATION" Grid.Row="0">
<ItemsControl ItemsSource="{Binding WSEDriverGETResult}">
<usercontrol:NOTIFICATIONUserControl/>
</ItemsControl>
</Expander>
<Expander Header="GET" Grid.Row="1">
<ItemsControl ItemsSource="{Binding WSEDriverGETResult}">
<usercontrol:GETUserControl/>
</ItemsControl>
</Expander>
</Grid>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
DataGrid 2 (NOTIFICATIONUserControl)
<DataGrid x:Name="NotificationDG"
ItemsSource="{Binding NotificationResult}"
DataGrid 3 (GETUserControl)
<DataGrid x:Name="GetDG"
ItemsSource="{Binding WSEDriverGETResult}"
Edit
Here is the code for my DependencyProperties bound to my DataGrid.
//----POST result list from WSE_DRIVER-----
public List<WSE_DRIVER> WSEDriverList
{
get { return (List<WSE_DRIVER>)GetValue(WSEDriverListProperty); }
set { SetValue(WSEDriverListProperty, value); }
}
public static readonly DependencyProperty WSEDriverListProperty =
DependencyProperty.Register("WSEDriverList",
typeof(List<WSE_DRIVER>),
typeof(WSEAuditViewModel),
new PropertyMetadata(WSEDriverListChanged_Callback));
private static void WSEDriverListChanged_Callback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
WSEAuditViewModel wse_win = (WSEAuditViewModel)d;
if (wse_win.WSEDriverList.Count > 0)
{
wse_win.SelectedWSEDriverPOST = wse_win.WSEDriverList.First();
}
}
//----Selected POST WSE_Driver----
public WSE_DRIVER SelectedWSEDriverPOST
{
get { return (WSE_DRIVER)GetValue(SelectedWSEDriverPOSTProperty); }
set { SetValue(SelectedWSEDriverPOSTProperty, value); }
}
public static readonly DependencyProperty SelectedWSEDriverPOSTProperty =
DependencyProperty.Register("SelectedWSEDriverPOST",
typeof(WSE_DRIVER),
typeof(WSEAuditViewModel),
new PropertyMetadata(SelectedWSEDriverPOSTChanged_Callback));
private static void SelectedWSEDriverPOSTChanged_Callback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
//Gets list and assigns to WSEDriverGETResult and NotificationResult
}
//----GET result from WSE_Driver----
public List<WSE_DRIVER> WSEDriverGETResult
{
get { return (List<WSE_DRIVER>)GetValue(WSEDriverGETResultProperty); }
set { SetValue(WSEDriverGETResultProperty, value); }
}
public static readonly DependencyProperty WSEDriverGETResultProperty =
DependencyProperty.Register("WSEDriverGETResult",
typeof(List<WSE_DRIVER>),
typeof(WSEAuditViewModel),
new PropertyMetadata(WSEDriverGETResultChanged_Callback));
private static void WSEDriverGETResultChanged_Callback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
//----Notification Info----
public List<SPEC_NOTIFICATION> NotificationResult
{
get { return (List<SPEC_NOTIFICATION>)GetValue(NotificationResultProperty); }
set { SetValue(NotificationResultProperty, value); }
}
public static readonly DependencyProperty NotificationResultProperty =
DependencyProperty.Register("NotificationResult",
typeof(List<SPEC_NOTIFICATION>),
typeof(WSEAuditViewModel),
new UIPropertyMetadata(NotificationResultChanged_Callback));
private static void NotificationResultChanged_Callback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}
Since the NotificationResult and WSEDriverGETResult properties belong to the same class as the WSEDriverList property, you should be able to use a {RelativeSource} to bind to the DataContext of the parent DataGrid:
<DataGrid x:Name="NotificationDG" ItemsSource="{Binding DataContext.NotificationResult,
RelativeSource={RelativeSource AncestorType=DataGrid}}"></DataGrid>
<DataGrid x:Name="GetDG" ItemsSource="{Binding DataContext.WSEDriverGETResult,
RelativeSource={RelativeSource AncestorType=DataGrid}}"></DataGrid>

One combo box filtering another combo box in a WPF Datagrid

I have a datagrid which has two combo box columns in it. The first combo box is a list of PersonnelTypes. Depending on which PersonnelType is selected, the second combo box should fill up with a list of Resources that match the selected PersonnelType
The problem is, lets say I have two rows of data, if I change the PersonnelType of one row, the datagrid will set the itemsource for all of the Resources in every row. I only want it to filter the row that I am in, not all the rows.
Here's the xaml for the part of the datagrid that has the combo boxes:
<DataGridTemplateColumn Header="Personnel Type" Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<ComboBox Name="cmbPersonnelTypes" FontWeight="Bold" ItemsSource="{Binding ViewModel.PersonnelTypes, RelativeSource={RelativeSource AncestorType=Window}}" SelectedItem="{Binding PersonnelType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="ID" DisplayMemberPath="Description" SelectionChanged="cmbPersonnelTypes_SelectionChanged" />
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Name" Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<ComboBox Name="cmbPersonnelName" FontWeight="Bold" ItemsSource="{Binding ViewModel.ResourcesToChooseFrom, RelativeSource={RelativeSource AncestorType=Window},UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding Resource, Mode=TwoWay}" SelectedValuePath="Refno" DisplayMemberPath="Name" />
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Here is the xaml for the whole data grid (just in case you need to see it):
<DataGrid AutoGenerateColumns="False" CanUserSortColumns="False" CanUserDeleteRows="True" IsReadOnly="True" Background="LightGray" CanUserAddRows="False" Margin="5" SelectedItem="{Binding SelectedLA_JobPersonnel}" ItemsSource="{Binding LA_Personnel}" Grid.ColumnSpan="4" MouseDoubleClick="DataGrid_MouseDoubleClick_1">
<DataGrid.Resources>
<ViewModels:BindingProxy x:Key="proxy" Data="{Binding}" />
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Personnel Type" Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<ComboBox Name="cmbPersonnelTypes" FontWeight="Bold" ItemsSource="{Binding ViewModel.PersonnelTypes, RelativeSource={RelativeSource AncestorType=Window}}" SelectedItem="{Binding PersonnelType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="ID" DisplayMemberPath="Description" SelectionChanged="cmbPersonnelTypes_SelectionChanged" />
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Name" Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<ComboBox Name="cmbPersonnelName" FontWeight="Bold" ItemsSource="{Binding ViewModel.ResourcesToChooseFrom, RelativeSource={RelativeSource AncestorType=Window},UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding Resource, Mode=TwoWay}" SelectedValuePath="Refno" DisplayMemberPath="Name" />
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn> <DataGridTemplateColumn Header="Date Out" Width="20*" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Background="LightGray" FontWeight="Bold" Text="{Binding DateOut, Mode=TwoWay, Converter={StaticResource thisNullDateConverter}, StringFormat={}{0:MMM-dd-yyyy hh:ss tt}}">
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<Toolkit:DateTimePicker Background="LightGray" FontWeight="Bold" Value="{Binding Path=DateOut, Mode=TwoWay, Converter={StaticResource thisNullDateConverter}}" Format="Custom" FormatString="MMM dd yyyy hh:ss tt"></Toolkit:DateTimePicker>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Date In" Width="20*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Background="LightGray" FontWeight="Bold" Text="{Binding DateIn, Mode=TwoWay, Converter={StaticResource thisNullDateConverter}, StringFormat={}{0:MMM-dd-yyyy hh:ss tt}}">
</TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<Toolkit:DateTimePicker Background="LightGray" FontWeight="Bold" Value="{Binding Path=DateIn, Mode=TwoWay, Converter={StaticResource thisNullDateConverter}}" Format="Custom" FormatString="MMM dd yyyy hh:ss tt"></Toolkit:DateTimePicker>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Here is the code behind for the xaml (xaml.cs):
public JobEditorViewModel ViewModel
{
get { return viewModel; }
}
private void cmbPersonnelTypes_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var combobox = sender as ComboBox;
if (combobox != null)
{
var selectedPersonnelType = combobox.SelectedItem as PersonnelType;
viewModel.SetResourcesToChooseFrom(selectedPersonnelType);
}
}
Here is the code in the viewModel:
public BindingList<PersonnelType> PersonnelTypes
{
get; set;
}
public JobEditorViewModel(int jobid, string region, DataAccessDataContext db, ServiceUserControlViewModel serviceViewModel)
{
PersonnelTypes = new BindingList<PersonnelType>(_db.PersonnelTypes.OrderBy(p => p.Head).ThenBy(p => p.Description).ToList());
}
private BindingList<Resource> _resourcesToChooseFrom;
public BindingList<Resource> ResourcesToChooseFrom
{
get { return _resourcesToChooseFrom; }
set
{
_resourcesToChooseFrom = value;
NotifyPropertyChanged("ResourcesToChooseFrom");
}
}
public void SetResourcesToChooseFrom(PersonnelType personnelType)
{
ResourcesToChooseFrom =
new BindingList<Resource>(_db.Resources.Where(r => r.Head == personnelType.Head && r.Refno > 2).OrderBy(r=>r.Name).ToList());
}
If you need to see more, let me know
Well, with some help from a colleague here at work, we figured out what I needed to do. Multibinding is the answer. First off we kind of hacked around so that the two combo boxes could be in the same column by placing them both in a grid and placing the grid in the one column. So now both combo boxes can see each other because they are in the same DataGridTemplateColumn. Before, we couldn't get them to see each other because they lost scope of each other in being two separate DataGridTemplateColumns.
Here's what we did in the xaml:
<DataGridTemplateColumn Header="Personnel Type-Name" Width="Auto" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="170"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" BorderBrush="Black" BorderThickness="1">
<TextBlock Text="{Binding PersonnelType.Description}"/>
</Border>
<Border Grid.Column="1" BorderBrush="Black" BorderThickness="1">
<TextBlock Text="{Binding Resource.Name}"/>
</Border>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="170"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<ComboBox Name="cmbPersonnelTypes" Grid.Column="0" FontWeight="Bold" ItemsSource="{Binding ViewModel.PersonnelTypes, RelativeSource={RelativeSource AncestorType=Window}, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding PersonnelType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="ID" DisplayMemberPath="Description" />
<ComboBox Name="cmbPersonnelName" Grid.Column="1" FontWeight="Bold" SelectedItem="{Binding Resource, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="Refno" DisplayMemberPath="Name" >
<ComboBox.ItemsSource>
<MultiBinding Converter="{StaticResource FilteredPersonnelConverter}">
<Binding Path="ViewModel.AvailablePersonnel" RelativeSource="{RelativeSource AncestorType=Window}"/>
<Binding Path="SelectedItem" ElementName="cmbPersonnelTypes"/>
<Binding Path="ViewModel.SelectedGlobalResourceViewOption" RelativeSource="{RelativeSource AncestorType=Window}"/>
</MultiBinding>
</ComboBox.ItemsSource>
</ComboBox>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
You'll notice there is a ValueConverter in the MultiBinding called FilteredPersonnelConverter. This value converter does all the filtering for me. Here's the code for that:
public class FilteredPersonnelListValueConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
var allResources = values[0] as IList<Resource>;
var personnelType = values[1] as PersonnelType;
var selectedGlobalResourceView = values[2] as ResourceViewOption;
if (personnelType == null)
return allResources;
if(selectedGlobalResourceView.ResourceViewTitle=="Regional")
return allResources.Where(r => r.Head == personnelType.Head && r.Obsolete == false && r.Location.Region.RegionID.Trim()==SettingsManager.OpsMgrSettings.Region.Trim()).OrderBy(r => r.Name).ToList();
if (selectedGlobalResourceView.ResourceViewTitle == "Local")
return allResources.Where(r => r.Head == personnelType.Head && r.Obsolete == false && r.LocnID.Trim() == SettingsManager.OpsMgrSettings.LOCNCODE.Trim()).OrderBy(r => r.Name).ToList();
return allResources.Where(r => r.Head == personnelType.Head &&r.Obsolete==false).OrderBy(r => r.Name).ToList();
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
So if anyone else is doing something like this, look into Multibinding, it will change your life
When the user changes the PersonelType dropdown in the view, the ViewModel should then filter the list of Resources (which should update the second dropdown box with the correct information).
Really your view model just needs to be set up to respond to changes on the view. That's what it boils down to.
Looking here:
public JobEditorViewModel(int jobid, string region, DataAccessDataContext db, ServiceUserControlViewModel serviceViewModel)
{
PersonnelTypes = new BindingList<PersonnelType>(_db.PersonnelTypes.OrderBy(p => p.Head).ThenBy(p => p.Description).ToList());
}
it looks like you set the personel types in the constructor, but you aren't responding to user changes. You need to do that in the PersonelType Binding in your view model.
EDIT
I see now that you're handling the selection changed. But really I think this should be done in the view model itself. Specifically because you actually access the viewmodel from the view to do make your changes.
Example
So here's my VM:
class ViewModel : INotifyPropertyChanged
{
DispatcherTimer timer = new DispatcherTimer();
public ViewModel()
{
// Create my observable collection
this.DateTimes = new ObservableCollection<DateTime>();
// Every second add anothe ritem
timer.Interval = TimeSpan.FromSeconds(1);
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
// This adds to the collection
this.DateTimes.Add(DateTime.Now);
}
public ObservableCollection<DateTime> DateTimes { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;
}
and My View:
<ListBox ItemsSource="{Binding DateTimes}"/>
Notice that I don't rebuild the collection. I just set it once, and then change it's size as neccesary.

WPF DataGrid updating items with .Refresh

I am using a datagridview to show my own items in it as List(Of MyStuff) and it works quiete well. When my class is changing the content the DataGrid does not change. I need to do DataGrid1.Items.Refresh and the items becomes updates.
But now my list contains 700 items and the .Refresh methode takes about 15 seconds just because one item changed. I've searched for any "UpdateContent" or "RowUpdate" or "RefreshRow" but found nothing. How is the right way to update single Rows?
Private LCommand As New List(Of MyLogEntry)
Me.dgv.DataContext = Me.LCommand
Me._LastUpdate = DateTime.MinValue
Me._CurrentIndex = -1
Call UpdateDgv()
Private Sub UpdateDgv()
Application.Current.Dispatcher.Invoke(AsyncRefresh)
End Sub
Private Sub RefreshDgvAsync()
Try
Me.dgv.SelectedIndex = Me._CurrentIndex
Me.dgv.ScrollIntoView(Me.dgv.SelectedItem)
Catch ex As Exception
End Try
If DateTime.Now.Subtract(Me._LastUpdate).TotalSeconds > 60 Then Me.dgv.Items.Refresh()
Me._LastUpdate = DateTime.Now
End Sub
<DataGrid x:Name="dgv" Grid.Row="1" RowHeight="20" AutoGenerateColumns="False" Grid.Column="1" ItemsSource="{Binding}" CanUserAddRows="False" VerticalScrollBarVisibility="Auto" ClipToBounds="True" GridLinesVisibility="Horizontal">
<DataGrid.Columns>
<DataGridTextColumn Header="N°" Binding="{Binding Index}" Width="SizeToCells" MinWidth="30" IsReadOnly="True" />
<!--<DataGridCheckBoxColumn Header="OK" Binding="{Binding Successfully}" Width="SizeToCells" MinWidth="35" IsReadOnly="True" Visibility="Collapsed" />-->
<DataGridTemplateColumn Header="State" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image x:Name="imgState" Source="{Binding StateImageUrl}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Logentry" Binding="{Binding Title}" Width="SizeToHeader" MinWidth="80" IsReadOnly="True" FontWeight="Bold" />
<DataGridTextColumn Header="Action" Binding="{Binding Description}" Width="SizeToCells" MinWidth="80" IsReadOnly="True" FontSize="12" />
<DataGridCheckBoxColumn Header="Available" Binding="{Binding Necessary}" Width="Auto" MinWidth="35" IsReadOnly="True" />
<DataGridTextColumn Header="Log" Binding="{Binding Log}" Width="*" MinWidth="50" IsReadOnly="False" FontSize="8" Foreground="Gray" />
</DataGrid.Columns>
</DataGrid>
It is not very clear for me why do you need to refresh your items at all. If you need to observe log entries collection changes then use ObservableCollection<LogEntry>, if you need to update cells when something in LogEntry changes then make it observable by implementing INotifyPropertyChanged.
When you call Refresh on underlying CollectionView all item containers are regenerated. And it takes a lot of time espacially when virtuallization is disabled.
Implement INotifyPropertyChanged at your collection.
Instead of having a List use ObserverableCollection<type>.
At any setter of MyLogEntry properties invoke the PropertyChangedEventHandler.
C# code (no testing):
public class MyLogEntry : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;
private object _myProperty;
public object MyProperty{
get { return _myProperty; }
set {
_myProperty = value;
OnPropertyChanged("MyProperty");
}
}
protected void OnPropertyChanged(string name){
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null){
handler(this, new PropertyChangedEventArgs(name));
}
}
}

refresh datagrid on view from viewmodel

I have a datagrid on a view that is bound to a viewmodel. When I initialze the view, the datagrid is populated with data from the viewmodel (ObservableCollection) as it should. However, with I try to do a search against the data, the datagrid does not get refreshed from the viewmodel. When I breakpoint the code in my viewmodel, I can see the results in the ObservableCollection has changed per my search, but somehow that is not getting communicated back to the view. Here is my view and viewmodel (BTW, I am using VS2010 RTM):
namespace Attendance.ViewModels
{
public class EmployeeSelectViewModel : ViewModel, INotifyPropertyChanged
{
#region Entity list and constructor
public EmployeeSelectViewModel()
{
{
Initialize();
}
}
private void Initialize()
{
if (employeeRpository == null)
employeeRpository = new EmployeeRepository();
ListOfEmployees = new ObservableCollection<EmployeeDto>(employeeRpository.GetEmployees(true));
}
private EmployeeRepository employeeRpository;
private ObservableCollection<EmployeeDto> listOfEmployees;
public ObservableCollection<EmployeeDto> ListOfEmployees
{
get { return listOfEmployees; }
set
{
if (listOfEmployees != value)
{
listOfEmployees = value;
NotifyPropertyChanged("ListOfEmployee");
}
}
}
private EmployeeDto selectedEmployee;
public EmployeeDto SelectedEmployee
{
get { return selectedEmployee; }
set
{
if (selectedEmployee != value)
{
selectedEmployee = value;
NotifyPropertyChanged("SelectedEmployee");
}
}
}
#endregion
#region UI control references
/// <summary>
/// search text property
/// </summary>
private string searchText;
public string SearchText
{
get { return searchText; }
set
{
if (searchText != value)
{
searchText = value;
NotifyPropertyChanged("SearchText");
}
}
}
public string Location { get; set; }
#endregion
#region Relay Commands
/// <summary>
/// new command
/// </summary>
private ViewCommand newCommand;
public ViewCommand NewCommand
{
get
{
if (newCommand == null)
newCommand = new ViewCommand(param => this.NewEmployee());
return newCommand;
}
}
private void NewEmployee()
{
NavigationActions.NewEmployeeView();
}
/// <summary>
/// edit command
/// </summary>
private ViewCommand editCommand;
public ViewCommand EditCommand
{
get
{
if (editCommand == null)
{
editCommand = new ViewCommand(param => this.EditEmployee());
}
return editCommand;
}
}
private void EditEmployee()
{
NavigationActions.OpenEmployeeView(SelectedEmployee);
}
/// <summary>
/// save command
/// </summary>
private ViewCommand saveCommand;
public ViewCommand SaveCommand
{
get
{
if (saveCommand == null)
{
saveCommand = new ViewCommand(
param => this.SaveEmployee(),
param => this.CanSaveEmployee
);
}
return saveCommand;
}
}
public void SaveEmployee()
{
employeeRpository.SaveChanges();
}
private bool CanSaveEmployee
{
get { return true; }
}
/// <summary>
/// clear search command
/// </summary>
private ViewCommand clearSearchCommand;
public ViewCommand ClearSearchCommand
{
get
{
if (clearSearchCommand == null)
clearSearchCommand = new ViewCommand(param => this.ClearSearch());
return clearSearchCommand;
}
}
private void ClearSearch()
{
this.SearchText = string.Empty;
ListOfEmployees = new ObservableCollection<EmployeeDto>(employeeRpository.GetEmployees(true));
}
/// <summary>
/// search command
/// </summary>
private ViewCommand searchCommand;
public ViewCommand SearchCommand
{
get
{
if (searchCommand == null)
searchCommand = new ViewCommand(param => this.SearchEmployee());
return searchCommand;
}
}
private void SearchEmployee()
{
if (this.SearchText == string.Empty || this.SearchText == null)
{
NavigationActions.ShowError("Search Employees.", "Please enter your search text ...");
return;
}
ListOfEmployees = new ObservableCollection<EmployeeDto>(employeeRpository.GetEmployeesByQuery(SearchText, Location));
}
/// <summary>
/// exit command
/// </summary>
private ViewCommand exitCommand;
public ViewCommand ExitCommand
{
get
{
if (exitCommand == null)
{
exitCommand = new ViewCommand(param => this.ExitWindow());
}
return exitCommand;
}
}
private void ExitWindow()
{
NavigationActions.CloseCurrentView();
}
#endregion
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#endregion
}
}
<Window x:Class="Attendance.Views.EmployeeSelectView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="clr-namespace:Attendance.ViewModels"
Title="Employee Maintenance" FocusManager.FocusedElement="{Binding ElementName=txtSearchCriteria}"
Height="525" Width="800" WindowStartupLocation="CenterScreen" WindowState="Normal"
WindowStyle="SingleBorderWindow" Icon="Images/gb_icon.png">
<Window.DataContext>
<vm:EmployeeSelectViewModel />
</Window.DataContext>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/DataGrid.Generic.xaml" />
</ResourceDictionary.MergedDictionaries>
<!--xml data start-->
<XmlDataProvider x:Key="LocationData" XPath="LocationList/LocationItem" Source="XMLData/Location.xml"/>
<!--xml data end-->
</ResourceDictionary>
</Window.Resources>
<Grid Width="775">
<DockPanel HorizontalAlignment="Left" Width="770">
<!-- TOOLBAR -->
<DockPanel DockPanel.Dock="Top" MinHeight="30" Margin="5">
<ToolBar FontWeight="Bold">
<!-- NEW -->
<Button Name="btnNew" Command="{Binding Path=NewCommand}">
<Button.ToolTip>
<StackPanel>
<Label FontWeight="Bold" Background="SteelBlue" Foreground="White">
Create a new Customer
</Label>
<TextBlock Padding="10" TextWrapping="WrapWithOverflow" Width="200">
Create a new customer in a new Window tab.
</TextBlock>
<Line Stroke="SteelBlue" StrokeThickness="1" X2="200" />
<StackPanel Orientation="Horizontal">
<Image Margin="2" Source="Images/new.png"/>
<Label>Press F1 for more help</Label>
</StackPanel>
</StackPanel>
</Button.ToolTip>
<StackPanel Orientation="Horizontal">
<Image Source="Images/new.png" Width="22" Height="22" Margin="2"/>
<Label VerticalAlignment="Center">_New</Label>
</StackPanel>
</Button>
<!-- EDIT -->
<Button Name="btnEdit" Command="{Binding Path=EditCommand}">
<Button.ToolTip>
<StackPanel>
<Label FontWeight="Bold" Background="SteelBlue" Foreground="White">
Edit the current record
</Label>
<TextBlock Padding="10" TextWrapping="WrapWithOverflow" Width="200">
Edit the current selected Customer.
</TextBlock>
<Line Stroke="SteelBlue" StrokeThickness="1" X2="200" />
<StackPanel Orientation="Horizontal">
<Image Margin="2" Source="Images/dialog-information.png"/>
<Label>Press F1 for more help</Label>
</StackPanel>
</StackPanel>
</Button.ToolTip>
<StackPanel Orientation="Horizontal">
<Image Source="Images/edit.png" Width="22" Height="22" Margin="2" />
<Label VerticalAlignment="Center">_Edit</Label>
</StackPanel>
</Button>
<!-- SEARCH -->
<Separator />
<TextBox Name="txtSearchCriteria"
MinWidth="300" Margin="5"
BorderThickness="1" BorderBrush="LightGray"
FontWeight="Normal" Foreground="Gray" Text="{Binding Path=SearchText}">
</TextBox>
<Button Name="btnSearch" Command="{Binding Path=SearchCommand}">
<Button.ToolTip>
<StackPanel>
<Label FontWeight="Bold" Background="SteelBlue" Foreground="White">
Search
</Label>
<TextBlock Padding="10" TextWrapping="WrapWithOverflow" Width="200">
Search a specific Customer.
</TextBlock>
<Line Stroke="SteelBlue" StrokeThickness="1" X2="200" />
<StackPanel Orientation="Horizontal">
<Image Margin="2" Source="Images/find.png"/>
<Label>Press F1 for more help</Label>
</StackPanel>
</StackPanel>
</Button.ToolTip>
<StackPanel Orientation="Horizontal">
<Image Source="Images/find.png" Width="22" Height="22" Margin="2" />
<Label VerticalAlignment="Center">_Find</Label>
</StackPanel>
</Button>
<Button Name="btnClearSearch" Command="{Binding Path=ClearSearchCommand}">
<Button.ToolTip>
<StackPanel>
<Label FontWeight="Bold" Background="SteelBlue" Foreground="White">
Search
</Label>
<TextBlock Padding="10" TextWrapping="WrapWithOverflow" Width="200">
Clear search results.
</TextBlock>
<Line Stroke="SteelBlue" StrokeThickness="1" X2="200" />
<StackPanel Orientation="Horizontal">
<Image Margin="2" Source="Images/find.png"/>
<Label>Press F1 for more help</Label>
</StackPanel>
</StackPanel>
</Button.ToolTip>
<StackPanel Orientation="Horizontal">
<Label VerticalAlignment="Center">_Clear Search</Label>
</StackPanel>
</Button>
<!-- EXIT -->
<Separator />
<Button Name="btnExit" Command="{Binding Path=ExitCommand}">
<Button.ToolTip>
<StackPanel>
<Label FontWeight="Bold" Background="SteelBlue" Foreground="White">
Start the application
</Label>
<TextBlock Padding="10" TextWrapping="WrapWithOverflow" Width="200">
Start the main application with the M-V-MV pattern.
</TextBlock>
<Line Stroke="SteelBlue" StrokeThickness="1" X2="200" />
<StackPanel Orientation="Horizontal">
<Image Margin="2" Source="Images/dialog-information.png"/>
<Label>Press F1 for more help</Label>
</StackPanel>
</StackPanel>
</Button.ToolTip>
<StackPanel Orientation="Horizontal">
<Image Source="Images/exit.png" Width="22" Height="22" Margin="2" />
<Label VerticalAlignment="Center">_Exit</Label>
</StackPanel>
</Button>
</ToolBar>
</DockPanel>
<!-- LIST -->
<DockPanel DockPanel.Dock="Top" MinHeight="30" Margin="0,0,0,5">
<Label>Location:</Label>
<ComboBox Name="cboLocation" Grid.Column="1" Grid.Row="4"
ItemsSource="{Binding Source={StaticResource LocationData}}"
DisplayMemberPath="location_text" SelectedValuePath="location_value"
SelectedValue="{Binding Path=Location}"
HorizontalAlignment="Left" Width="175" Margin="4" />
</DockPanel>
<DockPanel Margin="5">
<DataGrid ItemsSource="{Binding Path=ListOfEmployees}" AutoGenerateColumns="False" IsReadOnly="True"
Name="dgEmployee" SelectionMode="Single" SelectionUnit="FullRow" CanUserResizeColumns="True"
SelectedItem="{Binding Path=SelectedEmployee}" GridLinesVisibility="Horizontal">
<DataGrid.Columns>
<DataGridTextColumn Header="Employee ID" Width="SizeToCells" MinWidth="125" Binding="{Binding EmployeeID}" />
<DataGridTextColumn Header="First Name" Width="SizeToCells" MinWidth="200" Binding="{Binding FirstName}" />
<DataGridTextColumn Header="Last Name" Width="SizeToCells" MinWidth="200" Binding="{Binding LastName}" />
<DataGridTextColumn Header="Location" Width="SizeToCells" MinWidth="125" Binding="{Binding Location}" />
<DataGridCheckBoxColumn x:Name="Active" Header="Active" Binding="{Binding active}" MinWidth="75" />
</DataGrid.Columns>
</DataGrid>
</DockPanel>
</DockPanel>
</Grid>
This was my solution:
<DataGrid Name="dgrid" ItemsSource="{Binding UserSettings, IsAsync=True}" AutoGenerateColumns="False">
The key being the setting of IsAsync=True, allows the screen paint to occur
I found 2 ways to refresh DataGrid:
One was posted by VariableLost, it works with INotifyPropertyChanged but have one drawback. Whole DataGrid is refreshed with blink effect (disappears for a split second). Doesn't look natural.
Other solution is to refresh ObservableCollection in ViewModel or Code Behind (if you're not using MVVM):
CollectionViewSource.GetDefaultView(collectionName).Refresh();
Looks more natural because only changes cells affected by edition.
Your previous code should have also worked, but 'magic strings' got in the way.
Property name is ListOfEmployees and in its setter you raise PropertyChanged event with property name ListOfEmployee. 's' is missing.
Beware of your new code. It will raise CollectionChanged event on ListOfEmployees for every insertion, and that might make your app slower if you are doing many insertions.
For many insertions, it would be better to derive from ObservableCollection and implement Reset method that clears underlying Items, adds new items and raises CollectionChanged event of type Reset.
My question was answered in a post on another site. Instead of creating a new instance of the ListOfEmployees in my view model, I just cleared the existing one and added the results from my repository:
private void SearchEmployee()
{
if (String.IsNullOrEmpty(this.SearchText) || String.IsNullOrEmpty(this.Location))
{
NavigationActions.ShowError("Search Employees.", "Please enter your search text and select a location...");
return;
}
// clear the list and repopulate based on the search criteria
if (ListOfEmployees != null)
{
ListOfEmployees.Clear();
IList<EmployeeDto> iList = employeeRpository.GetEmployeesByQuery(SearchText, Location, IsActive);
foreach (EmployeeDto value in iList)
ListOfEmployees.Add(value);
}
}
That did the trick.

Resources