How to prevent NewItemPlaceholder row in a ComboBox bound to the same DataTable as a DataGrid in WPF - wpf

I'v a wizard style application that uses pages and the user can navigate between them either by Next and Previous buttons or by using a navigation bar to directely access certain pages.
On one page (i'll call it "grid page")i have a DataGrid bound to a DataTable. There is some initial data in the DataTable, but using the DataGrid the user can add, edit and delete rows as he wishes.
On the next page (i'll call i "combo box page") have a ComboBox which is bound to the same DataTable as the DataGrid on the grid page.
Both pages use the same object as data context.
If i jump to the combo box page directly everything works fine, the combo box has an entry for each row in the DataTable like it's supposed to be. Now i navigate to the grid page and don't touch anything there and then go to the combo box page again. Now the ComboBox displays a new item, a NewItemPlaceholder. Obviously this happens because the DataGrid has UserCanAddRows set to true and thus displays a placeholder row for a new item. But this should only concern the DataGrid and not the bound DataTable, so in my eyes this is a bug, or at least an absolutely horrible design.
Of course i don't want the NewItemPlaceholder in my ComboBox (and selecting it causes a lot of problems). So how can i prevent it from being displayed in the ComboBox?
Update: In the meantime i found out the placeholder item isn't in the DataTable as a row, which makes it even stranger, unless there is a flag in a DataTable that says "there is a NewItemPlaceholder in this table" but isn't a row itself. Additionally when i register to the Initialized event of the ComboBox i have the 2 items i'm looking for, when i register to the Loaded event i have the NewItemPlaceholder as well, so it must be added somewhere between those 2 events.

Use a CollectionViewSource. The problem occurs when more than one control shares the same view of a collection such as when a collection is bound in one case to a DataGrid and in another to the ComboBox. If the DataGrid allows the user to add items (i.e., the DataGrid's CanUserAddRows is true), the NewItemPlaceholder may have been added to the view. Try something like
<UserControl >
<UserControl.Resources >
<CollectionViewSource Source="{Binding MyCollection}"
x:Key="SourceWithoutNewItemPlaceholder" />
</UserControl.Resources>
<Grid Name="LayoutGrid" >
<ComboBox ItemsSource={Binding Source={StaticResource SourceWithoutNewItemPlaceholder}} >
</Grid>
</UserControl>

After several failed attempts i found 2 solutions:
1) In the Loaded event of the page i make a binding in the code behind, not to the DataTable itself, but to a DataTable.ToList. If i do it that way the NewItemPlaceholder won't show up.
2) In the view model i make a new property which returns the DataTable.ToList and bind to it. This way i can do the binding in XAML.
Just for reference, methods that don't work: Filter property on the ComboBox throws a NotSupportedException, seems the DataTable doesn't support this. Deleting the NewItemPlaceholder in the Loaded event of the ComboBox doesn't work either because you can't delete items when an ItemsSource is set. Removing the NewPlaceHolderItem when leaving the page with the data grid doesn't work either, because i can't find where it is (it's not in the DataTable or the View of the DataTable)

Use a CollectionViewSource to bind one control(Combobox) and collection property defined in ViewModel to bind another control (Datagrid).
Property in ViewModel :
private List<Rate> _rate = new List<Rate>();
public List<Rate> RatePlans
{
get { return _rate; }
set
{
_rate = value;
OnPropertyChanged("RatePlans");
}
}
Then in XAML :
<UserControl >
<UserControl.Resources >
<CollectionViewSource Source="{Binding RatePlans}" x:Key="RatesSource" />
</UserControl.Resources>
<Grid Name="LayoutGrid">
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<ComboBox Grid.Row="0" DisplayMemberPath="RatePlanName" ItemsSource="{Binding Source={StaticResource RatesSource}}" HorizontalAlignment="Stretch"/>
<DataGrid Grid.Row="1" HorizontalAlignment="Stretch" CanUserAddRows="True" AutoGenerateColumns="False" ItemsSource="{Binding RatePlans}">
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding RatePlanName}" Header="Rate Plan" />
<DataGridTextColumn Binding="{Binding RoomType}" Header="Room Type" />
<DataGridTextColumn Binding="{Binding Price}" Header="Price"/>
</DataGrid.Columns>
</DataGrid>
</Grid>
</UserControl>

Related

how to get muliplte checked datagrid checkbox items with MVVM

I have datagrid which consist of muliple data grid checkboxes,i want to get the datagrid checked items,i am able to get only the single selected row item,but i need collection of checked checkboxes, below code i am using .Please let me know how to resolve this
**Xaml*****
<DataGrid SelectedItem="{Binding SelectedRow, Mode=TwoWay}" ItemsSource="{Binding ManualDataTable}" Background="{Binding ElementName=gd,Path=Background}">
<DataGrid.Columns>
<DataGridCheckBoxColumn Binding="{Binding UserID}" Width="60" />
<DataGridTextColumn Binding="{Binding Name}" Width="140" Header="Name" FontSize="16" FontFamily="segoe_uilight" IsReadOnly="True" />
</DataGrid.Columns>
</DataGrid>
<Button BorderBrush="{x:Null}" Content="Add participants" Width="220" Height="50" FontSize="20" Command="{Binding SaveAssignedUser}"/>
*****View Model***********
DataTable _manualDataTable;
public DataTable ManualDataTable
{
get
{
return _manualDataTable;
}
set
{
_manualDataTable = value;
RaisePropertyChanged("ManualDataTable");
}
}
private List<DataRowView> selectedRow;
public List<DataRowView> SelectedRow
{
get
{
return selectedRow;
}
set
{
selectedRow = value;
RaisePropertyChanged(() => SelectedRow);
}
}
public void ExecuteSaveAssignedUser()
{
SelectedRow = new List<DataRowView>();**///need multiple checked checkboxes collection**
foreach (DataRowView drv in SelectedRow)
{
}
}
Your checkbox column is bound to a a property called UserID, which I guess is the name of a column in your DataTable. Whenever you check/uncheck a checkbox in the datagrid, the binding will change the value of UserID to true or false, in the relevant DataRow of the DataTable.
Why would you bind a checkbox column to a user ID? I suspect this isn't what you actually want. Instead you probably need to add a boolean column to your DataTable (e.g. IsSelected), and bind your checkbox column to that instead.
It looks like you are using MVVM, so it isn't possible to access the datagrid items from within your view-model. You can only access the data that the grid is bound to (i.e. your DataTable). Your view-model code needs to iterate through the rows in this DataTable, examining the value of the UserID column (or the "IsSelected" column if you add one!) to determine whether that row's checkbox is checked in the datagrid.
The SelectedItem property that you are binding to is completely unrelated to your checkbox column. Checking and unchecking these will have no effect on SelectedItem. This property is used to determine which row the user has selected with the mouse (which gives the row a different b/g colour). Forget about this property - it's not relevant to what you are trying to do.
As already mentioned elsewhere, try and avoid DataTables in WPF. Instead, define some kind of "User" class, and have your view-model expose a collection of these for your grid to bind to.
You seem to be missing the entire point of data binding... that is that you have access to all of the data that is displayed in your DataGrid from your code behind. You have bound the ManualDataTable DataTable property to the DataGrid, so the values that are data bound to the RadioButton controls are all in one column of your DataTable.
Therefore, all you need to do to access them is to look in your DataTable. There are several ways to achieve this, but here is one:
foreach(DataRow row in ManualDataTable.Rows)
{
if (row[requiredColumnIndex] == true) AddRowToSomeCollection(row);
}
However, if you're going to continue to use WPF, I'd seriously advise that you stop using these old classes, such as DataTables. Generally in WPF, we define custom classes and that makes everything much simpler in the long run.

In focus row in Xceed datagrid doesn't get updated

I have a Xceed Datagrid whose ItemsSource is CollectionViewSource defined in XAML. Whenever grid is updated, only the row which is in focus doesn't show updated values (revert back to original values) but all of other rows are updated. If I directly bound the grid to a Collection in ViewModel then everything works fine. The problem is only when, CollectionViewSource comes into picture. Any help is appreciated.
Can you try using the DataGridCollectionViewSource instead of a CollectionViewSource. By using this, you will reap the benefits of the DataGrid such as built in filtering, sorting, grouping, etc. An example from their documentation:
<Grid xmlns:xcdg="http://schemas.xceed.com/wpf/xaml/datagrid">
<Grid.Resources>
<xcdg:DataGridCollectionViewSource x:Key="cvs_orders"
Source="{Binding Source={x:Static Application.Current},
Path=Orders}"/>
</Grid.Resources>
<xcdg:DataGridControl x:Name="OrdersGrid"
ItemsSource="{Binding Source={StaticResource cvs_orders}}"/>
</Grid>
I got solution...
grid.CurrentItem = null

wpf datagrid autoscroll

I would like to set up a datagrid so that whenever an item is added to its itemssource the datagrid scrolls down to show the last item.
The datagrid is inside a datatemplate, so i cannot set the X:name property and access it directly from the codebehind.
What I have in mind is to use a datagrid event that fires when a row is added and has the grid scroll itself.
Here's some psuedo code that outlines how I have things set up:
UI.XAML exerpt
<TabControl ItemsSource="{Binding Parents}" x:Name="ProductsTab">
<TabControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Key}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<DataGrid Margin="5" ItemsSource="{Binding Value.Children}">
<DataGrid.Columns>
<Column Column definitions removed for your sanity/>
</DataGrid.Columns>
</DataGrid>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
UI.XAML.CS exerpt
public class UI
{
//Thanks to Dr. WPF for the ObservableDictionary class
public ObservableDictionary<string, Parent> Parents {get; set;}
}
Parent.CS
public class parent
{
public ObservableCollection<Child> Children {get; set;}
}
The datagrids are not editable.
In case you're wondering, I have read the post "How to autoscroll on WPF datagrid" the code in that post would work for me if I could find an event that fires whenever an item is added to the datagrid itemssource.
Any Ideas?
Combine the autoscrolling idea with the idea from this question or this MSDN thread: instead of listening to your grid's event to detect row additions, listen to the events from the ItemsSource.
Edit: Since you don't like that suggestion, you could try hooking LoadingRow, but I strongly suspect this will require EnableRowVirtualization = false in order to work (I haven't tried it). If your collection gets large, turning off row virutalization opens up the possibility of a serious performance hit.
you can access the DataGrid, even if it is in a DataTemplate, by doing a 'search' in the visual tree : VisualTreeHelper.GetChildCount // VisualTreeHelper.GetChild , then test again the type until you find your grid. And you might seek with same kind of methods the ScrollBar, and then you can hook an event handler and use a code-behind logic.

Prevent DataGrid row from being deleted

I want to protect some rows of a DataGrid to be protected from deletion by the user, although the property CanUserDeleteRows is set to true.
Is there a possibility to protect some rows, hopefully through a databinding or a trigger? The ItemsSource is bound to an ObservableCollection of T.
If you have a property on your bound objects that can be used to determine if the current row can be deleted , like 'IsDeleteEnabled', then you can bind the DataGrid's CanUserDeleteRows property to SelectedItem.IsDeleteEnabled.
For example,
<DataGrid Name="dataGrid1"
CanUserDeleteRows="{Binding ElementName=dataGrid1, Path=SelectedItem.IsDeleteEnabled}"
Never done this with a DataGrid. Usually, when I need to control something like this, I use a ListBox and a DataTemplate with a Grid within to give it the idea of a Grid or a ListView with a GridView in the template because they both give you more control over the interaction.
A shot in the dark, since you're binding, you could use a DataGridTemplateColumn.CellEditingTemplate and make your own Delete button/text which is visible or enabled based off logic within your binding object. Maybe something like this (I didn't test this, but it should be a direction you can head)?
<dg:DataGridTemplateColumn Header="Action">
<dg:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Text Content="Delete" />
</DataTemplate>
</dg:DataGridTemplateColumn.CellTemplate>
<dg:DataGridTemplateColumn.CellEditingTemplate>
<DataTemplate>
<ButtonEnabled="{Binding Path=IsDeleteEnabled, Mode=OneWay}" Content="Delete" Command="{Binding Path=DeleteMe}" />
</DataTemplate>
</dg:DataGridTemplateColumn.CellEditingTemplate>
</dg:DataGridTemplateColumn>
Using this method, since the command is bound to the individual object, you would probably have to raise an event your screen's ViewModel handles to remove that row from the ObservableCollection.
Again, not sure if this is the best way, but it's my 10 minute stab at it. So if it's horrible, please don't vote me down too much.

WPF binding to individual 'rows' (not columns) of a datagrid possible?

I have a datagrid. A column of the datagrid is a simple <DataGridTemplateColumn> with its CellTemplate containing a <DataTemplate> which contains a <ComboBox> such as
<my:DataGrid Name="dataGridMain" AutoGenerateColumns="False">
<my:DataGrid.Columns>
<my:DataGridTemplateColumn Header="Food" >
<my:DataGridTemplateColumn.CellTemplate >
<DataTemplate>
<ComboBox Name="comboDataTemplate"
Text="{Binding Path=Food,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding Source={StaticResource resFoodLookups}}"
DisplayMemberPath="FoodName"
SelectedValuePath="FoodID" IsEditable="True" />
</DataTemplate>
</my:DataGridTemplateColumn.CellTemplate>
</my:DataGridTemplateColumn>
</my:DataGrid.Columns>
</my:DataGrid>
All is working fine. Each combobox is bound to a static list due to the ItemsSource="{Binding Source={StaticResource resFoodLookups}}" statement.
But my requirement is that this list will change from row-to-row.
That is: each time a user types a new entry in the combobox list on one row, I want to have it available in the selection on the next row.
Basically, I want to create a new list for the user each time the user inserts a new word in the combobox on any of the rows. (The combobox is editable).
Now, I can wire up the "ItemsSource=..." at run-time, but I'm only able to do this once thus the <DataTemplate> propagates the 'same' list to 'all' the comboboxes on 'all' the rows.
My thoughts are that I need to change the ItemsSource=... property on an object-by-object basis on each combobox that is created in memory after the DataTemplate has created them - but I have no idea how to do this.
What you need to do is perform 2 way data binding to your the ItemsSource, this way when the ItemSource is updated in one of the combo boxes it will auto update your original collection and therefore your other combo boxes as well.
What I normally do is use the MVVM pattern. It is worth some research if you are not already using a particular pattern on your application.
Using it to solve your problem i would do the following:
Create a ViewModel (Lets call it MyViewModel) which has a collection of values called 'MyComboBoxItems' (It is important that you use ObservableCollection for the databinding to work)
When I create the Window/Control that contains your table, I also create an instance of MyViewModel and set its the Window.DataContext=myViewModelInstance
For your combobox binding use ItemsSource="{Binding Path=MyComboBoxItems, Mode=TwoWay}

Resources