MVVM datagrid binding - c#

I'm using MVVM for my project and I'm trying to bind a table from my database with a DataGrid. But when I run my application datagrid is empty.
MainWindow.xaml.cs:
public MainWindow(){
InitializeComponent();
DataContext = new LecturerListViewModel()
}
MainWindow.xaml:
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Source=Lecturers}" >
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}"/>
<DataGridTextColumn Header="Surname" Binding="{Binding Surname}"/>
<DataGridTextColumn Header="Phone" Binding="{Binding Phone_Number}" />
</DataGrid.Columns>
</DataGrid>
LecturerListViewModel.cs:
public class LecturerListViewModel : ViewModelBase<LecturerListViewModel>
{
public ObservableCollection<Lecturer> Lecturers;
private readonly DataAccess _dataAccess = new DataAccess();
public LecturerListViewModel()
{
Lecturers = GetAllLecturers();
}
and ViewModelBase implements INotifyPropertyChanged.
Lecturer.cs
public class Lecturer
{
public Lecturer(){}
public int Id_Lecturer { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public string Phone_Number { get; set; }
What did I do wrong? I checked it with debuger and DataContext contains all lecturers, but ther aren't shown in datagrid.

You have an error in binding. Try this:
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Lecturers}" >
Code-behind:
private ObservableCollection<Lecturer> _lecturers = new ObservableCollection<Lecturer>();
public ObservableCollection<Lecturer> Lecturers
{
get { return _lecturers; }
set { _lecturers = value; }
}
Here is simple example code (LecturerSimpleBinding.zip).

Here we go
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Path=Lecturers}" >
Then
private ObservableCollection<Lecturer> lecturers;
public ObservableCollection<Lecturer> Lecturers
{
get { return lecturers; }
set
{
lecturers = value;
this.NotifyPropertyChanged("Lecturers");
}
}

Sayed Saad above is correct. I see two potential problems with your setup, both of which Sayed resolves.
The example posted in the question doen not implement INotifyPropertyChanged
The CLR property being bound to must be a PUBLIC PROPERTY. Fields will not work, as databindindg works via reflection.

Lecturers is a field, but data binding works with properties only. Try declaring Lecturers like:
public ObservableCollection<Lecturer> Lecturers { get; set; }

MainWindow.xaml.cs: OK
MainWindow.xaml: OK
LecturerListViewModel.cs: OK - assuming that GetAllLecturers() method returns an ObservableCollection of Lecturer.
Lecturer.cs:
public class Lecturer : INotifyPropertyChanged
{
//public Lecturer(){} <- not necessary
private int _id;
public int Id
{
get { return _id; }
set
{
_id = value;
OnPropertyChanged("Id");
}
}
// continue doing the above property change to all the properties you want your UI to notice their changes.
...
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Check this answer:
Adding INotifyPropertyChanged to Model?

Related

Updating DataGrid in WPF with Caliburn Micro

I am working on a WPF project using Caliburn Micro. In this app I have a DataGrid, which I populate with data from a SQL Server database using Dapper. Please consider the following code snippets:
ChangesModel.cs
using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace PTSRDesktopUI.Models
{
//public class for all changes attributes
public class ChangesModel : INotifyPropertyChanged
{
public int ID { get; set; }
public string Facility { get; set; }
public string Controller { get; set; }
public string ParameterName { get; set; }
public string OldValue { get; set; }
public string NewValue { get; set; }
public DateTime ChangeDate { get; set; }
private bool _validated;
public bool Validated
{
get { return _validated; }
set { _validated= value; NotifyPropertyChanged(); }
}
public DateTime? ValidationDate { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged([CallerMemberName] String propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}
OverviewViewmodel.cs
using Caliburn.Micro;
using PTSRDesktopUI.Helpers;
using PTSRDesktopUI.Models;
namespace PTSRDesktopUI.ViewModels
{
public class OverviewViewModel : Screen
{
//Create new Bindable Collection variable of type ChangesModel
public BindableCollection<ChangesModel> Changes { get; set; }
public OverviewViewModel()
{
//Create connection to dataAccess class
DataAccess db = new DataAccess();
//get the changes from dataAccess function and store them as a bindabla collection in Changes
Changes = new BindableCollection<ChangesModel>(db.GetChangesOverview());
//Notify for changes
NotifyOfPropertyChange(() => Changes);
}
//Validate_Btn click event
public void Validate()
{
//TODO: Change CheckBox boolean value to true and update DataGrid
}
}
}
OverviewView.xaml
<!--Datagrid Table-->
<DataGrid Grid.Row="1" x:Name="Changes" CanUserAddRows="False" AutoGenerateColumns="False">
<DataGrid.Columns>
<!--..........-->
<!--Some irrelevant code-->
<!--..........-->
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox x:Name="Validated_CheckBox" IsChecked="{Binding Path=Validated, UpdateSourceTrigger=PropertyChanged}" IsHitTestVisible ="False"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn IsReadOnly="True" Binding="{Binding Path=ValidationDate, TargetNullValue='NaN',
StringFormat='{}{0:dd.MM HH:mm}'}"/>
<DataGridTemplateColumn CellStyle="{StaticResource DataGridCellCentered}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="Validate_Btn" cal:Message.Attach="[Event Click] = [Action Validate]"
Visibility="{Binding DataContext.Validated,
Converter={StaticResource BoolToVisConverter}, RelativeSource={RelativeSource AncestorType=DataGridCell}}"
cal:Bind.Model="{Binding DataContext, RelativeSource={RelativeSource AncestorType=DataGrid}}">Validate</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
What I would like to accomplish is this:
When the user clicks the Validate Button, the boolean value for the CheckBox is set to true, the ValidationDate is set to now and the DataGrid is updated. I will then fire a stored procedure to update the database table as well. Also note that the Button is only visible if the CheckBox is checked. So all I want to know is how would I access the Validated property and ValidationDate in the ViewModel method Validate(). Also, how would I update the DataGrid after I change the values for Validated and ValidationDate, so that if I open another ContentControl, the values don't reset?
Anyone have any ideas? Thanks in advance.
Change the signature of your Validate method in the view model to accept a ChangesModel:
public void Validate(ChangesModel model)
{
model.ChangeDate = DateTime.Now;
}
...and change your XAML markup to this:
<Button x:Name="Validate_Btn"
cal:Message.Attach="[Event Click] = [Action Validate($this)]"
cal:Action.TargetWithoutContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType=DataGrid}}"
Visibility="...">Validate</Button>
For the data in the DataGrid to get refreshed, you also need to raise the PropertyChanged event for the ChangeDate property:
private DateTime _changeDate;
public DateTime ChangeDate
{
get { return _changeDate; }
set { _changeDate = value; NotifyPropertyChanged(); }
}

User editable datagrid with empty row, even if observableCollection is empty

I'm using a datagrid, bound to an observablecollection with TwoWay binding.
My goal is, that a user generates a list of data, starting from an empty collection.
So I enabled the option CanUserAddRow.
In the code, I generate the obsevrable collection with the following code:
private ObservableCollection<Ticket> idlessTicketList = new ObservableCollection<Ticket>();
The Ticket class, which the ObservableCollection consists of, looks as follows:
public class Ticket
{
public Ticket() { }
public bool ticketUsed { get; set; }
public string ticketNumber { get; set; }
public string ticketCustomer { get; set; }
public string ticketText { get; set; }
public double ticketTime { get; set; }
public Decimal ticketTypeNr { get; set; }
public string ticketTypeText { get; set; }
}
In the MainWindow Method I set the itemSource of my Datagrid to my ObservableCollection:
public MainWindow()
{
InitializeComponent();
gridIdlessTickets.ItemsSource = idlessTicketList;
}
My problem is now, that the empty row to add a new row is not displayed at startup.
If I add a new row by code myGridd.Add(row), then the empty row is displayed correctly and everythings works a expected.
How must the ObservableCollection be initialized and referenced to the itemSource correctly?
Where is the best place to initialize an itemSource?
Thanks in advance
This should work for you. Let me know if it helped:
XAML:
<Window>
<Grid>
<Datagrid ItemsSource="{Binding idlessTicketList }" SelectionMode="Single" SelectionUnit="Cell" IsReadOnly="False"
CanUserAddRows="True" CanUserDeleteRows="False" AutoGenerateColumns="False">
//ColumnDefinition...etc
<DataGridTextColumn Header="TicketNumber" Binding="{Binding TicketNumber}" />
<DataGridTextColumn Header="TicketCustomer" Binding="{Binding TicketCustomer}"
</Datagrid>
<Button name="ThisIsYourUpdateButton" Command="{Binding UpdateMyTicket}" Width="200" Content="Update me now"/>
</Grid>
</Window>
Code Behind (.xaml.cs):
public MainWindow()
{
InitializeComponent(); //parses the XAML...
DataContext = new MainWindowViewModel(); //outsources the view code to the
//"controller" to make the view only display and nothing else...
}
ViewModel: (MainWindowViewModel.cs)
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows;
namespace YourNameSpace
{
public class MainWindowViewModel : INotifyPropertyChanged
{
public ICommand UpdateMyTicket => new DelegateCommand<object>(ExecuteUpdateTicket);
public Ticket TicketInstance {get;set;}
public event PropertyChangedEventHandler PropertyChanged;
public virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
private ObservableCollection<Ticket> _idlessTicketList;
public ObservableCollection<Ticket> idlessTicketList
{
get { return _idlessTicketList; }
set
{
_idlessTicketList = value;
OnPropertyChanged("idlessTicketList");
}
}
//Constructor
public MainWindowViewModel()
{
idlessTicketList = new ObservableCollection<Ticket>();
}
public void ExecuteUpdateTicket(obj param)
{
//if the button is clicked you update your Ticket class properties here!
}
}
}
Add DelegateCommand.cs class like this:
using System;
namespace YourNamespaceName
{
public class DelegateCommand<T> : System.Windows.Input.ICommand
{
private readonly Predicate<T> _canExecute;
private readonly Action<T> _execute;
public DelegateCommand(Action<T> execute)
: this(execute, null)
{
}
public DelegateCommand(Action<T> execute, Predicate<T> canExecute)
{
_execute = execute;
_canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
if (_canExecute == null)
return true;
return _canExecute((parameter == null) ? default(T) : (T)Convert.ChangeType(parameter, typeof(T)));
}
public void Execute(object parameter)
{
_execute((parameter == null) ? default(T) : (T)Convert.ChangeType(parameter, typeof(T)));
}
public event EventHandler CanExecuteChanged;
public void RaiseCanExecuteChanged()
{
if (CanExecuteChanged != null)
CanExecuteChanged(this, EventArgs.Empty);
}
}
}
With a seperate View-Model Class which initializes the observablecollection, the empty row is displayed correctly.
But the data is not updated anymore.
Here's what I did:
I added the new class as a new namesspace
xmlns:vm="clr-namespace:nsMainWindowViewModel"
The binding is written like that:
<DataGrid ItemsSource="{Binding Path=idlessTicketList, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" AutoGenerateColumns="False" >
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Mode=TwoWay, Path=ticketCustomer, UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTextColumn Binding="{Binding Mode=TwoWay, Path=ticketText, UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTextColumn Binding="{Binding Mode=TwoWay, Path=ticketTime, UpdateSourceTrigger=PropertyChanged, StringFormat=\{0:n2\}}"/>
</DataGrid.Columns>
</Datagrid>
With this implementation, the PropertyChangedEventHandler is called when the application starts, but not wen a element changes.
How must the binding be written?
For the datagrid i tried:
{Binding Path=(idlessTicketList), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}
{Binding Path=(vm:idlessTicketList), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}
{Binding Path=(vm:MainWindowViewModel.idlessTicketList), Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}
and for the columns I tried;
{Binding Mode=TwoWay, Path=ticketCustomer, UpdateSourceTrigger=PropertyChanged}
{Binding Mode=TwoWay, Path=(vm:ticketCustomer), UpdateSourceTrigger=PropertyChanged}
{Binding Mode=TwoWay, Path=(vm:MainWindowViewModel.ticketCustomer), UpdateSourceTrigger=PropertyChanged}
Whit the debugging, I can see that the PropertyChange Method is called initially, but not after editing single elements.
How must the binding be defined to update te observableCollection in another namespace?
Thanks in advance

Databind itemsource of DataGridComboBoxColumn to collection in view model does not work

What I am trying to do here is databind the Itemsource of a DataGridComboBoxColumn to a collection of strings declared as property of my item view model.
The Datagrid itself is bound to another viewmodel which has a collection of viewModels that represent the rows on the datagrid.
All my other bindings work properly. The collection is also filled, but the combobox remains empty.
XAML:
<Window.Resources>
<ResourceDictionary>
<local:GeneralDataGridViewModel x:Key="generalDataGridVm"/>
</ResourceDictionary>
</Window.Resources>
<Grid>
<DataGrid DataContext="{StaticResource generalDataGridVm}"
ItemsSource="{Binding Collection}">
<DataGrid.Columns>
<DataGridComboBoxColumn x:Name="chbCodes"
Header="Code"
ItemsSource="{Binding Path=DataContext.Collection, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
C# ItemViewModel:
public class ItemViewModel : INotifyPropertyChanged
{
private ObservableCollection<string> _collection;
public ObservableCollection<string> Collection
{
get
{
return _collection;
}
}
public Model Model { get; set; }
public string Code
{
get { return Model.Code; }
set { Model.Code = value; }
}
public ItemViewModel()
{
}
public ItemViewModel(Model model)
{
Model = model;
_collection = new ObservableCollection<string>();
_collection.Add(model.Code);
Model.PropertyChanged += Model_PropertyChanged;
}
public void Model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
}
}
c# DataGridViewModel:
public class GeneralDataGridViewModel
{
private ObservableCollection<ItemViewModel> _collection;
public ObservableCollection<ItemViewModel> Collection
{
get { return _collection; }
set
{
_collection = value;
NotifyPropertyChanged("Collection");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected void NotifyPropertyChanged(String property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public GeneralDataGridViewModel()
: base()
{
_collection = new ObservableCollection<ItemViewModel>();
}
public GeneralDataGridViewModel(List<Model> models)
{
_collection = new ObservableCollection<ItemViewModel>((from m in models
select new ItemViewModel(m)).ToList());
}
}
C# Model:
public class Model: INotifyPropertyChanged
{
public string Code { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public override string ToString()
{
return Code;
}
}
The code you have posted does not compile, but looking at it the issue might be with the data context, which you are setting to a static resource. If you are going to do this, the view model must be in your resource dictionary. The bindings in XAML are fine, see below for an example of this working:
XAML:
<Window.Resources>
<ResourceDictionary>
<local:GeneralDataGridViewModel x:Key="generalDataGridVm"/>
</ResourceDictionary>
</Window.Resources>
<StackPanel Orientation="Vertical">
<DataGrid DataContext="{StaticResource generalDataGridVm}" Name="DataGrid1" ItemsSource="{Binding Collection}">
<DataGrid.Columns>
<DataGridComboBoxColumn x:Name="chbCodes"
Header="Code"
ItemsSource="{Binding Path=DataContext.Collection, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
</DataGrid.Columns>
</DataGrid>
</StackPanel>
By declaring generalDataGridVm in XAML, the constructor is called in XAML, let's assume that construction supplies the values for the collection:
public GeneralDataGridViewModel() : base()
{
_collection = new ObservableCollection<ItemViewModel>();
_collection.Add(new ItemViewModel(new Model("code1")));
_collection.Add(new ItemViewModel(new Model("code2")));
_collection.Add(new ItemViewModel(new Model("code3")));
_collection.Add(new ItemViewModel(new Model("code4")));
}
With this code, it results in a populated list:
So I think you just need to make sure that you are declaring your view model properly (I would suggest not creating this in XAML unless there is some good reason).
Then ensure that the collection is kept up to date properly in that particular instance of your view model.

WPF data binding to Window DataContext object properties

**[Solved]**this is my first question asked here, so some mistakes could pass.
In my WPF app I want to bind in "two-way mode" all controls to corresponding properties in special object instance BuildingManagementSystem, which I'd like to set as Window DataContext.
But nothing works (data are not displayed). What is the right way to bind it?
Here is my code:
public partial class MainWindow : Window
{
BuildingManagementSystem bms { get; set; }
public MainWindow()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(Window1_Loaded);
}
void Window1_Loaded(object sender, RoutedEventArgs e)
{
bms = new BuildingManagementSystem();
this.DataContext = bms;
}
public class BuildingManagementSystem
{
public string Name { get; set; }
public readonly FireAlarmSystem FireAlarmSystem;
public BuildingManagementSystem()
{
FireAlarmSystem = new FireAlarmSystem();
}
}
class FireAlarmSystem
{
private int alarmSmokeRate, currentSmokeRate;
public List<PowerConsumer> Devices { get; set; }
public int CurrentSmokeRate
{
get { return currentSmokeRate; }
set { SetField(ref currentSmokeRate, value, () => CurrentSmokeRate); }
}
public FireAlarmSystem()
{
Devices = new List<PowerConsumer>();
}
}
class PowerConsumer
{
public string Name { get; set; }
public double Power { get; set; }
public int Priority { get; set; }
public bool Enabled { get; set; }
}
XAML:
<DataGrid Name="FireAlarmGrid" HorizontalAlignment="Left" Margin="10,51,0,0" CanUserAddRows="True"
CanUserDeleteRows="True" VerticalAlignment="Top" AutoGenerateColumns="False" ItemsSource="{Binding FireAlarmSystem.Devices}" >
<DataGrid.RowValidationRules>
<local:FireValidationRule ValidationStep="UpdatedValue"/>
</DataGrid.RowValidationRules>
<DataGrid.Columns>
<DataGridCheckBoxColumn Header="Enabled" Binding="{Binding Enabled}"></DataGridCheckBoxColumn>
<DataGridTextColumn Header="Name" Binding="{Binding Name,ValidatesOnExceptions=True }" >
</DataGridTextColumn>
<DataGridTextColumn Header="Power" Binding="{Binding Power}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<TextBox Name="tbCurrentSmokeRate" Text="{Binding Path=FireAlarmSystem.CurrentSmokeRate, Mode=TwoWay}" VerticalAlignment="Top" Width="70"/>
I personally like to create static instances of the viewmodel as shown here:
<Window.Resources>
<ViewModels:MainWindowVM x:Key="VM"></ViewModels:MainWindowVM>
When I do that then binding is easy because the properites page finds the properties for you in VS.
<StackPanel Grid.Column="0" Margin="5,5,5,0"
DataContext="{Binding Source={StaticResource VM}}">
But remember you need to add the namespace
xmlns:ViewModels="clr-namespace:MyWPF.ViewModels"
xmlns:Views="clr-namespace:MyWPF.Views"
This allow property binding like this:
<Views:UcTitle x:Name="XTitle" ></Views:UcTitle>
<Views:UcLegendTitle x:Name="XLegendTitle"/>
<Views:UcSeriesTitle x:Name="XSeriesTitle" />
<Views:UcSeriesTypes x:Name="XSeriesTypes"/>
And you don't have to type any of the names in...
In your case you are not using a ViewModel but you are setting the data-context correctly. So this can only be either no data to display or improper property bind. Remember for this to work you need three things 1) DataContext 2) Vales and 3) Proper binding by Name... It's the third one that trips up folks a lot when just starting out with the great WPF binding system.
Couple of mistakes out here
Seems like BuildingManagementSystem is intended to be your data-context
In place of BuildingManagementSystem bms { get; set; } write the below code:
BuildingManagementSystem bms =new BuildingManagementSystem ();
You need to implement INotifyPropertyChanged event in your ViewModels to reflect the changes on UI
If you intent is to assign values to each of the underlying child view-models, make use of parameterized constructors to pass and assign values. Simply instantiating the child viewModels in parent will not serve any purpose.
To get MVVM working...
You will need to update your View Model to Implement INotifyPropertyChanged to let wpf know your property changed
Use Observable collections instead of lists, ObservableCollections have the wiring to let Datagrid listview, listboxes know items in the collections have changed
Change the Field to a Property so WPF will see it...
<DataGrid Name="FireAlarmGrid" HorizontalAlignment="Left" Margin="10,51,0,0" CanUserAddRows="True"
CanUserDeleteRows="True" VerticalAlignment="Top" AutoGenerateColumns="False" ItemsSource="{Binding FireAlarmSystem.Devices}" >
<DataGrid.RowValidationRules>
<local:FireValidationRule ValidationStep="UpdatedValue"/>
</DataGrid.RowValidationRules>
<DataGrid.Columns>
<DataGridCheckBoxColumn Header="Enabled" Binding="{Binding Enabled,Mode=TwoWay}"></DataGridCheckBoxColumn>
<DataGridTextColumn Header="Name" Binding="{Binding Name,ValidatesOnExceptions=True,Mode=TwoWay }" >
</DataGridTextColumn>
<DataGridTextColumn Header="Power" Binding="{Binding Power,Mode=TwoWay}"></DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
<TextBox Name="tbCurrentSmokeRate" Text="{Binding Path=FireAlarmSystem.CurrentSmokeRate, Mode=TwoWay}" VerticalAlignment="Top" Width="70"/>
public class BuildingManagementSystem : INotifyPropertyChanged
{
private string _Name;
public string Name
{
get { return _Name; }
set
{
if (_Name != value)
{
_Name = value;
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
}
private FireAlarmSystem _fireAlarmSystem;
public FireAlarmSystem FireAlarmSystem { get { return _fireAlarmSystem; } }
public BuildingManagementSystem()
{
_fireAlarmSystem = new FireAlarmSystem();
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged = delegate { };
#endregion
}
public class FireAlarmSystem : INotifyPropertyChanged
{
private int alarmSmokeRate, currentSmokeRate;
public ObservableCollection<PowerConsumer> Devices { get; set; }
public int CurrentSmokeRate
{
get { return currentSmokeRate; }
set
{
//SetField(ref currentSmokeRate, value, () => CurrentSmokeRate);
PropertyChanged(this, new PropertyChangedEventArgs("CurrentSmokeRate"));
}
}
public FireAlarmSystem()
{
Devices = new ObservableCollection<PowerConsumer>();
//Create some test data...
Devices.Add(new PowerConsumer() { Name = "One", Enabled = true, Power = 100, Priority = 1 });
Devices.Add(new PowerConsumer() { Name = "two", Enabled = false, Power = 101, Priority = 2 });
Devices.Add(new PowerConsumer() { Name = "three", Enabled = false, Power = 103, Priority = 3 });
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged = delegate { };
#endregion
}
public class PowerConsumer
{
public string Name { get; set; }
public double Power { get; set; }
public int Priority { get; set; }
public bool Enabled { get; set; }
}
Thanks to all, I've found a mistake, it was defining FireAlarmSystem as readonly, it shold be a property.
2 Stuart Smith, you're right with INotifyPropertyChanged, but I have an abstract class-ancestor BuildingSystem, which implements this interface. I've forgot to post it.

Selecting the new row after adding a new row to a DataTable bound to a DataGrid

I have a simple C# program with some data in a DataTable, which is bound to a WPF DataGrid.
<DataGrid Grid.Row="1" DataContext="{Binding}" Name="dataGridEditTab"
and
dataGridEditTab.ItemsSource = NavTable.DefaultView;
I have an "Add Row" button that adds a blank row to the DataTable when pressed. The blank row shows up on the DataGrid fine, but it is not selected. I want to automatically select the new row, but I cannot figure out how to do this. I think I need to set SelectedItem to something, but I haven't figured out what. Any help? Thanks.
According to this thread (How to set selected row of DataGridView to newly-added row when the grid is bound to sorted DataView) you can accomplish this using the DataGridViewRowsAddedEventArgs. Basically create a global variable to place the newly added row index in. Assign the appropriate value to that index in the EventArgs, and then use that index to select the row you are looking for.
In case it is MVVM use binding for SelectedItem or SelectedIndex. If not, bettter to use it)
To achieve your goal just bind SelectedIndex and whenever you add the row, set SelectedIndex as RowsCount-1;
Here is an example.
<Grid >
<DataGrid AutoGenerateColumns="False" CanUserAddRows="False" ItemsSource="{Binding Path=Persons}" Margin="0,65,0,0"
SelectedIndex="{Binding Path=SelectedIndex, Mode=TwoWay}">
<DataGrid.Columns>
<DataGridTextColumn Header="First name" Binding="{Binding Path=FirstName}" Width="*"/>
<DataGridTextColumn Header="Last name" Binding="{Binding Path=LastName}" Width="*"/>
</DataGrid.Columns>
</DataGrid>
<Button Content="Add new row" Height="23" HorizontalAlignment="Left" Margin="317,12,0,0" Command="{Binding Path=AddCommand}" VerticalAlignment="Top" Width="112" />
</Grid>
and viewmodel
public class MainViewModel : INotifyPropertyChanged
{
private int _selectedPerson;
public MainViewModel()
{
AddCommand = new RelayCommand(AddAndSelectePerson);
Persons = new DataTable();
Persons.Columns.Add("FirstName");
Persons.Columns.Add("LastName");
Persons.Rows.Add("Alexandr", "Puskin");
Persons.Rows.Add("Lev", "Tolstoy");
}
public ICommand AddCommand { get; private set; }
public int SelectedIndex
{
get { return _selectedPerson; }
set
{
_selectedPerson = value;
OnPropertyChanged("SelectedIndex");
}
}
public DataTable Persons { get; private set; }
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
private void AddAndSelectePerson()
{
Persons.Rows.Add();
SelectedIndex = Persons.Rows.Count - 1;
}
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
public class RelayCommand : ICommand
{
private readonly Action _actionToExecute;
public RelayCommand(Action actionToExecute)
{
_actionToExecute = actionToExecute;
}
#region ICommand Members
public void Execute(object parameter)
{
_actionToExecute();
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
#endregion
}
public class Person
{
public Person()
{
}
public Person(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
public string FirstName { get; set; }
public string LastName { get; set; }
}
Example to try

Categories