I have an ObservableCollection<MyType>, where MyType implements INotifyPropertyChanged using Fody.
This collection was binded to my ui as this
<Grid x:Name="DetallePresupuesto" Grid.Row="2">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<TextBlock Text="{x:Static resources:Labels.SALUD_DetallePresupuesto}" Background="Bisque" TextAlignment="Center"/>
<DataGrid
x:Name="DetallePresupuestoGrid"
Grid.Row="1"
Grid.ColumnSpan="1"
AutoGenerateColumns="False"
Style="{DynamicResource ParadigmaNDataGrid}"
ItemsSource="{Binding Path=DetallePresupuesto,UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand Command="{Binding ItemSelectionChangedCommand}" CommandParameter="{Binding ElementName=DetallePresupuestoGrid, Path=SelectedItem}"/>
</i:EventTrigger>
<i:EventTrigger EventName="AddingNewItem">
<cmd:EventToCommand Command="{Binding InsertItemCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.Columns>
<DataGridTemplateColumn Header="{x:Static resources:Labels.GENERAL_Item}" Width="10*" ClipboardContentBinding="{x:Null}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Merlin_ConceptosFacturacion.StrDescripcionConcepto}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<controls:Search SearchParms="{Binding ElementName=EstaVentana,Path=DataContext.BusqItems}"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="{x:Static resources:Labels.GENERAL_Cantidad}" Width="2*" Binding="{Binding Path=NumCantidad}" ClipboardContentBinding="{x:Null}"/>
<DataGridTextColumn Header="{x:Static resources:Labels.GENERAL_ValorUnitario}" Width="2.5*" Binding="{Binding Path=NumValorFacturacionDigitado}" ClipboardContentBinding="{x:Null}"/>
<DataGridTextColumn Header="{x:Static resources:Labels.GENERAL_ValorTotal}" Width="2.5*" Binding="{Binding Path=NumValor}" ClipboardContentBinding="{x:Null}"/>
<DataGridTemplateColumn Width="70" Header="{x:Static resources:Labels.GENERAL_BorrarItem}" HeaderStyle="{StaticResource ResourceKey=ParadigmaNDataGridHeaderStyle}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Style="{StaticResource InLineDeleteButton}" Command="{Binding Path=DataContext.DeleteCommand, ElementName=EstaVentana}" CommandParameter="{Binding ElementName=DetallePresupuestoGrid,Path=SelectedItem}" Visibility="{Binding Merlin_ConceptosFacturacion.NumIdConcepto,Converter={cnv:decimalToVisibilityConverter}}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
private ObservableCollection<MyType> _DetallePresupuesto;
public ObservableCollection<MyType> DetallePresupuesto
{
get {
if (_DocumentoPresupuesto == null)
return new ObservableCollection<MyType>();
else
return new ObservableCollection<MyType>(_DocumentoPresupuesto.Parent.MyType);
}
set { SetProperty(ref _DetallePresupuesto, value); }
}
And this command that deletes one row each time
public void ExecuteDelete(object p)
{
try
{
bool borro = false;
MyType det = (MyType)p;
MyType item = DetallePresupuesto.Where(x => x.NumIdTransaccion == det.NumIdTransaccion).FirstOrDefault();
if (item != null)
{
DetallePresupuesto.RemoveAt(DetallePresupuesto.IndexOf(item));
db.SetEntityState(det, EntityState.Deleted);
db.SaveEntity(det);
}
}
catch (Exception e)
{
ReportarInconsistencia(e.Message);
}
}
My problem is when delete an item from the collection id does it but it doesn't refresh the UI.
How can I Do to force de UI refresh ?
You're not binding to DetallePresupuesto in the UI. You are binding to either:
return new ObservableCollection<MyType>();
or
return new ObservableCollection<MyType>(_DocumentoPresupuesto.Parent.MyType);
For the UI to be updated you need to let the property return the list that you are removing from, i.e. the DetallePresupuesto. That means the getter should look something like this.
public ObservableCollection<MyType> DetallePresupuesto
{
get {
if (_DetallePresupuesto == null)
_DetallePresupuesto = new ObservableCollection<MyType>(); //populate here
return _DetallePresupuesto;
}
}
Also, try to avoid setters for your collections.
Related
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
In my DataGrid i have a column that contains an Image created like this:
<DataGridTemplateColumn x:Name="imgView" Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Source="/MyApplication;component/Resources/View.png" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
To handle events from my View in my View Model i am using the namespace:
xmlns:intr="http://schemas.microsoft.com/expression/2010/interactivity"
On a normal Image i can handle the event like this:
<Image Source="/MyApplication;component/Resources/View.png" HorizontalAlignment="Left" Width="150">
<intr:Interaction.Triggers>
<intr:EventTrigger EventName="MouseLeftButtonDown">
<intr:InvokeCommandAction Command="{Binding ViewImageMouseDownCommand}"/>
</intr:EventTrigger>
</intr:Interaction.Triggers>
</Image>
And it works just fine, yet when i add the same code to my Image in the DataGridTemplateColumn like this:
<DataGridTemplateColumn x:Name="imgViewCompany" Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Source="/MyApplication;component/Resources/View.png">
<intr:Interaction.Triggers>
<intr:EventTrigger EventName="MouseLeftButtonDown">
<intr:InvokeCommandAction Command="{Binding ViewImageMouseDownCommand}"/>
</intr:EventTrigger>
</intr:Interaction.Triggers>
</Image>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
It doesn't seem to work.
I have also tried a method i found here : https://www.codeproject.com/Tips/478643/Mouse-Event-Commands-for-MVVM
Which also didn't work on the Image in the DataGridTemplateColumn
My ViewModel :
class vmCompaniesList: vmBase, INotifyPropertyChanged
{
public DataView CompaniesListView { get; private set; }
private vwCompanyListDataTable CompaniesList { get; set; }
public RelayCommand<Image> ViewImageMouseDownCommand { get; private set; }
public vmCompaniesList()
{
ViewImageMouseDownCommand = new RelayCommand<Image>(ViewImageMouseDown);
using (vwCompanyListTableAdapter taCompanies = new vwCompanyListTableAdapter())
{
CompaniesList = taCompanies.GetData();
CompaniesListView = CompaniesList.DefaultView;
}
}
private void ViewImageMouseDown(object parameter)
{
MessageBox.Show("Click", "", MessageBoxButton.OK, MessageBoxImage.Exclamation);
}
}
So i am wondering how i can handle the event?
EDIT: So i have tried, according to the possible duplicate post, to add my Image to a Button and subscribing to the click but still no joy, i am wondering if it is to do with it being inside the DataGridTemplateColumn
This is what i tried :
<DataGridTemplateColumn x:Name="imgViewCompany" Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Command="{Binding ViewImageMouseDownCommand}">
<Image Source="/MyApplication;component/Resources/View.png" />
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
And:
<DataGridTemplateColumn x:Name="imgViewCompany" Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Command="{Binding ViewImageMouseDownCommand}">
<intr:Interaction.Triggers>
<intr:EventTrigger EventName="Click">
<intr:InvokeCommandAction Command="{Binding ViewImageMouseDownCommand}"/>
</intr:EventTrigger>
</intr:Interaction.Triggers>
<Image Source="/MyApplication;component/Resources/View.png" />
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
EDIT 2: Thanks to ASh this is what got it working:
<DataGridTemplateColumn x:Name="imgViewCompany" Width="Auto">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Source="/AlphaCureCRM(WPF);component/Resources/View.png">
<intr:Interaction.Triggers>
<intr:EventTrigger EventName="MouseLeftButtonDown">
<intr:InvokeCommandAction Command="{Binding Path=DataContext.ViewImageMouseDownCommand,
RelativeSource={RelativeSource AncestorType=DataGrid}}"/>
</intr:EventTrigger>
</intr:Interaction.Triggers>
</Image>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
MouseBinding should reduce markup required to bind command to click event. However the issue is probably incorrect command binding. ViewImageMouseDownCommand is not in the DataGridRow DataContext (single element of DataView, DataRowView). Try bind to DataGrid DataContext:
Command="{Binding Path=DataContext.ViewImageMouseDownCommand,
RelativeSource={RelativeSource AncestorType=DataGrid}}"
I need help with buttons in list view. I am creating custom form for SCSM. List view is working without problems, but I need add there Add button, Open button and Delete button. Add button working without problem, but other buttons do nothing. Can anybody help me with it?
Thanks
XAML code is here:
<ListView Name="ListViewMonitor" VerticalAlignment="Top" HorizontalAlignment="Stretch" Margin="10,0,00,0" MinHeight="100" Grid.Column="0" Grid.Row="1" ItemsSource="{Binding Path=CustomRelatedMonitor, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}">
<ListView.View>
<GridView>
<GridViewColumn Header="{Binding Path=Strings[ID].Value, FallbackValue=ID, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type views:FormView}}}" DisplayMemberBinding="{Binding Path=ID}" Width="Auto"/>
<GridViewColumn Header="{Binding Path=Strings[CustomAssetStatus].Value, FallbackValue=CustomAssetStatus, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type views:FormView}}}" Width="Auto">
<GridViewColumn.CellTemplate>
<DataTemplate>
<smcontrols:ListPicker Name="listPickerAssetStatus" HorizontalAlignment="Stretch" Width="Auto" ParentCategoryId="{Binding Source=12435943-EBA1-3E78-BB6C-A731E44EDCFD, Mode=OneWay}" SelectedItem="{Binding Path=CustomAssetStatus, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
<StackPanel Margin="10,0,10,0" Grid.Row="1" Grid.Column="1" VerticalAlignment="Top" HorizontalAlignment="Right">
<Button VerticalAlignment="Top" HorizontalAlignment="Right" Name="buttonAddMonitor" Height="20" Width="65" Content="{Binding Path=Strings[buttonAdd].value, FallbackValue=Add, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type views:FormView}}}" Click="buttonAddMonitor_Click" />
<Button VerticalAlignment="Top" HorizontalAlignment="Right" Margin="0,10,0,0" Name="buttonRemoveMonitor" Height="20" Width="65" IsEnabled="{Binding ElementName=ListViewMonitor, Path=SelectedItems.Count}" Content="{Binding Path=Strings[buttonRemove].value, FallbackValue=Remove, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type views:FormView}}}" Click="buttonRemoveMonitor_Click" />
<Button VerticalAlignment="Top" HorizontalAlignment="Right" Margin="0,10,0,0" Name="buttonOpenMonitor" Height="20" Width="65" IsEnabled="{Binding ElementName=ListViewMonitor, Path=SelectedItems.Count}" Content="{Binding Path=Strings[buttonOpen].value, FallbackValue=Open, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type views:FormView}}}" Click="buttonOpenMonitor_Click" />
</StackPanel>
Code for buttons:
public ListView ListViewAllMonitors { get; set; }
private ListView GetListView (string Name)
{
switch (Name)
{
case "buttonAddMonitor":
case "buttonRemoveMonitor":
case "buttonOpenMonitor":
{
return this.ListViewAllMonitors;
}
default:
{
throw new Exception(String.Format("ListView for control: {0}- not found", Name));
}
}
}
private void buttonAddMonitor_Click(object sender, RoutedEventArgs e)
{
ButtonAdd_Click(new Guid("DD3BDB96-BC9E-D46E-5847-BAB387039B36"), ListViewMonitor);
}
private void ButtonAdd_Click(Guid mpClassGuid, ItemsControl listView)
{
FormUtilities.Instance.LaunchAddInstancePickerDialog(listView.ItemsSource as Collection<IDataItem>, mpClassGuid);
}
private void buttonRemoveMonitor_Click(object sender, RoutedEventArgs e)
{
var button = (Button)sender;
var CustomListView = GetListView(button.Name);
if (CustomListView == null) return;
var list = (IList<IDataItem>)CustomListView.ItemsSource;
if (list == null) return;
foreach (IDataItem item in new ArrayList(CustomListView.SelectedItems))
{
list.Remove(item);
}
}
private void buttonOpenMonitor_Click(object sender, RoutedEventArgs e)
{
var button = (Button)sender;
var ListView = GetListView(button.Name);
if (ListView == null) return;
OpenInstance(ListView);
}
public void OpenInstance(ListView ListView)
{
if (ListView == null) return;
foreach (IDataItem dataItem in ListView.SelectedItems)
{
FormUtilities.Instance.PopoutForm(dataItem);
}
}
Ihave a textbox and a DataGrid. When I type in something in textbox, I want to filter the data in DataGrid.
I have done that with the below mentioned code:
In XAML:
<CollectionViewSource x:Key="GroupsViewSource" Source="{Binding Groups, UpdateSourceTrigger=PropertyChanged}" Filter="CollectionViewSource_Filter">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="GroupName"/>
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
<DataGrid Grid.Row="5" Grid.Column="1" ItemsSource="{Binding Source={StaticResource GroupsViewSource}}"
SelectedItem="{Binding SelectedGroup}"
AutoGenerateColumns="False" CanUserAddRows="False"
SelectionMode="Single" SelectionUnit="FullRow"
EnableRowVirtualization="False" VirtualizingPanel.IsContainerVirtualizable="False" RowEditEnding="DataGrid_RowEditEnding">
<DataGrid.Columns>
<DataGridTemplateColumn Header="Group Name" Width="*" SortMemberPath="GroupName">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding GroupName}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<TextBox Text="{Binding GroupName}" />
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Parent Group" Width="*" SortMemberPath="ParentID">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding ParentID, Converter={StaticResource parentIDToGroupNameConverter}}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
<DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding Source={StaticResource ParentGroupsViewSource}}"
DisplayMemberPath="GroupName"
SelectedValue="{Binding ParentID, Converter={StaticResource parentIDToGroupNameConverter}}" SelectedValuePath="GroupName"/>
</DataTemplate>
</DataGridTemplateColumn.CellEditingTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Edit" Width="50" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Button x:Name="btnEdit" Style="{StaticResource ResourceKey=EditButton}" Height="35" Width="35"
Visibility="{Binding DataContext.IsInEdit,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}},
Converter={StaticResource boolToVisibilityInverseConverter}}"
Click="EditSaveButton_Click"
Command="{Binding DataContext.EditCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"/>
<Button x:Name="btnSave" Grid.Row="1" Style="{StaticResource ResourceKey=SaveButton}" Height="35" Width="35"
Visibility="{Binding DataContext.IsInEdit,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}},
Converter={StaticResource boolToVisibilityConverter}}"
Click="EditSaveButton_Click"
Command="{Binding DataContext.SaveCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn Header="Delete" Width="70" IsReadOnly="True">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Button x:Name="btnDelete" Style="{StaticResource ResourceKey=DeleteButton}" Height="35" Width="35"
Visibility="{Binding DataContext.IsInEdit,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}},
Converter={StaticResource boolToVisibilityInverseConverter}}"
Command="{Binding DataContext.DeleteCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"/>
<Button x:Name="btnCancel" Grid.Row="1" Style="{StaticResource ResourceKey=CancelButton}" Height="35" Width="35"
Visibility="{Binding DataContext.IsInEdit,
RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}},
Converter={StaticResource boolToVisibilityConverter}}"
Command="{Binding DataContext.CancelCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}}"/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
<TextBox Grid.Column="0" Text="{Binding SearchGroupName, UpdateSourceTrigger=PropertyChanged}" />
In CodeBehind:
private void CollectionViewSource_Filter(object sender, FilterEventArgs e)
{
var a = e.Item.GetType().GetProperty("GroupName");
if (a != null)
{
if (_viewModel.SearchGroupName != "")
{
var s = a.GetValue(e.Item, null);
if (s != null)
e.Accepted = s.ToString().Contains(_viewModel.SearchGroupName, StringComparison.OrdinalIgnoreCase);
else
e.Accepted = false;
}
else
e.Accepted = true;
}
}
In ViewModel:
ERPLiteDBContext db = new ERPLiteDBContext();
public ListViewModel()
{
Groups = new ObservableCollection<Group>(db.Groups);
SearchGroupName = "";
}
private string _searchGroupName;
public string SearchGroupName
{
get
{
return _searchGroupName;
}
set
{
_searchGroupName = value;
OnPropertyChanged("SearchGroupName");
}
}
private ObservableCollection<Group> _groups;
public ObservableCollection<Group> Groups
{
get
{
return _groups;
}
set
{
_groups = value;
OnPropertyChanged("Groups");
}
}
The above code works. But for above code to work, I have to type-in something into the textBox, then click on the header of the DataGrid to sort it. So, in short above code does not provide live filtering. I would like to filter my data when I type-in something in TextBox......
So the filtering in your code is actually a follow-up from sorting. To perform filtering whenever you like, I suggest following, making small changes to your viewmodels:
Group knows wether itself should be filtered or not. It has a IsHidden property:
public class Group // actually a viewmodel
{
public bool IsHidden
{
get;
set;
}
public string Name
{
get;
set;
}
...
}
IsHidden is set for each member in the collection, each time the Text changes. The CollectionViewSource checks this property:
private void OnTextBoxTextChanged(object sender, TextChangedEventArgs e)
{
// reset first to keep continious filtering
Groups.ToList().ForEach(i => i.IsHidden = false);
foreach (var filteredGroup in Groups.Where(vm => vm.Name == _viewModel.SearchGroupName))
{
filteredGroup.IsHidden = true;
}
ICollectionView cv = CollectionViewSource.GetDefaultView(_dataGrid.ItemsSource);
if (cv != null)
{
// filter the Groups collection
cv.Filter = (vm as Group) => vm.IsHidden == false;
}
}
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>