i am new to wpf, i have datagrid as follows,
<DataGrid Grid.Row="0" x:Name="dg1" Grid.Column="0" SelectionChanged="DataGrid_SelectionChanged" ItemsSource="{Binding Path=Articles}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridTextColumn Header="Description" Binding="{Binding Path=Description}" />
</DataGrid.Columns>
</DataGrid>
ViewModel has property
public IEnumerable<Article> Articles
{
get
{
return _ArticleList;
}
}
I am not able to get the selected item, following code returns error.
Unable to cast object of type 'MS.Internal.NamedObject' to type 'Article'.
private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Article Article = (Article)this.dg1.SelectedItems;
}
Please any suggestions how to implement the same??
EDIT:
DataGrid.SelectedItems is an IList. if you want just the Selected One use DataGrid.SelectedItem <-- without s :)
if you want to set the SelectedItem via Binding:
public Article SelectedArticle
{
set
{
this._selectedArticle= value;
OnPropertyChanged("SelectedArticle");
}
get
{
return _selectedArticle;
}
}
XAml
<DataGrid SelectedItem="{Binding SelectedArticle, Mode=OneWayToSource}" />
or as CommandParameter for Button with ICommand
<Button Command="{Binding EditDataCommand}" CommandParameter="{Binding ElementName=MyGridCtrl, Path=MyDataGrid.SelectedItem}"/>
or all SelectedItems for Button with ICommand
<Button Command="{Binding DeleteDataCommand}" CommandParameter="{Binding ElementName=MyGridCtrl, Path=MyDataGrid.SelectedItems}" >
You need to check if items are selected:
private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(this.dg1.SelectedItems.Count > 0){
Article article = (Article)this.dg1.SelectedItems;
}
}
Hope this works...
Article _article =
((dgReferences.SelectedItem as
DataGridRow).Item as Article);
Regards
ArunDhaJ
Related
I use Syncfusion in my WPF project. The following is a DataGrid with multiple selection modes:
<grid:SfDataGrid x:Name="dataGrid"
SelectionMode="Multiple"
Grid.Row="0"
SelectedItems="{Binding SelectedRows}"
BorderThickness="1"
NavigationMode="Row"
BorderBrush="Black"
CellStyle="{StaticResource ResourceKey=cellStyle}"
AutoGenerateColumns="False"
HeaderStyle="{StaticResource ResourceKey=headerStyle}"
ColumnSizer="AutoLastColumnFill"
ItemsSource="{Binding PagedSource, ElementName=dataPager}">
<grid:SfDataGrid.Columns>
<grid:GridCheckBoxSelectorColumn MappingName="SelectorColumn"
Width="30" />
<grid:GridTextColumn AllowDragging="False"
HeaderText="Id"
MappingName="EquipmentId"
IsHidden="True" />
<grid:GridTextColumn AllowDragging="False"
HeaderText="Code"
MappingName="EquipmentCode" />
<grid:GridTextColumn AllowDragging="False"
HeaderText="Title"
MappingName="EquipmentTitle" />
</grid:SfDataGrid.Columns>
</grid:SfDataGrid>
I want to get a list of selected rows (their Ids that exist in the first hidden column) in the ViewModel. How can I achieve this?
Note: I use Microsoft.Toolkit.MVVM in my project.
i dont know if it helps but i know how to only get the selected item by using
this in xaml side
<grid:SfDataGrid x:Name="dataGrid" SelectionChanged="GetSelected">
this is in c# side
private void GetSelected(object sender, SelectionChangedEventArgs e)
{
Object selectedItem = dataGrid.SelectedItem as Object;
}
I used SelectedItems="{Binding SelectedItems, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" in the XAML code and then, I used the following property in the ViewModel:
public class EquipmentIdentitySettingViewModel: ObservableObject {
//...Some codes here
private ObservableCollection < object > _selectedItems;
public ObservableCollection < object > SelectedItems {
get {
return _selectedItems;
}
set {
SetProperty(ref _selectedItems, value);
}
}
public ICommand ApplyCommand {
get;
}
public EquipmentIdentitySettingViewModel() {
ApplyCommand = new RelayCommand(() => ApplyChanges());
}
private void ApplyChanges() {
if (SelectedItems != null) {
foreach(Equipment item in SelectedItems) {
MessageBox.Show(item.EquipmentCode.ToString());
}
}
}
}
The problem solved.
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}" />
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.
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.
I have a WPF ComboBox:
<ComboBox ... ItemsSource="{Binding Source={StaticResource viewModel}, Path=getItems, Mode=OneTime}" x:Name="combobox" SelectionChanged="combobox_SelectionChanged">
...
</ComboBox>
with lots of items.
And my ViewModel class:
public class ViewModel
{
private readonly ObservableCollection<ObjectA> _objectACollection= new ObservableCollection<ObjectA>();
public ViewModel()
{
_objectACollection.Add(new ObjectA("Text 1", "Text", "Text"));
_objectACollection.Add(new ObjectA("Text 2", "Text", "Text"));
_objectACollection.Add(new ObjectA("Text 3", "Text", "Text"));
}
public void combobox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Trace.WriteLine(combobox.SelectedIndex);
}
public ObservableCollection<ObjectA> getItems
{
get { return _objectACollection; }
}
}
and the selectionChanged listener:
private void combobox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
Trace.WriteLine(combobox.SelectedIndex);
}
The ComboBox is displayed and when I choose something I get the index of the collection objects.
But is there any way to return me the object? for example:
I select the first element in the ComboBox(index 0),
how can I get (in the combobox_SelectionChanged listener) the object from the _objectACollection with index 0?
There is SelectedItem property of ComboBox. I think that you can bind SelectedItem with TwoWay with your VM. Following is exmaple. I hope that this help.
<ComboBox ... ItemsSource="{Binding Source={StaticResource viewModel}, Path=getItems, Mode=OneTime}" x:Name="combobox" SelectedItem="{Binding SelectedObjectA, Mode=TwoWay}">
...
</ComboBox>
You should add SelectedObjectA property in your VM. You can get selected item from VM.SelectedObjectA property.
private ObjectA _SelectedObjectA;
public ObjectA SelectedObjectA
{
get
{
return _SelectedObjectA;
}
set
{
if (_SelectedObjectA == value)
return;
_SelectedObjectA = value;
// Notifu changed here
}
}
You can use combobox.SelectedItem.
Maybe you could try using a collection that implements ICollectionView interface, I am sure there are a few baked in. It keeps track of the selected item in your collection for you without the need for a separate SelectedObjectA property on your viewmodel. So you can have:
> <ComboBox ... ItemsSource="{Binding Source={StaticResource viewModel},
> Path=**SomeICollectionView**, Mode=OneTime}" x:Name="combobox"> ...
> </ComboBox>
to get the selected item from the viewmodel class all you have to do is SomeICollectionView.CurrentItem