Datagrid scroll bar stops working - wpf

When I run the app, the vertical scroll bar works as expected. However, when I add a new line/row, the bar (control that should go up and down on the slider) doesn't slide. With the mouse wheel I can scroll up and down the list of rows, and I can click on the up and down arrows. So the scroll bar works, but not as expected. The control should slide up and down, like it does at first, but after adding that new line, it does not.
I hope that is clear enough, I've searched many issues to find this peculiar behavior, but was unsuccessful. Here is the XAML, in part, as it is now:
<DataGrid x:Name="inventoryDataGrid" AutoGenerateColumns="False"
SelectedValuePath="Id"
EnableRowVirtualization="True"
EnableColumnVirtualization="True"
Style="{DynamicResource DataGridDemoStyle}"
CanUserSortColumns="True"
VerticalAlignment="Top"
ItemsSource="{Binding Source={StaticResource claimInventoryViewSource}}"
RowEditEnding="dgInv_RowEditEnding"
CellEditEnding="dgInv_CellEditEnding"
SelectionChanged="dgInv_SelectionChanged"
IsSynchronizedWithCurrentItem="True" CanUserAddRows="False"
RowHeaderWidth="0"
Sorting="DataGrid_Standard_Sorting" MouseDoubleClick="inventoryDataGrid_DoubleClick"
CanUserDeleteRows="True"
SelectionMode="Single"
HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Visible"
Width="999.5"
CommandManager.PreviewCanExecute="Grid_PreviewCanExecute" Grid.Column="0"
Grid.Row="1"
Margin="0,3,0,0" RowDetailsVisibilityMode="VisibleWhenSelected" Height="227"
LostFocus="inventoryDataGrid_LostFocus" Background="#FFFCF2E7"
AlternatingRowBackground="#FFF2F2D6" RowBackground="#FF6FC4BF"
GotFocus="inventoryDataGrid_GotFocus">
<DataGrid.Resources>
<Style x:Key="DataGridCellStyle" TargetType="{x:Type DataGridCell}" >
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Foreground" Value="White"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.Resources>
<DataGrid.Columns>
Thanks!

I was able to solve this issue. The problem was that there was code I implemented a long time ago, in an EndEdit routine (found here: EndEdit equivalent in WPF), which somehow caused this erratic behavior in my datagrid scrollbar.
Once I removed this code, my scrollbar worked without problems. Then of course I had to research a way to save the data in text boxes without the use of EndEdit, but that is way off topic for this Question.

Related

WPF Datagrid Column Width Issue

I have columns in a DataGrid that are being set by an ObservableCollection that is the type of a simple data object that I created. The first column has a width set to "Auto" and the second column as a width set to "1*".
I am currently using the method in the answer here to autoupdate the width of my column that is set to "Auto" when the ItemsSource changes. This seems to work most of the time:
This looks great, and works all of the time
Although, when the ItemsSource is a little bit larger (lets say about 30-35 records), the "Auto" width (first) column will shrink down only when the DataGrid (including the scroll bar) is clicked:
This will be resized properly if it hasn't been clicked
My XAML looks like this:
<my:DataGrid CanUserSortColumns="false" CanUserResizeRows="false" CanUserResizeColumns="false" CanUserReorderColumns="false" CanUserDeleteRows="false" CanUserAddRows="false" AutoGenerateColumns ="False" SelectionMode="Single" SelectionUnit="Cell" Height="113" HorizontalAlignment="Left" Margin="11,22,0,0" Name="dataGrid" VerticalAlignment="Top" Width="226" Background="#FFE2E2E2" AlternatingRowBackground="#FFA4CFF2" BorderBrush="#FF7C7C7C" HorizontalGridLinesBrush="White" PreviewKeyDown="dataGrid_PreviewKeyDown" CellEditEnding="dataGrid_CellEditEnding" BeginningEdit="dataGrid_BeginningEdit" PreparingCellForEdit="dataGrid_PreparingCellForEdit" SelectedCellsChanged="dataGrid_SelectedCellsChanged" Loaded="dataGrid_Loaded" TargetUpdated="dataGrid_TargetUpdated">
<my:DataGrid.Columns>
<my:DataGridTextColumn Binding="{Binding Path=Name, NotifyOnTargetUpdated=True}" Width="Auto">
<my:DataGridTextColumn.CellStyle>
<Style TargetType="{x:Type my:DataGridCell}">
<Setter Property="KeyboardNavigation.IsTabStop" Value="False"></Setter>
<Setter Property="IsHitTestVisible" Value="False"></Setter>
<Setter Property="Focusable" Value="False"></Setter>
<Setter Property="Background" Value="WhiteSmoke"></Setter>
<Setter Property="BorderBrush" Value="LightGray"></Setter>
</Style>
</my:DataGridTextColumn.CellStyle>
</my:DataGridTextColumn>
<my:DataGridTextColumn Binding="{Binding Path=Value}" Width="1*"></my:DataGridTextColumn>
</my:DataGrid.Columns>
</my:DataGrid>
The code to assure the updating of the column:
private void dataGrid_TargetUpdated(object sender, DataTransferEventArgs e)
{
dataGrid.Columns[0].Width = 0;
dataGrid.UpdateLayout();
dataGrid.Columns[0].Width = new DataGridLength(0, DataGridLengthUnitType.Auto);
dataGrid.UpdateLayout();
}
Is there any reason this may be happening only when the list is longer like this?
DataGrid's TargetUpdated might not get called in a few scenarios. For example, when you have more rows coming in but they are not visible then the datagrid doesn't have to "waste cycles on" re-rendering something that is not visible. The initial TargetUpdated is fine, but you might have to find an additional hook, and do similar thing there, such as hooking into the CollectionChanged of the object that's bound to ItemsSource of your datagrid, your observableCollection has the event CollectionChanged, subscribe and try your logic there.

Why is my DataGridComboBoxColumn clearing its value when I navigate away from it?

I have a DataGrid with two columns:
DataGridComboBoxColumn
DataGridTextColumn.
I have set up data validation so that if one has a value, the other will be in error until it also has a value. The validation is silly, but it provides some simple criteria with which to do validation so I can illustrate this issue.
When I type something into the text cell, press tab, then click back on the first cell, the first cell shows that it is in an error state (which is correct). The problem is that when I select something from the combo box dropdown and navigate away from that cell (either by pressing tab or by clicking in another cell), the value I selected for the combo box disappears. I have the binding set to update my source whenever the property changes, so it gets set to the value I select as soon as I select it. But, when I navigate away from the cell, the property gets set to null. I do not see this behavior if the cell is not in an error state.
Can anyone help please? Here is the XAML for my DataGrid:
<DataGrid Grid.Row="2"
Name="GrdData"
ItemsSource="{Binding Path=Dvm.Data}"
SelectedItem="{Binding Path=Dvm.SelectedData, Mode=TwoWay}"
CanUserAddRows="True"
CanUserDeleteRows="False"
AutoGenerateColumns="False"
Margin="5"
SelectionMode="Single"
IsEnabled="{Binding Path=IsGridEnabled}">
<DataGrid.Columns>
<DataGridComboBoxColumn Header="Column 1"
SelectedItemBinding="{Binding Path=Col1, Mode=TwoWay, ValidatesOnDataErrors=True, UpdateSourceTrigger=PropertyChanged}"
Width="*"
DisplayMemberPath="Description">
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding Path=DropDownValues, Mode=OneWay}" />
<Setter Property="IsSynchronizedWithCurrentItem" Value="False"/>
</Style>
</DataGridComboBoxColumn.ElementStyle>
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="ItemsSource" Value="{Binding Path=DropDownValues, Mode=OneWay}"/>
<Setter Property="IsDropDownOpen" Value="True" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
</DataGridComboBoxColumn>
<DataGridTextColumn Header="Column 2"
Binding="{Binding Path=Col2, Mode=TwoWay, ValidatesOnDataErrors=True}"
Width="*"/>
</DataGrid.Columns>
</DataGrid>
I can't imagine what I'm doing wrong. I saw this other link that seems to describe the same problem I am having, but the solution that worked for them doesn't seem to work for me; I added the SelectedValueBinding and SelectedValuePath, but the behavior did not change.
Remove Mode=TwoWay from the bindings.
The problem is caused by a bug in the clipboard and automation support. That works by setting a special property on the cell to ClipboardContentBinding and then reading the value. If that binding is two-way, it winds up sometimes pushing an old value from the special property back to the view model, and validation errors seem to trigger this behavior. DataGridBoundColumns and DataGridComboBoxColumns will supply Binding or SelectedItemBinding if ClipboardContentBinding is null, so you’ll get this bug if you set either of those to a TwoWay binding.
If you don’t set Mode, it will be Default and use the default from the property, which is TwoWay for TextBox.Text and ComboBox.SelectedItem but OneWay for the special clipboard property.

WPF/Xaml Datagrid wont fire ValidatesOnDataErros when binding is in DataGridTextColumn

I have a datagrid that is bound to an observable collection from my viewmodel. This all works fine and displays my data in the datagrid.
What i need to do now is validate some columns when the user chnages the text. I am using the IDataErrorInfo to do this.
If i do the following:-
**<TextBox
Width="100"
Text="{Binding Path=CallCode,
Mode=TwoWay,
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged }"/>**
This works and triggers the validation code in my viewmodel, however if i add this code to the datagrid as below it doesnt do anything!:-
<Border x:Name="body"
DockPanel.Dock="Top"
Grid.Row="2"
Grid.Column="0">
<!-- Results -->
<DataGrid x:Name="Results"
ItemsSource="{Binding CallCodesList}"
AutoGenerateColumns="False"
ScrollViewer.CanContentScroll="True"
ScrollViewer.VerticalScrollBarVisibility="Auto"
CanUserAddRows="False">
<DataGrid.Columns >
**<DataGridTextColumn
Header="Call Code"
CanUserSort="True"
Width="100"
Binding="{Binding CallCode,
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged />**
<DataGridCheckBoxColumn Width="70"
Binding="{Binding Path=HasSpeech}"
Header="Speech"
IsThreeState="True">
<DataGridCheckBoxColumn.ElementStyle>
<Style TargetType="CheckBox">
<Setter Property="IsChecked" Value="{Binding HasSpeech}" />
</Style>
</DataGridCheckBoxColumn.ElementStyle>
</DataGridCheckBoxColumn>
</DataGrid>
</Border>
Is there someting i am missing or can it not see something because it is in a datagrid, this is all new so currently stuck :(
Any help with this would be great.
It seams that the code is fine, i don't know if the wpftoolkit's data grid allow IDataErrorInfo validation, but in this article (Validation in WPF Toolkit’s DataGrid) you can see a good example of using validation on wpftoolkit's data grid, but using the IDataError interface way.
I hope this help to you...

Fill up DataGrid with empty rows

I have a DataGrid bound to a DataTable. I want the DataGrid to always have at least ten (empty) rows, also if there are not enough real data-items (the data comes in little by little).
One approach would be to easily add ten empty rows to the DataTable at initialization. But when a real data-item comes in, I can't easily add the row, I have to find the first empty row to overwrite it, what is not very handy.
So someone knows a smarter/built-in way to achieve this?
It's gonna be mess, no matter from what side it's approached. I'd say your best bet (provided that your grid cells content won't be wrapped) is to use a visual brush (with lines) as your DataGrid's background.
UPDATE 1 - XAML
It's alwast there, the trick is to use MinHeight, which will produce a vision of blank items thanks to tiled background. If your grid will be populated with the real data the background will expand, rendering more lines.
One thing I didn't try is how it'll handling scrolling.
Here's an example:
<Grid>
<Grid.Resources>
<VisualBrush x:Key="StripesBrush" TileMode="Tile" Viewport="0,0,5,20"
Viewbox="0,0,10,10" ViewportUnits="Absolute"
ViewboxUnits="Absolute">
<VisualBrush.Visual>
<Line X1="0" X2="10000" Y1="0" Y2="0" Stroke="DarkGray"/>
</VisualBrush.Visual>
</VisualBrush>
</Grid.Resources>
<DataGrid x:Name="g" AutoGenerateColumns="False"
GridLinesVisibility="None"
MinHeight="100"
Height="100"
VerticalAlignment="Top"
Background="{StaticResource StripesBrush}">
<DataGrid.Columns>
<DataGridTextColumn Header="A" Width="Auto" Binding="{Binding Item1}">
<DataGridTextColumn.CellStyle>
<Style TargetType="DataGridCell">
<Setter Property="Background" Value="Transparent"></Setter>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="B" Width="Auto" Binding="{Binding Item2}" />
</DataGrid.Columns>
</DataGrid>
</Grid>
if its just for layout, you can simply add a second datagrid with 10 empty rows under your real datagrid. or you take the aproach Dmitry posted and use visual brush

Showing a new Window right next to a DataCell in DataGrid (WPF)

I want a scenario when a user clicks on a cell in DataGrid in WPF, I want to open NumPad next to it (This is basically for touch based input).
The NumPad, I understand is a separate window.
1) How can I know which Cell is selected 2) how can I show the NumPad next to the cell? 3) How can I find the coordinates of cell to position my NumPad? 4) How can I set the value of cell based on NumPad entry?
NumPad is a WPF User Control in the same application.
DataGrid is a .NET 4 Control.
It's a normal Windows Desktop application
This is not a trivial task and you should have some knowledge of WPF to accomplish this, but here are some ideas what you might look for:
The DataGridCell.IsSelected property tells you whether a cell is selected.
I would use a Popup to show the NumPad directly besides the cell.
If you use a Popup you do not need the coordinates, but you can specify the relative placement using the Popup.Placement property. Also see this MSDN document: Popup Placement Behavior
You could try to use a Binding from the NumPad to the user control in the DataGridCell.
Using the DataGrid.CellStyle or the DataGridColumn.CellStyle property you can specify an alternate style for all cells of the DataGrid or some specific column. Within this style, you could change the template and add a Popup which is opened only if the current cell is selected. You can easily achieve this by binding the Popup.IsOpen property to the DataGridCell.IsSelected property.
This is just an initial idea. You will still have to have a look at the provided MSDN links and also read some other stuff about WPF. Although it might take some time to learn this 'WPF way' (i.e. only XAML), it is (in my eyes) much easier than using lots of code-behind to determine the currently selected cell, positioning a control at the correct location, transferring the data from the NumPad to the cell and so on...
I really like Gehho's answer.
Doing as he suggested, besides using the Template column over styling text columns, resulted in the following XAML:
<Grid x:Name="LayoutRoot">
<DataGrid>
<DataGrid.Columns>
<DataGridTextColumn Header="R" Binding="{Binding Color.R}" />
<DataGridTextColumn Header="G" Binding="{Binding Color.G}" />
<DataGridTextColumn Header="B" Binding="{Binding Color.B}" />
<DataGridTextColumn Header="Alpha" Binding="{Binding Color.A}" />
<DataGridTemplateColumn Header="Thumb">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Border x:Name="border" Background="{Binding}">
<Popup IsOpen="{Binding IsMouseOver, ElementName=border, Mode=OneWay}"
PopupAnimation="Fade"
Placement="MousePoint">
<Border Width="200" Height="200" Background="{Binding Background , ElementName=border, Mode=OneWay}" />
</Popup>
</Border>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<SolidColorBrush Color="Red"/>
<SolidColorBrush Color="Green"/>
<SolidColorBrush Color="Blue"/>
<SolidColorBrush Color="Yellow"/>
<SolidColorBrush Color="SteelBlue"/>
<SolidColorBrush Color="Lime"/>
<SolidColorBrush Color="Cyan"/>
</DataGrid>
</Grid>
</Window>
Hope this helps!

Resources