How to scroll to a given row when WPF DataGrid is loaded - wpf

I display a few thousand rows in a WPF DataGrid, but would like to automatically scroll to a specific row when it is loaded (and later also add a button to scroll to this specific row). Is it possible to achieve it via data binding or from a view model property?
Of course, I've already browsed the Internet, but could not find any corresponding property.. For exemple, SelectedCells only has a get accessor (but I don't think it would have scrolled whatsoever anyway). I don't think that the other properties, such as SelectedItem helps.
Thanks for any insights :-)

There are two options: scroll to a row without selecting it and scroll to a row and select it.
Scroll to row without selecting it
This solution works for any DataGrid.SelectionUnit value (cell or row based selection).
You set the row from a property in your view model, which binds to DataGrid.CurrentItem.
Scrolling must be handled by the DataGrid by handling the DataGrid.CurrentCellChanged event.
View model (must implement INotifyPropertyChanged)
object currentRow = this.DataSource[currentRowIndex];
this.CurrentRow = currentRow;
View.xaml
<DataGrid ItemsSource="{Binding DataSource}"
CurrentItem="{Binding CurrentRow}"
CurrentCellChanged="DataGrid_OnCurrentCellChanged" />
View.xaml.cs
private void DataGrid_OnCurrentCellChanged(object sender, EventArgs eventArgs)
{
var dataGrid = sender as DataGrid;
dataGrid.ScrollIntoView(dataGrid.CurrentItem);
}
Scroll to row by selecting it
This solution works only for DataGrid.SelectionUnit values DataGridSelectionUnit.FullRow or DataGridSelectionUnit.CellOrRowHeader (not cell-only based selection - cell-only solution must completely handle selection and scroll in view).
You set the selected row from a property in your view model, which binds to DataGrid.SelectedItem.
Scrolling must be handled by the DataGrid by handling the DataGrid.SelectionChanged event.
View model (must implement INotifyPropertyChanged)
object selectedRow = this.DataSource[currentRowIndex];
this.SelectedRow = selectedRow;
View.xaml
<DataGrid ItemsSource="{Binding DataSource}"
SelecetedItem="{Binding SelectedRow}"
SelectionChanged="DataGrid_OnCurrentCellChanged" />
View.xaml.cs
private void DataGrid_OnSelectionChanged(object sender, SelectionChangedEventArgs eventArgs)
{
var dataGrid = sender as DataGrid;
dataGrid.ScrollIntoView(dataGrid.SelectedItem);
}

Related

Silverlight - use combobox to select datagrid item

I'm a bit new to WPF and .NET, so this may be a simple task:
I have textblocks that get their values from the selected item of a datagrid. I'm trying to bind a combobox to one column in that datagrid so the user sees all the values in that datagrid column. When selecting an item in the combobox, it should make that row the selected item in the datagrid as well.
Here is my DataGrid:
<sdk:DataGrid AutoGenerateColumns="True" Name="l1dGrid" IsReadOnly="True" ItemsSource="{Binding}" DataContext="{Binding Path=DataContext}" />
and here is where the Datagrid gets data loaded:
_PCContext.Load(_PCContext.GetLine1_DownstairsQuery());
l1dGrid.ItemsSource = _PCContext.Line1_Downstairs;
Now I just need a combobox to have the ability to change the selected item in the DataGrid.Thanks in advance for any assistance!!!
[EDIT - SOLVED]
Ok, so I ended up just querying the datagrid based on the selected item of the combobox and setting the datagrid selected item to the one that matched that query.Here is the code I used to do this:
private void stockPick_comboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
string selection = stockPick_comboBox.SelectedValue.ToString().Replace("Line1_Downstairs : ", "");
selectGridItem(selection);
}
private void selectGridItem(string selection)
{
var stock = (from i in _PCContext.Line1_Downstairs
where i.Stock == selection
select i).FirstOrDefault();
l1dGrid.SelectedItem = stock;
}

Using ScrollIntoView on DataGrid with Checkbox changes behavior

I seem to have conflicting requirements. I have a DataGrid that has a checkbox as the first column. The users want the checkbox to be selectable with a single click, not a double click. I was able to make that happen by using a DataGridTemplateColumn and a checkbox like this:
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsSelected, UpdateSourceTrigger=PropertyChanged}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
The user also a control that allows them to specify a row (there can be hundreds of rows). If they specify a row that isn't in view I want it to scroll into view. I compromised and added an event handler in the code behind for the DataGrid_SelectionChanged event. Originally I was just using the ScrollIntoView command but offscreen rows would get highlighted but the grid did not scroll them into view. I was then able to add a Focus command and the row scrolled into view. So now the event handler looks like this:
private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
DataGrid dg = (DataGrid)sender;
if (dg.SelectedItem == null) return;
dg.ScrollIntoView(dg.SelectedItem);
dg.SelectedItem.Focus();
}
Now I'm back to the original problem, the row scrolls into view but to check the checkbox on any other row (that you don't move into via the jump to row control) you have to click twice. Anybody know what is causing the rows moved to manually to require double clicks?
Well I got it to work with the following code which was inspired by some tangentially related posts on getting the focus into cells. I have no clue as to why ScrollIntoView doesn't work, working or why performing the last three lines was the one way I could get the row to scroll into view without disabling the checkbox.
private void DataGrid_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
DataGrid dg = (DataGrid)sender;
if (dg.SelectedItem == null) return;
dg.ScrollIntoView(dg.SelectedItem);
DataGridRow dg_row = (DataGridRow)dg.ItemContainerGenerator.ContainerFromItem(dg.SelectedItem);
if (dg_row == null) return;
dg_row.Focus();
}

WPF Datagrid autogenerated columns

I have bound a datatable to a datagrid in WPF. Now on clicking a row in the grid I need to have a window pop up. But for that, I need to first change a column in the datagrid to be a hyperlink. Any ideas on how to do that?
<DataGrid Name="dgStep3Details" Grid.Column="1" Margin="8,39,7,8" IsReadOnly="True" ItemsSource="{Binding Mode=OneWay, ElementName=step3Window,Path=dsDetails}" />
If I can't change an autogenerated column to hyperlink, is there a way to add a button to each row instead?
Thanks
Nikhil
So, it was really hard to create hyperlink columns to autogenerated datagrid. What I eventually did was this - create buttons to the grid on the fly and then attach a routed event for the same based on the autogenerate event of the datagrid where I shall put my code. I didn't want my code to be hardcoded to the columns and now I'm flexible by changing the datatable on the fly. Here is the code:
private void dgStep3Details_AutoGeneratedColumns(object sender, EventArgs e)
{
DataGrid grid = sender as DataGrid;
if (grid == null)
return;
DataGridTemplateColumn col = new DataGridTemplateColumn();
col.Header = "More Details";
FrameworkElementFactory myButton = new FrameworkElementFactory(typeof(Button), "btnMoreDetails");
myButton.SetValue(Button.ContentProperty, "Details");
myButton.AddHandler(Button.ClickEvent, new RoutedEventHandler(btnMoreDetails_Click));
DataTemplate cellTempl = new DataTemplate();
//myButton.SetValue(Button.CommandParameterProperty, ((System.Data.DataRowView)((dgStep3Details.Items).CurrentItem)).Row.ItemArray[0]);
cellTempl.VisualTree = myButton;
col.CellTemplate = cellTempl;
dgStep3Details.Columns.Add(col);
}
public void btnMoreDetails_Click(object sender, RoutedEventArgs e)
{
//Button scrButton = e.Source as Button;
string currentDetailsKey = ((System.Data.DataRowView)(dgStep3Details.Items[dgStep3Details.SelectedIndex])).Row.ItemArray[0].ToString();
// Pass the details key to the new window
}
I don't think you'll be able to get these advanced UI features out of autogenerated columns. I think you'll either have to decide to program these columns in C# or VB.NET when you retrieve your data and tailor them the way you like, or you'll have to abandon the UI ideas you've mentioned. Autogenerated columns just cannot do that.
However, you could change your approach. Try checking into events like MouseLeftButtonDown, etc. and see if you can simulate the behavior you want by other means.

How to stop automatic refresh of a WPF ListBox Databinded on a EntityFramework object

I have a Xaml Page with a Databinded ListBox and a detail grid to create or update selected element.
My Page.DataContext is binded on a ADO.NET Entity Data Model table ("Univers").
private void Page_Loaded(object sender, RoutedEventArgs e)
{
SEPDC = new Models.SEP();
universViewSource = new CollectionViewSource();
universViewSource.Source = SEPDC.Univers.Execute(System.Data.Objects.MergeOption.AppendOnly);
DataContext = universViewSource;
}
The Xaml code of the ListBox :
<ListBox DisplayMemberPath="Nom" ItemsSource="{Binding}" Name="universListBox" SelectedValuePath="IdUnivers"/>
When i select an element in the ListBox, the grid detail automatically display the information of the selected element
Here the "Nom" TextBox witch use TwoWay databinding :
<TextBox Name="nomTextBox" Text="{Binding Path=Nom, Mode=TwoWay}" />
When i modify the TextBox "Nom", the ListBox is automatically updated. Great ... But i haven't call the SaveChanges method of my SEPDC DataContext object ...
How can i stop the automatic refresh of my ListBox until i explicit call the SaveChanges method and if possible, without use the Binding UpdateSourceTrigger=Explicit option ?
Regards.
You can use two separate entity data context (SEPDC) objects. Your ListBox is bound to one and your detail grid to the other. When the SelectedValue changes in the ListBox, find the same entity in the detail grid's entity data context and set it. After saving changes from the detail grid's entity data context, refresh the one for the ListBox.
I use this technique but i have to recreate the ListBox SEPDC each time i refresh the ListBox.
List<Models.Univers> list;
using (Models.SEP dc = new Models.SEP())
list = dc.Univers.Execute(System.Data.Objects.MergeOption.AppendOnly).ToList();
universListBox.DataContext = list;
The Refresh method doesn't work.
Regards

Access Datagrid row on TemplateColumn button click

I am implementing a file upload tool using Silverlight. In this I can browse for files and when I select a file then it is bound to a datagrid. In the datagrid I have a template column with a button to delete the particular item from the datagrid and ItemSource of the datagrid which is a List<>.
I have a class UploadedFiles as below.
public class UploadedFiles
{
public FileInfo FileInf{get;set;}
public int UniqueID{get;set;}
public string FileName{get;set;}
public string FileExtension{get;set;}
public long FileSize{get;set;}
}
I am using a datagrid with a templatecolumn like below with ItemSource set as List<UploadedFiles>
<data:DataGridTemplateColumn Width="100">
<data:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Click="btn_Click" Content="Del" Width="45"/>
</DataTemplate>
</data:DataGridTemplateColumn.CellTemplate>
</data:DataGridTemplateColumn>
and the button click event handler is
private void btn_Click(object sender, System.Windows.RoutedEventArgs e)
{
/* I need to access the particular list item based on the datagrid
row in which the clicked button resides.*/
}
I need to access the particular list item based on the datagrid row in which the clicked button resides and remove the item from the List<UploadedFiles> and rebind the datagrid.
Thanks
Two things to look at here:
Firstly, to get the individual UploadedFiles object, cast the sender to a Button (or FrameworkElement) and access the DataContext property. The DataContext will be the UploadedFiles row (you will need to cast again from object).
Secondly, rather than removing the item from the list and rebinding, have you considered using an ObservableCollection instead? If you use that, removing the row will automatically remove it from the DataGrid without needing you to rebind.
private void btn_Click(object sender, System.Windows.RoutedEventArgs e)
{
var uploadedFiles = (UploadedFiles)((FrameworkElement)sender).DataContext;
//access collection and remove element
}

Resources