I need to implementation auto start begin edit cell in the same row and next column, after click in previous column.
In begin my table, code works correctly. But in the last row and in penultimate column code doesn't work correctly, it begin start edit in the last row and in third column.
But after how I write value by myself, code start to work correctly. I use telerik framework.
Thank you for your responses!
c#
var element = sender as FrameworkElement;
ConditionsRadGridView.CommitEdit();
var parentCell = element.GetVisualParent<GridViewCell>();
if (parentCell != null)
{
var index = ConditionsRadGridView.Columns.IndexOf(parentCell.Column);
if (index != -1 && index < parentCell.ParentRow.Cells.Count)
{
var nextCell = parentCell.ParentRow.Cells[index] as GridViewCell;
if (nextCell != null)
{
ConditionsRadGridView.CurrentCellInfo = new GridViewCellInfo(nextCell);
ConditionsRadGridView.BeginEdit();
}
}
}
part of wpf
<telerik:RadGridView ValidatesOnDataErrors="InViewMode"
Name="ConditionsRadGridView"
Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2"
Margin="6 3 6 6" HorizontalAlignment="Left"
RowIndicatorVisibility="Collapsed" AutoGenerateColumns="False"
CanUserDeleteRows="False" CanUserInsertRows="False" ShowGroupPanel="False" CanUserReorderColumns="False"
FrozenColumnCount="1"
CanUserFreezeColumns="False"
ItemsSource="{Binding Conditions}" EnableColumnVirtualization="False"
RowStyle="{StaticResource ValidationFixStyle}"
SelectionMode="Extended"
cal:Message.Attach="[Event PreviewKeyUp] = [Action ConditionsKeyUp($this.SelectedItems, $eventArgs)]">
<telerik:RadGridView.Columns>
<telerik:GridViewDataColumn CellStyle="{StaticResource SelectedCellStyle}" Header="{x:Static properties:Resources.Station}" DataMemberBinding="{Binding Name, Mode=TwoWay}"
CellTemplateSelector="{StaticResource ConditionNameTemplateSelector}"/>
<telerik:GridViewColumn IsVisible="{Binding IsHaveGroups}">
<telerik:GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox Visibility="{Binding IsGroup, Converter={StaticResource BoolToVisibilityConverter}}" IsChecked="{Binding IsSplitUp, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
cal:Message.Attach="[Event Click] = [Action IsSplitUpClick($datacontext)]" />
</DataTemplate>
</telerik:GridViewColumn.CellTemplate>
</telerik:GridViewColumn>
<telerik:GridViewDataColumn Header="{x:Static properties:Resources.AffinityOperator}" ColumnGroupName="Affinity"
DataMemberBinding="{Binding AffinityOperator, UpdateSourceTrigger=PropertyChanged}" TextAlignment="Center" IsReadOnly="True">
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate DataType="{x:Type vm:StationConditionViewModel}">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<controls3:RadComboBox IsEnabled="{Binding IsRowEnabled}" SelectionChanged="StartEditNextCell"
SelectedItem="{Binding AffinityOperator, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Operators}" VerticalAlignment="Center" HorizontalAlignment="Stretch"/>
</Grid>
</DataTemplate>
</telerik:GridViewDataColumn.CellTemplate>
</telerik:GridViewDataColumn>
<telerik:GridViewDataColumn Header="{x:Static properties:Resources.AffinityValue}" ColumnGroupName="Affinity" IsReadOnlyBinding="{Binding AffinityIsReadOnly}"
DataMemberBinding="{Binding Path=AffinityValue}" TextAlignment="Right">
<telerik:GridViewDataColumn.CellTemplate>
<DataTemplate DataType="{x:Type vm:StationConditionViewModel}">
<TextBlock Text="{Binding AffinityValue, StringFormat={}{0:N2}}" Visibility="{Binding AffinityVisibility}" VerticalAlignment="Center" HorizontalAlignment="Right" />
</DataTemplate>
</telerik:GridViewDataColumn.CellTemplate>
</telerik:GridViewDataColumn>
Related
I have 2 classes for my 2 Tables(User_Tbl.cs and RegFee_Tbl.cs),and my classes connected together with foreign key(above picture),here is(below pictures) my property of my RegFee_Tbl.sc and my User_Tbl.cs
and there is my datagrid(memebersDatagride is name of datagrid),I Bound 3 column of this datagrid for import data from RegFee_Tbl.cs and i bound 1 column of this datagrid to import data from User_Tbl.cs.and in my mainWindows.cs i use this method to load item from database to my datagrid,but i dont know how to import data from 2 different tables(tables connected together with FK) to a datagrid
that's my method for load item from my tables(MainWindows.cs):
public partial class Dashboard : UserControl
{
GymDbEntities db = new GymDbEntities();
public static DataGrid datagrid;
public Dashboard()
{
InitializeComponent();
Load();
}
private void Load()
{
membersdataGrid.ItemsSource = db.User_Tbl.ToList();
//membersdataGrid.ItemsSource = db.RegFee_Tbl.ToList();
datagrid = membersdataGrid;
}
and there is my datagrid in xaml:
<DataGrid Language="fa-IR" x:Name="membersdataGrid" RowStyle="{DynamicResource DataGridRowStyle1}" ColumnHeaderStyle="{DynamicResource DataGridColumnHeaderStyle1}" CellStyle="{DynamicResource DataGridCellStyle1}" Style="{DynamicResource DataGridStyle1}">
<DataGrid.Columns>
<!--bara eyjad sotone checkbox-->
<DataGridCheckBoxColumn CanUserResize="False" Width="auto" ElementStyle="{StaticResource CheckBoxStyle1}">
<DataGridCheckBoxColumn.HeaderTemplate>
<DataTemplate>
<CheckBox Style="{StaticResource CheckBoxStyle1}" />
</DataTemplate>
</DataGridCheckBoxColumn.HeaderTemplate>
</DataGridCheckBoxColumn>
<DataGridTextColumn Header="#" IsReadOnly="True" CanUserReorder="False" Width="auto" Binding="{Binding ID}"/>
<DataGridTemplateColumn Header="اعضا" IsReadOnly="True" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Border Width="25" Height="25" CornerRadius="50" Margin="0 0 10 0" Background="{Binding BgColor}">
<TextBlock Text="{Binding Character}" VerticalAlignment="Center" HorizontalAlignment="Center" Foreground="White" Margin="0 0 0 1"/>
</Border>
<TextBlock Text="{Binding Name}" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="نام پدر" IsReadOnly="True" Width="*" Binding="{Binding Fee}"/>
<DataGridTextColumn Header="شماره تماس" IsReadOnly="True" Width="*" Binding="{Binding NumberPhone}"/>
<DataGridTextColumn Header="آخرین واریزی" IsReadOnly="True" Width="*" Binding="{Binding FatherName}" />
<DataGridTemplateColumn Header="عملیات" IsReadOnly="True" Width="auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Button x:Name="GridEditBtn" Style="{StaticResource gridEditbutton}">
<Icon:PackIconMaterial Kind="PencilBoxOutline" Style="{StaticResource gridButtonIcon}"/>
</Button>
<Button x:Name="GridRemoveBtn" Click="GridRemoveBtn_Click" Style="{StaticResource gridRemovebutton}" Margin="5 0 0 0" >
<Icon:PackIconMaterial Kind="DeleteOutline" Style="{StaticResource gridButtonIcon}" />
</Button>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
You are using Entity. So you have a Context (db) and each table is a list object. See code below.
GymDbEntities db = new GymDbEntities();
var results = (from u in db.User_Tbl
join r in db.RegFee_Tbl on u.NavigationProperties equal r.NavigationProperties
join s in sysdiagram on u.NavigationProperties equals s.NavigationProperties
select new { u = u, r = r, s = s}
).ToList();
I have this form.
Summary
On selected record from Datagrid, the right form will fill the TextBoxes.
Problem
Notice this:
When I untick the checkbox, the DataGrid gets updated too..
The DataGrid should only be updated on Click of a button.
Question
How do I remove the update between the DataGrid and the form? So instead, if I untick the box, the value remains as the original value. I do not need this automatic update.
XAML
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1.5*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<!--DataGrid View for record selection-->
<Border BorderBrush="Black" BorderThickness="1" Grid.Column="0">
<StackPanel>
<DataGrid x:Name="FSQMGrid"
ItemsSource="{Binding FSQMRecords}"
IsReadOnly="True"
AutoGenerateColumns="False"
Style="{StaticResource MaterialDesignDataGrid}"
>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Id}" Header="Id" Visibility="Hidden"/>
<DataGridTextColumn Binding="{Binding DocumentReference}" Header="Document Reference"/>
<DataGridTextColumn Binding="{Binding DocumentTitle}" Header="Document Title"/>
<DataGridTextColumn Binding="{Binding Path}" Header="Path" Visibility="Hidden"/>
<DataGridTextColumn Binding="{Binding IssueNumber}" Header="Issue Number"/>
<DataGridTextColumn Binding="{Binding IssueDate}" Header="Issue Date"/>
<DataGridTextColumn Binding="{Binding NextReviewDate}" Header="Next Review Date"/>
<DataGridTextColumn Binding="{Binding Archived}" Header="Archived"/>
<DataGridTextColumn Binding="{Binding UserIDModified}" Header="User ID Modified" Visibility="Hidden"/>
<DataGridTextColumn Binding="{Binding UsernameModified}" Header="User Modified"/>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
</Border>
<!--Editing Data-->
<StackPanel Grid.Column="1">
<materialDesign:Card Padding="20"
Margin="0 0 0 0">
<StackPanel>
<TextBlock Margin="16 16 12 8"
FontSize="16">Selected Record</TextBlock>
<Separator Style="{StaticResource MaterialDesignLightSeparator}" Background="LightGray"/>
<CheckBox Content="Archived"
IsChecked="{Binding ElementName=FSQMGrid, Path=SelectedItem.Archived}"
Style="{StaticResource MaterialDesignAccentCheckBox}"
Margin="0 0 0 0"/>
<StackPanel Margin="0 10 0 0">
<TextBox materialDesign:HintAssist.Hint="Document Reference"
Text="{Binding ElementName=FSQMGrid, Path=SelectedItem.DocumentReference, Mode=TwoWay}"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"/>
<TextBox materialDesign:HintAssist.Hint="Document Title"
Text="{Binding ElementName=FSQMGrid, Path=SelectedItem.DocumentTitle, Mode=TwoWay}"
Margin="0 10 0 0"
Style="{StaticResource MaterialDesignFloatingHintTextBox}" />
<TextBox materialDesign:HintAssist.Hint="Section"
Text="{Binding ElementName=FSQMGrid, Path=SelectedItem.SectionNumber, Mode=TwoWay}"
Margin="0 10 0 0"
Style="{StaticResource MaterialDesignFloatingHintTextBox}" />
<TextBox materialDesign:HintAssist.Hint="Issue Number"
Text="{Binding ElementName=FSQMGrid, Path=SelectedItem.IssueNumber, Mode=TwoWay}"
Margin="0 10 0 0"
Style="{StaticResource MaterialDesignFloatingHintTextBox}" />
<DatePicker materialDesign:HintAssist.Hint="Issue Date"
Text="{Binding ElementName=FSQMGrid, Path=SelectedItem.IssueDate, Mode=TwoWay}"
Margin="0 10 0 0"
Style="{StaticResource MaterialDesignFloatingHintDatePicker}"/>
<DatePicker materialDesign:HintAssist.Hint="Review Date"
Text="{Binding ElementName=FSQMGrid, Path=SelectedItem.NextReviewDate, Mode=TwoWay}"
Margin="0 10 0 0"
Style="{StaticResource MaterialDesignFloatingHintDatePicker}"/>
<Button Command="{}"
Content="Save Information"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="50,20,0,0"/>
</StackPanel>
</StackPanel>
</materialDesign:Card>
</StackPanel>
</Grid>
You are editing the same property of the same object that you see in the DataGrid so the data is indeed updated regardless of whether you click the Button.
You could consider to implement the IEditableObject interface in your data object class. It provides functionality to commit or rollback changes to an object that is used as a data source.
The other option would be to edit a copy of the object that you bind to in the DataGrid and then update the latter when when the button is actually clicked.
The binding source in the Datagrid and the binding source of the selectedRecord are the same object that implements the inotifyPropertyChanged interface.So, if you update this object anywhere, it's going to synchronize on the view.
This is a button-click updated solution written in the PRISM framework.
like this:
public class DataGridViewModel:ViewModel
{
public ObservableCollection<DataGridRowItem> Source { get; }
public ICommand SelectedCommand =>new DelegateCommand<DataGridRowItem>(item =>
{
var clone = new DataGridRowItem()
{
Archived = item.Archived
};
var vm = new SelectedRecordViewModel()
{
Data = clone,
SaveCommand = new DelegateCommand(() =>
{
item.Archived = clone.Archived;
})
};
var selectedRecord = new Window();
selectedRecord.DataContext = vm;
selectedRecord.Show();
});
}
public class SelectedRecordViewModel : ViewModel
{
public DataGridRowItem Data { get; set; }
public ICommand SaveCommand { get; set; }
}
public class DataGridRowItem : ViewModel
{
private bool archived;
public bool Archived
{
get => archived;
set => this.SetValue(ref archived, value);
}
}
ps:The solution mentioned by #mm8 is better
I am new to WPF and got issues in adding new Row to Data-Grid on button click. I have tried few solutions but was not successful.
Tried-
If i enable "CanUserAddRows="True", This gives me buttons also which are present in upper rows. Also i dont want this approach as i want t add new row on button click.
I am attaching an image of how new row looks like if i use "CanUserAddRows="True"
<StackPanel Name="Decrypt_Vault">
<DataGrid x:Name="gdDecryptVault" HorizontalAlignment="Left" ColumnWidth="Auto" Margin="10,10,405,18" AutoGenerateColumns="False" HorizontalGridLinesBrush="LightGray" VerticalGridLinesBrush="LightGray">
<DataGrid.Columns>
<!--<DataGridTextColumn Header="Host" Binding="{Binding Path=Host}" Width="*" IsReadOnly="True" />-->
<DataGridTemplateColumn Header="Host">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Host}"></TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Background="Aquamarine" Text="{Binding Path=Host, Mode=TwoWay, UpdateSourceTrigger=Explicit}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<!--<DataGridTextColumn Header="Login" Binding="{Binding Path=Login}" Width="*" IsReadOnly="True" />-->
<DataGridTemplateColumn Header="Login">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Login}"></TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Background="Aquamarine" Text="{Binding Path=Login, Mode=TwoWay, UpdateSourceTrigger=Explicit}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<!--<DataGridTextColumn Header="Password" Binding="{Binding Path=Password}" Width="*" IsReadOnly="True"/>-->
<DataGridTemplateColumn Header="Password" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Name="LinePassword" Text="{Binding Path=Password}" Visibility="{Binding Path=IsPasswordVisible, Converter={StaticResource BoolToVis}}"></TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Background="Aquamarine" Text="{Binding Path=Password, Mode=TwoWay, UpdateSourceTrigger=Explicit}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Password Actions" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel>
<Button Click="bTogglePassword_Click" ToolTip="Toggle Password"
Name="bTogglePassword" Visibility="Visible" Height="30" Width="30" >
<Image Source="/Images/button_login1.png" Stretch="Fill" Height="30" Width="30"/>
</Button>
<Button Click="bCopyToClipBoard_Click" ToolTip="Copy Password to clipboard"
Name="bCopyToClipBoard" Visibility="Visible" Height="30" Width="30" >
<Image Source="/Images/Copy_icon.png" Stretch="Fill" Height="30" Width="30"/>
</Button>
</DockPanel>
<!--<TextBlock Text="{Binding Path=Password}"></TextBlock>-->
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<!--<DataGridTextColumn Header="Items" Binding="{Binding Path=Project.Name}" Width="*" IsReadOnly="True"/>-->
<DataGridTemplateColumn Header="Items" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Project.Name}"></TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Background="Aquamarine" Text="{Binding Path=Project.Name, Mode=TwoWay, UpdateSourceTrigger=Explicit}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<!--<DataGridTextColumn Header="Created by" Binding="{Binding Path=CreatedBy}" Width="*" IsReadOnly="True"/>-->
<DataGridTemplateColumn Header="Created By" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=CreatedBy}"></TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Background="Aquamarine" Text="{Binding Path=CreatedBy, Mode=TwoWay, UpdateSourceTrigger=Explicit}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<!--<DataGridTextColumn Header="Notes" Binding="{Binding Path=Notes}" Width="*" IsReadOnly="True"/>-->
<DataGridTemplateColumn Header="Notes" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=Notes}"></TextBlock>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Background="Aquamarine" Text="{Binding Path=Notes, Mode=TwoWay, UpdateSourceTrigger=Explicit}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Actions" Width="100">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<DockPanel>
<!--<Button Content="Edit" Click="EditVaultLine"/>-->
<Button Click="EditVaultLine" ToolTip="Edit"
Name="Edit" Visibility="Visible" Height="30" Width="30" >
<Image Source="/Images/edit.png" Stretch="Fill" Height="30" Width="30"/>
</Button>
<Button Click="DeleteVaultLine" ToolTip="Delete"
Name="Delete" Visibility="Visible" Height="30" Width="30" >
<Image Source="/Images/delete.png" Stretch="Fill" Height="30" Width="30"/>
</Button>
</DockPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<DockPanel >
<Button Click="SaveEditedVaultLine" ToolTip="Save"
Name="Save" Visibility="Visible" Height="30" Width="30" >
<Image Source="/Images/save2.png" Stretch="Fill" Height="30" Width="30"/>
</Button>
<Button Click="CancelEditVaultLine" ToolTip="Cancel"
Name="Cancel" Visibility="Visible" Height="30" Width="30" >
<Image Source="/Images/erase.png" Stretch="Fill" Height="30" Width="30"/>
</Button>
</DockPanel>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</StackPanel>
My .CS code to Bind data in Data-Grid
var result = weekTaskView.getVaultRecordLines();
List<VaultRecordLine> list = new List<VaultRecordLine>();
foreach (var item in result.Entities)
{
VaultRecordLine vrl = new VaultRecordLine();
if (item.Attributes.Contains("createdby"))
{
vrl.CreatedBy = item.Attributes["createdby"].ToString();
}
if (item.Attributes.Contains("new_account"))
{
vrl.Host = item.Attributes["new_account"].ToString();
}
if (item.Attributes.Contains("new_login"))
{
vrl.Login = item.Attributes["new_login"].ToString();
}
if (item.Attributes.Contains("new_password"))
{
vrl.Password = item.Attributes["new_password"].ToString();
}
if (item.Attributes.Contains("new_vaultid"))
{
vrl.Id = new Guid(item.Attributes["new_vaultid"].ToString());
}
list.Add(vrl);
}
gdDecryptVault.ItemsSource = list;
Requirement- When i click 'Add New Row button', i need a new row in EDITABLE mode so that user can fill up data. Also i need 'Save' and 'Cancel 'buttons at the end of row to save that data or to 'Cancel' it respectively.
I've made a small example-app to show you how you can add items to your DataGrid.
Your DataGrid should be bound to an ObservableCollection<T> where T is the type of your DataClass. The definition of your DataGrid then looks something like:
<DataGrid AutoGenerateColumns="False" ItemsSource="{Binding Items}" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Host" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center" Margin="4,2" Text="{Binding Host, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Login" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center" Margin="4,2" Text="{Binding Login, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Password" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock VerticalAlignment="Center" Margin="4,2" Text="{Binding Password, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Then you have a Button to add new rows to the ItemsSource of the DataGrid. The Button can look like:
<Button Grid.Row="1" Content="Add item" Command="{Binding AddItemCommand}"/>
In your c#-Code the AddItemCommand where the Command of your Button is bound to looks like:
private ICommand addItemCommand;
public ICommand AddItemCommand
{
get { return addItemCommand ?? (addItemCommand = new RelayCommand(AddItem)); }
}
private void AddItem(object obj)
{
Items.Add(new MyItem());
}
And the ObservableCollection<T> where the DataGrid is bound to looks like:
private ObservableCollection<MyItem> items;
public ObservableCollection<MyItem> Items
{
get { return items ?? (items = new ObservableCollection<MyItem>()); }
}
So now when you press the Button the AddItemCommand will be executed and this is adding a new Item to the ItemsSource of the DataGrid.
RelayCommand is an implementation of the ICommand-interface. So you don't have to use click-events. So you can uncouple your XAML-Definitions (View) from your C#-Logic (ViewModel). This is one important step to work with the MVVM-Pattern.
public class RelayCommand : ICommand
{
private readonly Action<object> execute;
private readonly Predicate<object> canExecute;
public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
{
if(execute == null)
throw new ArgumentException(nameof(execute));
this.execute = execute;
this.canExecute = canExecute;
}
public bool CanExecute(object parameter)
{
return canExecute == null || canExecute(parameter);
}
public void Execute(object parameter)
{
execute(parameter);
}
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
}
If you're new to WPF you should take a look at the MVVM-Pattern. It's really useful
I have a DataGrid as follows:
<DataGrid CanUserAddRows="True" CanUserReorderColumns="False" CanUserSortColumns="False" CanUserDeleteRows="True"
ItemsSource="{Binding Groups}" AutoGenerateColumns="False">
<DataGrid.InputBindings>
<KeyBinding Key="Enter" Command="{Binding DataContext.NewRowCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Page}}}"/>
</DataGrid.InputBindings>
<DataGrid.Resources>
<CompositeCollection x:Key="Items">
<ComboBoxItem IsEnabled="False" Background="#FF2A2A2A" Foreground="White">
<Grid TextElement.FontWeight="Bold" >
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="A" />
<ColumnDefinition Width="50" />
<ColumnDefinition SharedSizeGroup="B" />
</Grid.ColumnDefinitions>
<Grid.Children>
<TextBlock Grid.Column="0" Text="Group Name" />
<TextBlock Grid.Column="2" Text="Effect" />
</Grid.Children>
</Grid>
</ComboBoxItem>
<CollectionContainer Collection="{Binding Source={StaticResource GroupNamesWithCorrespondingEffectsCollection}}" />
</CompositeCollection>
<DataTemplate DataType="{x:Type helpers:GroupNameWithCorrespondingEffect}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition SharedSizeGroup="A" />
<ColumnDefinition Width="50" />
<ColumnDefinition SharedSizeGroup="B" />
</Grid.ColumnDefinitions>
<Grid.Children>
<TextBlock Grid.Column="0" Text="{Binding GroupName}" />
<TextBlock Grid.Column="2" Text="{Binding CorrespondingEffect}" />
</Grid.Children>
</Grid>
</DataTemplate>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTemplateColumn Header="Name" Width="2*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding GroupName}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Group" Width="2*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{DynamicResource Items}"
SelectedValue="{Binding ParentID}"
SelectedValuePath="GroupID" Grid.IsSharedSizeScope="True" TextSearch.TextPath="GroupName">
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Effect" Width="*" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding DataContext.Effects, RelativeSource={RelativeSource AncestorType={x:Type Page}}}" DisplayMemberPath="Effect"
SelectedValue="{Binding EffectID}" SelectedValuePath="EffectID"
Visibility="{Binding Path=DataContext.SelectedGroupID,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Page}},
Converter={StaticResource effectsVisibilityConverter}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Here is my GroupsViewModel to which my Page's DataContext is Bound :
public class GroupsViewModel : ViewModelBase
{
public GroupsViewModel()
{
Groups = new ObservableCollection<Group>();
NewRowCommand = new RelayCommand(NewRow);
using (Entities db = new Entities())
{
List<GroupNameWithCorrespondingEffect> _GroupNamesWithCorrespondingEffects = (
from g in db.Groups
select new GroupNameWithCorrespondingEffect
{
GroupID = g.GroupID,
GroupName = g.GroupName,
CorrespondingEffect = g.Master_Effects.Effect
}
).ToList();
GroupNamesWithCorrespondingEffects
= new ObservableCollection<GroupNameWithCorrespondingEffect>(
_GroupNamesWithCorrespondingEffects.Where
(
u => !StaticMethods.GetAllChildren(25)
.Select(x => x.GroupID)
.Contains(u.GroupID)
).ToList()
);
Effects = new ObservableCollection<Master_Effects>(from m in db.Master_Effects
select m);
}
}
private ObservableCollection<Group> _groups;
public ObservableCollection<Group> Groups
{
get
{
return _groups;
}
set
{
_groups = value;
OnPropertyChanged("Groups");
}
}
public ICommand NewRowCommand { get; set; }
private void NewRow(object obj)
{
Groups.Add(new Group());
}
}
Problems :
I enter some data in the datagrid and then Press Enter a new row to the datagrid is added, which is expected. But new row is added to the top of the DataGrid instead I expect it to be added at last position. Also the data in other rows is cleared but I expect it to be as it is.
Is CanUserAddRows causing some confusion?
"When this property is set to true a blank row is displayed at the bottom of the DataGrid."
This row will always be underneath the rows provided by the ObservableCollection. I put some dummy data into NewRole like this:
var p = new Person() {Name = "New " + DateTime.Now.TimeOfDay.TotalMilliseconds};
People.Add(p);
to make the result a bit clearer. When you add a few rows the largest value of TotalMilliseconds will be at the end of the collection and will be the penultimate row in the DataGrid.
The culprit is your KeyBinding. According to your sample project, you never save the value that's in DataGrid back in the collection when you press Enter. It normally happens on lost focus, but since your Enter adds a row before focus is lost, you add a blank row into the empty collection.
DataGrid detects the change and updates the view with this new row, while preserving your changes that are in flight (the row where you pressed Enter hasn't finished updating). The result is obviously what you see.
I'm not sure why you're using Enter as KeyBinding to add row, when this should happen by itself? If it isn't happening it is because DataGrid isn't able to create your model (perhaps it's not public? or may be you don't have default parameterless ctor defined?)
For your sample project, I removed your KeyBinding & implemented INotifyChanged on your Person model and it works.
If you're using CellTemplates, you need to implement CellEditingTemplate as well.
<DataGrid CanUserAddRows="True" CanUserDeleteRows="True" CanUserReorderColumns="False" CanUserResizeColumns="False" AutoGenerateColumns="False" ItemsSource="{Binding People}">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Name" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Name}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Age" Width="*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Age}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding Age}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
I had a listview with validation fine but I wanted to changed the look of the textbox that was inside of listview so I changed it to datagrid. After I changed it the validation got screwed up. Whenever a automatic name is added, I got the error message says the name is already existed even though it is not. Why was it fine with listview? Any input is welcome. Here is the code;
Listview which the validation is fine:
<ListView Name="_regionQueryListBox" Width="122"
HorizontalAlignment="Left" VerticalAlignment="Stretch"
DataContext="{Binding}" IsSynchronizedWithCurrentItem="True"
Style="{StaticResource ListViewRegionSelectorStyle}"
ItemsSource="{Binding Path=Model}">
<ListView.View>
<GridView>
<GridViewColumn Header="Region"
Width="{Binding Path=ActualWidth, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListView}}">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBox HorizontalAlignment="Left" VerticalAlignment="Stretch"
Text="{Binding Path=RegionName}">
</TextBox>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Datagrid, validation doesn't work:
<DataGrid x:Name="_regionQueryListBox" HorizontalAlignment="Left" VerticalAlignment="Stretch"
AutoGenerateColumns="False"
AlternatingRowBackground="Silver" AlternationCount="2"
CanUserReorderColumns="False" CanUserResizeColumns="False" CanUserSortColumns="False"
CanUserAddRows="False" CanUserDeleteRows="False" CanUserResizeRows="False"
VerticalScrollBarVisibility="Visible" HorizontalScrollBarVisibility="Disabled"
SelectionMode="Single"
DataContext="{Binding}"
ItemsSource="{Binding Path=Model}" >
<DataGrid.Columns>
<DataGridTemplateColumn Header="Region" Width="110" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBox Text="{Binding Path=RegionName}"
TextChanged="regionTextBox_TextChanged" >
</TextBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Validation:
private void regionTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
{
TextBox textBox = sender as TextBox;
if (textBox != null)
{
string name = textBox.Text;
StringBuilder errorMessage = null;
RegionQueryViewModel queryViewModel = DataContext as RegionQueryViewModel;
if (queryViewModel.Model.Any(q => q.RegionName == name))
{
errorMessage = new StringBuilder();
errorMessage.AppendLine(string.Format("{0} already exists in the list.", name));
}
if (errorMessage != null)
{
MessageBox.Show(errorMessage.ToString(), "Item Already Exists");
name = string.Empty;
//RegionName = name;
return;
}
}
}
I'm guessing that the default updatesourcetrigger=lostfocus of the datagrid messes up your validation.
<DataTemplate>
<TextBox Text="{Binding Path=RegionName, UpdateSourceTrigger=PropertyChanged}"
TextChanged="regionTextBox_TextChanged" />
</DataTemplate>