Find Textbox in DataGrid WPF - c#

I edited last question.
I have 2 classes
public class Signal: INotifyPropertyChanged
{
public string Name { get; set;}
public Int32 Value { get; set;}
private ObservableCollection < RawVal > rawValue1;
public ObservableCollection < RawVal > rawValue
{
get { return rawValue1; }
set
{ rawValue1 = value;
OnPropertyChanged("rawValue");
if (value != null && value.Count > 0)
{
SelectedRaValue = value.First();
}
}
}
private RawVal selectedRaValue;
public RawVal SelectedRaValue
{
get
{
return selectedRaValue;
}
set
{
selectedRaValue = value;
OnPropertyChanged("SelectedRaValue");
ComboValue = value.name;
OnPropertyChanged("ComboValue");
}
}
public string ComboValue
{
get;
set;
}
#region Implementation of INotifyPropertyChanged
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
#endregion
}
And Here XAML:
<DataGrid ItemsSource="{Binding}" Name="grdSignal" Grid.Row="1" CanUserAddRows="False" AutoGenerateColumns="False" SelectionChanged="grdSignal_SelectionChanged_1">
<DataGrid.Columns>
<DataGridTextColumn Header="Signal Name" Binding="{Binding Name}" Width="150"/>
<DataGridTemplateColumn Header="Physical Value" Width="120">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding rawValue, Mode=TwoWay}" SelectedItem="{Binding SelectedRaValue,Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" DisplayMemberPath="name" Name="cmbVal"
IsEditable="True" KeyDown="cmbVal_KeyDown" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Value" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding ComboValue}" Name="tBoxValue" TextChanged="tBoxVale_textChanged"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Comment" Width="200">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Comment}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Now, here comes my problem. Combobox is Editable, if user enters anything (string), and presses ENTER Key, Combobox text goes to Textbox Text.
private void cmbVal_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)
{
string s = ((ComboBox)sender).Text;
DataGridRow row = sigGrid.ItemContainerGenerator.ContainerFromIndex(sigGrid.SelectedIndex) as DataGridRow;
var i = 3;
TextBox ele = sigGrid.FindName("tbValue") as TextBox;
}
}
And Result is:

#Mamurbek,
You could use the FindVisualDescendants function of that post :
Datagrid templatecolumn update source trigger explicit only updates first row
Using that function, you could have all textboxes for instance :
var textboxes = AccessGrid.FindAllVisualDescendants()
.Where(elt => elt.Name == "tBoxValue" )
.OfType<TextBox>();
Only the first for instance :
var textboxes = AccessGrid.FindAllVisualDescendants()
.Where(elt => elt.Name == "tBoxValue" )
.OfType<TextBox>()
.FirstOrDefault();
Or only the 3rd using Skip and Take :
int n = 3;
var textboxes = AccessGrid.FindAllVisualDescendants()
.Where(elt => elt.Name == "tBoxValue" )
.OfType<TextBox>()
.Skip(n-1)
.Take(1);
Hope it helps

If you use bindings in your xaml, you obviously have a object used as a datacontext for your grid named grdSignal.
And from this line...
<TextBox Text="{Binding Comment}"/>
I can see that the bound objects have a Comment property:
If you want to read or write this value, you have to locate the respective property is your data source which is bound to the grid (used a data context) and read or assign this property.
In your case, if your assign a value to this property (if it implements INotifyPropertyChanged) the TextBox will update automatically to the new value.
In summary:
If you use bindings, you have to read and assign from / to properties of
your data classes in order to communicate with the UI. Bindings connect targets (controls in your UI) to sources (data in your data context class or view model), so there should be no need to name controls in your view.
If you do not use bindings, you can communicate with the UI using the code behind file and accessing elements by their names, but this is not the way it should be done in WPF (not MVVM style)

Related

WPF MVVM - Get access to DependencyProperty of DataGrid in View from ViewModel

In my View I have a DataGrid which stores objects of 2 descending types. Every row has a Button with a Command connected to the ViewModel. In the ViewModel I need to find out which type of object has been chosen.
The question is what is the best and simple way of accessing SelectedItem property of the DataGrid from the Execute command method in a ViewModel?
So far I did it like this:
var window = Application.Current.Windows.OfType<Window>()
.SingleOrDefault(x => x.IsActive);
var dataGrid = (DataGrid) window.FindName("MyGridName");
...
UPDATE - Xaml:
<DataGrid Name="MyGridName" ItemsSource="{Binding Elements}"
AutoGenerateColumns="False" CanUserAddRows="False"
CanUserDeleteRows="False" IsReadOnly="True">
<DataGrid.Columns>
<DataGridTemplateColumn Width="auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Name="OptionsBtn" Margin="5" Width="auto"
Height="30" Content="Options"
Command="{Binding ElementName=ElementsViewWindow,
Path=DataContext.ShowOptionsMenuCommand}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
If you take the right MVVM approach this is very easy to do. All you need is to define the item collection of you entities which will be bound to ItemsSource of your DataGrid and a property which will be bound to SelectedItem of your DataGrid. Then in your command you simply reference your selected item property of your model to access the selected item in your DataGrid.
Here is an example implementation with MVVM Light. First you define an observable collection of your entities:
public const string ItemsCollectionPropertyName = "ItemsCollection";
private ObservableCollection<DataItem> _itemsCollection = null;
public ObservableCollection<DataItem> ItemsCollection
{
get
{
return _itemsCollection;
}
set
{
if (_itemsCollection == value)
{
return;
}
_itemsCollection = value;
RaisePropertyChanged(ItemsCollectionPropertyName);
}
}
Then you define a property to hold the selected item:
public const string SelectedItemPropertyName = "SelectedItem";
private DataItem _selectedItem = null;
public DataItem SelectedItem
{
get
{
return _selectedItem;
}
set
{
if (_selectedItem == value)
{
return;
}
_selectedItem = value;
RaisePropertyChanged(SelectedItemPropertyName);
}
}
After that you define a command to handle business logic:
private ICommand _doWhateverCommand;
public ICommand DoWhateverCommand
{
get
{
if (_doWhateverCommand == null)
{
_doWhateverCommand = new RelayCommand(
() => { /* do your stuff with SelectedItem here */ },
() => { return SelectedItem != null; }
);
}
return _doWhateverCommand;
}
}
Finally you create view elements and bind them to the ViewModel:
<DataGrid ItemsSource="{Binding ItemsCollection}" SelectedItem="{Binding SelectedItem}" AutoGenerateColumns="True" />
<Button Content="Do stuff" Command="{Binding DoWhateverCommand}" />
The question is what is the best and simple way of accessing SelectedItem property of DataGrid from Execute command function in a ViewModel?
Just add a property to the view model class where the ShowOptionsMenuCommand property is defined and bind the SelectedItem property of the DataGrid to this one:
<DataGrid Name="MyGridName" ItemsSource="{Binding Elements}" SelectedItem="{Binding SelectedElement}" ... >
Then you can access the source property (SelectedElement or whatever you choose to call it) directly from the Execute method.
The other option would be to pass the item as a CommandParameter to the command:
<Button Name="OptionsBtn" ... Content="Options"
Command="{Binding ElementName=ElementsViewWindow, Path=DataContext.ShowOptionsMenuCommand}"
CommandParameter="{Binding}" />

Is there a way to force entry of a WPF DataGrid column before other columns?

I have an editable DataGrid with a column A and B. When adding a new item/row, it doesn't make sense for the user to enter anything in column B until they enter something in column A. Is there a way to force the user, when creating a new item, to initialize column A first? I'm able to use RowValidationRules just fine to ensure column A is initialized, but I'm trying to find a way to prevent that altogether.
I've also looked at handling Begin/Prepare cell edit events and cancelling edit of column B until A is initialized. The problem there is that the user can enter a value for A but it is not yet visible/pending, and has not yet been committed by the DataGrid.
Assuming that this is your model
public class Model : INotifyPropertyChanged
{
string _desc;
public string Desc { get { return _desc; } set { _desc = value; RaisePropertyChanged("Desc"); } }
string _name;
public string Name { get { return _name; } set { _name = value; RaisePropertyChanged("Name"); } }
public event PropertyChangedEventHandler PropertyChanged;
void RaisePropertyChanged(string propname)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propname));
}
}
and first column is bound to Name and second column to Desc:
<DataGrid.Columns>
<DataGridTextColumn Header="A" Binding="{Binding Name, UpdateSourceTrigger=PropertyChanged}"/>
<DataGridTextColumn Header="B" Binding="{Binding Desc}"/>
</DataGrid.Columns>
you can handle BeginningEdit event, as you said. Note that in Binding the first column I set UpdateSourceTrigger=PropertyChanged. This solve the issue you pointed out:
it is not yet visible/pending, and has not yet been committed by the DataGrid
Now you can check the requirements and Cancel them if needed. For example:
private void dg_BeginningEdit(object sender, DataGridBeginningEditEventArgs e)
{
if (((DataGrid)sender).CurrentCell.Column.DisplayIndex == 1 &&
((Model)e.Row.Item).Name == null)
{
MessageBox.Show("Insert value of the first column first!");
e.Cancel = true;
}
}
You could do this using a DataGridTemplateColumn for Column B.
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=ColPropertyB}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=ColPropertyB}"
IsReadOnly="{Binding Path=ColPropertyA, Converter={StaticResource IsPropertyInvalidConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
Instead of using the IsReadOnly property you could also use Visibility and multiple UIElements or what ever you want.
A second solution could be the usage of a CellEditingTemplateSelector and two CellEdititingTempates. The first with TextBlock if ColA is invalid and a TextBox otherwise. The TemplateSelector validates property A and returns the correct Template.
<!-- DataGrid ResourceDictionary -->
<DataTemplate x:Key="ValidTemplate">
<TextBox Text="{Binding Path=ColPropertyB}" />
</DataTemplate>
<DataTemplate x:Key="InvalidTemplate">
<TextBlock Text="{Binding Path=ColPropertyB}" />
</DataTemplate>
<ColBTemplateSelector x:Key="ColBTemplateSelector"
ValidDataTemplate="{StaticResource ValidTemplate}"
InvalidDataTemplate="{StaticResource InvalidTemplate}" />
<!-- DataGridColumns -->
<DataGridTemplateColumn CellEditingTemplateSelector="{StaticResource ColBTemplateSelector}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=RowPropertyB}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
TemplateSelector
public class ColBTemplateSelector : DataTemplateSelector
{
public DataTemplate ValidDataTemplate { get; set; }
public DataTemplate InvalidDataTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
var tempItem = (item as YourDataItem);
if (tempItem == null || tempItem.IsColPropertyAInvalid()) return InvalidDataTemplate;
return ValidDataTemplate;
}
}
With the TemplateSelector I'm not sure if it is called always when entering editing mode...

Binding ICommand WPF ObservableCollection DataGrid

I'm trying to get a WPF MVVM template to work with the basic functionality I am doing in a WPF but non MVVM application. In this case I am trying to capture the RowEditEnding event (which I am) to validate the data on the row that has changed (and this is the problem).
In the XAML I have used an event trigger:
<DataGrid AutoGenerateColumns="False" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Stretch"
ItemsSource="{Binding oDoc.View}">
<DataGrid.Columns>
<DataGridTextColumn x:Name="docIDColumn" Binding="{Binding DocId}" Header="ID" Width="65"/>
<DataGridTextColumn x:Name="DocumentNumberColumn" Binding="{Binding Number}" Header="Document Number" Width="*"/>
<DataGridTextColumn x:Name="altIDColumn" Binding="{Binding AltID, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Header="Alt" Width="55"/>
</DataGrid.Columns>
<i:Interaction.Triggers>
<i:EventTrigger EventName="RowEditEnding">
<i:InvokeCommandAction Command="{Binding DocRowEdit}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
With a delegate command to rout to the handler:
public ObservableCollection<Document> oDoc
{
get
{
return _oDoc;
}
}
public ICommand DocRowEdit
{
get { return new DelegateCommand(DocumentRowEditEvent); }
}
public void DocumentRowEditEvent()
{
//How do I find the changed item?
int i = 1;
}
I have not found a way to find the member of the ObservableCollection (oDoc) that has pending changes. I notice that the datagrid is doing some validation, the AltID field that I want to change will highlight red if I put in a non numeric value. But I want to handle the validation, and associated messaging myself. What am I missing? I was thinking to somehow raise a property changed event, but don't find how to wire something like this in:
protected void RaisePropertyChangedEvent(string propertyName)
{
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs(propertyName));
}
The last two code blocks are from my ViewModel class, and I'm trying to do this without any code behind, aside from instantiating the ViewModel in the MainWindow constructor.
You can add a property to your ViewModel:
public Document CurrentDocument
{
get
{
return _currentDocument;
}
set
{
if (value != _currentDocument)
{
_currentDocument = value;
OnPropertyChanged("CurrentDocument") // If you implement INotifyPropertyChanged
}
}
}
and then you can bind it to SelectedItem property of your DataGrid:
<DataGrid AutoGenerateColumns="False" HorizontalAlignment="Stretch" Margin="0,0,0,0" VerticalAlignment="Stretch"
ItemsSource="{Binding oDoc.View}" SelectedItem="{Binding CurrentDocument}">
Therefore your edit method will be:
public void DocumentRowEditEvent()
{
CurrentDocument.Number = DateTime.Now.Ticks;
/* And so on... */
}
I hope it helps.

Getting whole row value if checkbox is checked in datagrid C#

I'm trying to write a code for getting 3 values from textboxes if some of checkboxes in same row is checked.
Anyone know an easy(or hard) way to do this?
My datagrid looks like this:
I have Load button that finds a file of specific type(XML.config) somewhere in file system, after that I'm calling a method that gets some strings from that file, find substrings of them and put them in 3 separated lists. Those values are in datagrid as Type, MapTo and Name.
I accomplish this by putting all 3 lists in one ObservableCollection and after that I'm sending that ObservalableCollection to datagrid like this:
ObservableCollection<Tuple<string, string, string>> _obsCollection = new ObservableCollection<Tuple<string, string, string>>();
public ObservableCollection<Tuple<string, string, string>> MyObsCollection
{
get { return _obsCollection; }
}
tabela.ItemsSource = _obsCollection;
This is XAML code that shows binding:
<DataGrid Grid.Column="0" AutoGenerateColumns="False" Height="206" HorizontalAlignment="Left" Margin="12,265,0,0" Name="tabela" VerticalAlignment="Top" Width="556" SelectionChanged="tabela_SelectionChanged" Grid.RowSpan="2" ItemsSource="Binding MyObsCollection">
<DataGrid.Columns>
<DataGridTextColumn Header="Type" Width="122" Binding="{Binding Item1}"/>
<DataGridTextColumn Header="MapTo" Width="122" Binding="{Binding Item2}"/>
<DataGridTextColumn Header="Name" Width="121" Binding="{Binding Item3}"/>
<DataGridTemplateColumn Header="Controller">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding DataGridChecked}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Service">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding DataGridChecked}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Injection">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding DataGridChecked}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
What I'm practically trying to is to accomplish looping thou all 3 columns containing checkboxes to see which of them are selected and if any of 3 in same row is selected then I need to send all 3 string values from that row to some variable.
Anyone can help me with this. For instance I don't know how to get isSelected property from checkbox in data grid.
I was doing a lot of researching and all that I was able to find was examples for DataGridView, and almost nothing for DataGrid.
Instead of using Tuple create your own class, say RowData with all the properties you want to show as columns:
public class RowData: INotifyPropertyChanged
{
//implement INotifyPropertyChanged
public string Type { get; set; }
public string MapTo { get; set; }
public string Name { get; set; }
public bool Controller { get; set; }
public bool Service { get; set; }
public bool Injection { get; set; }
}
change ObservableCollection to use your type
public ObservableCollection<RowData> MyObsCollection { get { .... } }
and set AutoGenerateColumns="True" on DataGrid
<DataGrid
Grid.Column="0"
Grid.RowSpan="2"
AutoGenerateColumns="True"
Height="206"
Width="556"
HorizontalAlignment="Left"
Margin="12,265,0,0"
Name="tabela"
VerticalAlignment="Top"
SelectionChanged="tabela_SelectionChanged"
ItemsSource="{Binding MyObsCollection}"/>
and then to get items where any of 3 CheckBoxes is selected you do:
var selectedList = MyObsCollection.Where(n => n.Controller || n.Service || n.Injection).ToList();
To get the Checked item row from DataGrid and add it into list
List<string>chzone=new List<string>();//Declare it as globally,
//Note: if DataGrid is Binded as ,Datagrid.Itemsource=dt.defaultview;
//If datagrid contain the Checkbox, get the checked item row and add it into list
private void DataGridcheckbox_Checked(object sender, RoutedEventArgs e)
{
var checker = sender as CheckBox;
if (checker.IsChecked == true)
{
var item = checker.DataContext as DataRowView;
object[] obj = item.Row.ItemArray;//getting entire row
chzone.Add(obj[0].ToString());//getting row of first column value
}
else //This is for Unchecked Scenario
{
var item = checker.DataContext as DataRowView;
object[] obj = item.Row.ItemArray;
bool res = chzone.Contains(obj[0].ToString());
if (res == true)
{
chzone.Remove(obj[0].ToString());//this is used to remove item in list
}
}
}

How to Select All CheckBox of a Column by DataGrid Header CheckBox in WPF DataGrid

I have a DataGrid with one CheckBoxColumn. In the header of that CheckBoxColumn I have added a CheckBox to Select all CheckBoxes of that Datagrid Row.
How can I achieve that?
My XAML Code for WPF dataGrid:
<DataGrid AutoGenerateColumns="False" CanUserAddRows="False" Grid.RowSpan="2" Height="130" HorizontalAlignment="Left" IsReadOnly="False" Margin="189,340,0,0" Name="dgCandidate" TabIndex="7" VerticalAlignment="Top" Width="466" Grid.Row="1" >
<DataGrid.Columns>
<DataGridTextColumn x:Name="colCandidateID" Binding="{Binding CandidateID}" Header="SlNo" MinWidth="20" IsReadOnly="True" />
<DataGridTextColumn x:Name="colRegistraion" Binding="{Binding RegisterNo}" Header="Reg. No." IsReadOnly="True" />
<DataGridTextColumn x:Name="colCandidate" Binding="{Binding CandidateName}" Header="Name" MinWidth="250" IsReadOnly="True" />
<DataGridTemplateColumn>
<DataGridTemplateColumn.Header>
<CheckBox Name="chkSelectAll" Checked="chkSelectAll_Checked" Unchecked="chkSelectAll_Unchecked"></CheckBox>
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate >
<DataTemplate >
<CheckBox x:Name="colchkSelect1" Checked="colchkSelect1_Checked" Unchecked="colchkSelect1_Unchecked" ></CheckBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Convert your Candidate class into something like this:
public class Candidate : DependencyObject
{
//CandidateID Dependency Property
public int CandidateID
{
get { return (int)GetValue(CandidateIDProperty); }
set { SetValue(CandidateIDProperty, value); }
}
public static readonly DependencyProperty CandidateIDProperty =
DependencyProperty.Register("CandidateID", typeof(int), typeof(Candidate), new UIPropertyMetadata(0));
//RegisterNo Dependency Property
public int RegisterNo
{
get { return (int)GetValue(RegisterNoProperty); }
set { SetValue(RegisterNoProperty, value); }
}
public static readonly DependencyProperty RegisterNoProperty =
DependencyProperty.Register("RegisterNo", typeof(int), typeof(Candidate), new UIPropertyMetadata(0));
//CandidateName Dependency Property
public string CandidateName
{
get { return (string)GetValue(CandidateNameProperty); }
set { SetValue(CandidateNameProperty, value); }
}
public static readonly DependencyProperty CandidateNameProperty =
DependencyProperty.Register("CandidateName", typeof(string), typeof(Candidate), new UIPropertyMetadata(""));
//BooleanFlag Dependency Property
public bool BooleanFlag
{
get { return (bool)GetValue(BooleanFlagProperty); }
set { SetValue(BooleanFlagProperty, value); }
}
public static readonly DependencyProperty BooleanFlagProperty =
DependencyProperty.Register("BooleanFlag", typeof(bool), typeof(Candidate), new UIPropertyMetadata(false));
}
in MainWindow.xaml:
<DataGrid ItemsSource="{Binding CandidateList}">
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding CandidateID}"/>
<DataGridTextColumn Header="RegNr" Binding="{Binding RegisterNo}"/>
<DataGridTextColumn Header="Name" Binding="{Binding CandidateName}"/>
<DataGridTemplateColumn>
<DataGridTemplateColumn.Header>
<CheckBox Checked="CheckBox_Checked" Unchecked="CheckBox_Checked"></CheckBox>
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate >
<DataTemplate>
<CheckBox IsChecked="{Binding BooleanFlag}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
in MainWindow.xaml.cs:
public MainWindow()
{
DataContext = this;
CandidateList.Add(new Candidate()
{
CandidateID = 1,
CandidateName = "Jack",
RegisterNo = 123,
BooleanFlag = true
});
CandidateList.Add(new Candidate()
{
CandidateID = 2,
CandidateName = "Jim",
RegisterNo = 234,
BooleanFlag = false
});
InitializeComponent();
}
//List Observable Collection
private ObservableCollection<Candidate> _candidateList = new ObservableCollection<Candidate>();
public ObservableCollection<Candidate> CandidateList { get { return _candidateList; } }
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
foreach (var item in CandidateList)
{
item.BooleanFlag = true;
}
}
private void UnheckBox_Checked(object sender, RoutedEventArgs e)
{
foreach (var item in CandidateList)
{
item.BooleanFlag = false;
}
}
Strictly speaking the model should not know about the view and so the solution proposed by blindmeis, where the model change is updating every row in the datagrid, breaks the MVVM/Presentation Design pattern. Remember that in MVVM the dependency flow is View -> ViewModel -> Model so if you are referencing controls in your view model (or control codebehind) then you have effectively broken the pattern and you will probably run into issues further down the track.
I have added CheckBox to Select all CheckBox in Datagrid Row
if you mean select all checkbox in datagrid column, then i would say: simply update your itemssource collection with checked/unchecked.
public bool SelectAll
{
get{return this._selectAll;}
set
{
this._selectAll = value;
this.MyItemsSourceCollection.ForEach(x=>x.MyRowCheckProperty=value);
this.OnPropertyChanged("SelectAll");
}
}
xaml
<DataGridTemplateColumn>
<DataGridTemplateColumn.Header>
<CheckBox isChecked="{Binding SelectAll}"></CheckBox>
</DataGridTemplateColumn.Header>
<DataGridTemplateColumn.CellTemplate >
<DataTemplate >
<CheckBox IsChecked="{Binding MyRowCheckProperty}"></CheckBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
i dunno if the xaml bindings are right, but i hope you can see my intention
It turns out that this is quite a lot harder to get right than one would hope.
The first problem is that you can't just bind the view model to the column header because it doesn't have the view model as its data context, so you need a binding proxy to correctly route the binding to the view model.
public class BindingProxy : Freezable
{
public static readonly DependencyProperty DataProperty = DependencyProperty.Register(
"Data",
typeof(object),
typeof(BindingProxy),
new UIPropertyMetadata(null));
public object Data
{
get { return this.GetValue(DataProperty); }
set { this.SetValue(DataProperty, value); }
}
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
}
Now create a binding proxy in your data grid's resources:
<DataGrid.Resources>
<aon:BindingProxy
x:Key="DataContextProxy"
Data="{Binding}" />
</DataGrid.Resources>
Then the column needs to be defined as:
<DataGridTemplateColumn>
<DataGridTemplateColumn.HeaderTemplate>
<DataTemplate>
<CheckBox
Command="{Binding
Data.SelectAllCommand,
Source={StaticResource DataContextProxy}}"
IsChecked="{Binding
Data.AreAllSelected,
Mode=OneWay,
Source={StaticResource DataContextProxy},
UpdateSourceTrigger=PropertyChanged}"
IsThreeState="True" />
</DataTemplate>
</DataGridTemplateColumn.HeaderTemplate>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox
IsChecked="{Binding
Path=IsSelected,
UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
Note that there needs to be a binding to both the check box's IsChecked dependency property and its Command property and the IsChecked binding is OneWay. The IsChecked binding gets the check box to display the current state of the items and the Command binding performs the bulk selection. You need both.
Now in the view model:
public bool? AreAllSelected
{
get
{
return this.Items.All(candidate => candidate.IsSelected)
? true
: this.Items.All(candidate => !candidate.IsSelected)
? (bool?)false
: null;
}
set
{
if (value != null)
{
foreach (var item in this.Items)
{
item.IsSelected = value.Value;
}
}
this.RaisePropertyChanged();
}
}
And the SelectAllCommand property is an implementation of ICommand where the Execute method is:
public void Execute(object parameter)
{
var allSelected = this.AreAllSelected;
switch (allSelected)
{
case true:
this.AreAllSelected = false;
break;
case false:
case null:
this.AreAllSelected = true;
break;
}
}
Finally your row item view models (i.e. the things in Items) need to raise PropertyChanged on the main view model each time the value of IsSelected changes. How you do that is pretty much up to you.

Categories