In a "Refresh" function for a WPF DataGrid, I'm trying to save the sorting criteria prior to the reload of items to the DataGrid, the new items loaded and then set the previous sorting criteria. I'm using this code:
void Refresh(DataGrid docsDataGrid) {
var sd = ListSortDirection.Ascending;
DataGridColumn sortCol = null;
foreach (var column in DocsDataGrid.Columns)
{
if (column.SortDirection != null)
{
sd = (ListSortDirection)column.SortDirection;
sortCol = column;
}
}
docsDataGrid.ItemsSource = GetLatestItems();
sortCol.SortDirection = sd;
}
With the code above, I do get the visual cue of "sorted column", but the newly loaded items are not really sorted, so it is a bit of a mirage. I tried with docsDataGrid.Items.Refresh() after sortCol.Direction = sd but the datagrid does not pick up the sorting direction set programatically.
How can I leverage the built-in sorting facilities of the DataGrid and programatically set the column to be sorted (and actually sort rows by that column)?
After some additional research the SortDataGrid example in this SO Q&A helped me: Sort a wpf datagrid programmatically
It seems that I should have dealt also with DataGridItems.SortDescriptions as well.
Not sure if this will help you but why not save the selected SortDirection in a setting and load it when you need it?
Related
So, I have a DataGrid, that I want to manipulate programmatically a lot.
string[] values = new string[something.Count];
for (int i = 0; i < somethingElse.Count; i++)
{
if (condition)
values[i] = Data[i].ToString();
else
values[i] = "";
}
var style = new System.Windows.Style(typeof(DataGridRowHeader));
style.Setters.Add(new Setter(DataGridRowHeader.ContentProperty, Data[0].ToString()));
MyGrid.Items.Add(new DataGridRow()
{
HeaderStyle = style,
Item = values
});
This I do in a loop and I am able to fill in my grid with all the data I need.
Later, I am able to access cells, edit them, take their values, whatever I want and need.
However, when user wants to use the grid as you would in MS Excel, the cells are not editable.
So I went the other way and created a :
ObservableCollection<ObservableCollection<string>> gridData = new ObservableCollection<ObservableCollection<string>>();
//*** ... *** the gridData is filled in the same way, you can imagine
MyGrid.ItemsSource = gridData;
This does fill in the data perfectly the same way, more than that, the data are now editable.
But my custom row headers disappeared.
I need them, also, I do not think I want to use binding for row header values.
Can the first approach be somehow modified to still be editable, but rows with data being filled the very same way?
I eventually solved this combining both the abovementioned approaches.
First, I generate ObservableCollection<ObservableCollection<string>> dataCollection, fill it with data.
Next I generate an ObservableCollection<DataGridRow> rowCollection collection.
In my declarative part of the loop from the first approach, that you can see in the question, I do this
rowCollection.Add(new DataGridRow()
{
HeaderStyle = style,
Item = dataCollection[i] //for the corresponding iteration element
});
This all ends up when setting
MyGrid.ItemsSource = rowCollection;
Till now (very little time of testing), this seems to be what I was looking for, hope it might help someone else aswell.
I have this problem for days and can't find solution for it. I tried all possible solutions i found on internet, but seems like none suits this one.
Thing is that i added repository item to gridControls (i added it through designer, not through code). Then, in code i added data source to that repository lookUpEdit and i have items in dropDown in that column. But when i select item in repository and click on other cell, Selected item in repository is cleared and repository shows null value again...
Any ideas what i did wrong ?
EDIT:
Also, when i click on any cell in my grid, i have delay of second or two, and after that delay clicked cell is focused... Any solutions for all of this?
EDIT:
Don't know what code to show You, because I did all in devExpress designer. Here is part of the code where I set data source to repository item, and i will give You code from designer of that repository item.
private void ConfigureRepositoryItems()
{
BetService.SportManagerClient dbSportManager = new BetService.SportManagerClient();
BetService.BLOddsControlSettings[] oddsControlSettings = dbSportManager.GetOddsControlSettings("", "");
repositoryOddsControlSettings1.DataSource = oddsControlSettings;
}
And here is code from designer:
//
// repositoryOddsCalculationSettings1
//
this.repositoryOddsCalculationSettings1.AutoHeight = false;
this.repositoryOddsCalculationSettings1.Buttons.AddRange(new DevExpress.XtraEditors.Controls.EditorButton[] {
new DevExpress.XtraEditors.Controls.EditorButton(DevExpress.XtraEditors.Controls.ButtonPredefines.Combo)});
this.repositoryOddsCalculationSettings1.Columns.AddRange(new DevExpress.XtraEditors.Controls.LookUpColumnInfo[] {
new DevExpress.XtraEditors.Controls.LookUpColumnInfo("ID", "ID", 20, DevExpress.Utils.FormatType.None, "", false, DevExpress.Utils.HorzAlignment.Default),
new DevExpress.XtraEditors.Controls.LookUpColumnInfo("Name", "Name")});
this.repositoryOddsCalculationSettings1.DisplayMember = "Name";
this.repositoryOddsCalculationSettings1.Name = "repositoryOddsCalculationSettings1";
this.repositoryOddsCalculationSettings1.NullText = "Select Settings";
this.repositoryOddsCalculationSettings1.PopupSizeable = false;
this.repositoryOddsCalculationSettings1.ValueMember = "ID";
For starters check whether the column name in your Grid datasource and the column in your grid control match. The match is case sensitive so name and Name are not same and hence can cause this issue. Secondly make sure the Grid datasource column datatype matches the value type of the LookUpEdit. If the LookupEdit is returning int and the Grid datasource column datatype is string, this alone can cause lots of headaches.
I am working on a windows application using .net 2.0. The UI appl has a datagrid and the data will be populated from the XML file.
The data grid has more than 500 rows. Sorting functionality has implemented. but customer still wants a find option or a search functionality on one of the columns with a text box where user is going to enter first 3 letters and it has to search in the grid and has to show the related rows that starts with the give search criteria.
Any suggestions pls how to implement this....
Thanks
You can use a Filter option in the BindingSource object.
private BindingSource dashBoardBindingSource = new BindingSource();
dashBoardBindingSource.DataSource=<<data source items>>;
dashBoardBindingSource.Filter="Column Name=textbox.text";
datagrid.DataSource = dashBoardBindingSource;
Store off your full collection of data, and then when the filter needs to be performed, create the filtered collection and bind the filtered collection to the grid. Just wire up appropriate text changed events to your filter box, calling FilterGridData. It works nicely when filtering via multi-column as well. Oh, and you don't have to use BindingList here. Use whatever data source you want to bind to the grid - the core of this is just "create the filtered collection by filtering with LINQ."
BindingList<Foo> _allFoos;
private void LoadData(IEnumerable<Foo> dataToDisplayInGrid)
{
this._allFoos = new BindingList<Foo>(dataToDisplayInGrid.ToList());
this.FilterGridData(string.Empty);
}
private void FilterGridData(string filterText)
{
BindingList<Foo> filteredList = null;
if (!string.IsNullOrEmpty(filterText))
{
string lowerCaseFilterText = filterText.ToLower();
IList<Foo> filteredItems = this._allFoos.Where(x => (x.Name ?? string.Empty).ToLower().Contains(lowerCaseFilterText)).ToList();
filteredList = new BindingList<Foo>(filteredItems);
}
else
{
filteredList = new BindingList<Foo>(this._allFoos);
}
dataGrid.DataSource = filteredList;
}
I have a ListView which I'm binding to a CollectionViewSource in code behind with:
collectionView = CollectionViewSource.GetDefaultView(TableView.ItemsSource);
collectionView.SortDescriptions.Clear();
collectionView.SortDescriptions.Add(new SortDescription(propertyName, direction));
The TableView is the ListView, the propertyName is the name of the column I want sorted, and direction is either ascending or descending.
The XAML has the following for ItemSource:
ItemsSource="{Binding Rows}"
The code behind has the following for the Rows:
List<TableRow> rows;
public List<TableRow> Rows
{
get { return rows; }
set
{
rows = value;
UpdateProperty("Rows");
}
}
the update is as follows:
public void Update()
{
...generate a list of rows...
Rows = ...rows...
}
The problem occurs when the Update is called, the list view does update, but loses the sorting set previously on the CollectionViewSource.
If you are "newing" rows then any setting on the prior rows is gone. If you clear (not new) the rows then I think they will hold the setting.
And you don't even want Rows = rows in update.
After assign rows then.
NotifyPropertyChange("Rows");
So the UI know to update
If you are going to new then reassign
collectionView = CollectionViewSource.GetDefaultView(TableView.ItemsSource);
collectionView.SortDescriptions.Clear();
collectionView.SortDescriptions.Add(new SortDescription(propertyName, direction));
Maybe
private List<TableRow> rows = new List<TableRow>();
and have that the only place you new it
The answer is to reapply the sort descriptions after the update, as in:
collectionView = CollectionViewSource.GetDefaultView(TableView.ItemsSource);
collectionView.SortDescriptions.Clear();
collectionView.SortDescriptions.Add(new SortDescription(propertyName, direction));
Then the sorting isn't lost. Refresh on the collection view doesn't help.
If an item property value involved in one of the grouping, sorting and filtering operations is
updated, then the sorting/grouping/filtering will not be done again.
WPF 4.5 introduce a feature called live shaping which shapes the
collection view in live.
See this article for more info.
Did you try to do CollectionView.Refresh() after your update?
If this does not help then I think your problem occurs because you change the source of your CollectionView by assigning new value to your Rows list.
I don't know if it is possible to your code but don't assign new list just clear your previous one and insert new rows there.
if (Rows != null)
Rows.Clear();
Rows.TrimExcess();
else
Rows = new List<TableRow>();
I have a DataGridView that uses databinding, with manually created columns, and this works fine.
However I want the rows' BackColor to be databound as well, and so far my attempts have hit errors.
This is my latest attempt:
dataGridFileTransfer.RowHeadersVisible = false;
dataGridFileTransfer.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
dataGridFileTransfer.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dataGridFileTransfer.MultiSelect = false;
dataGridFileTransfer.ReadOnly = true;
var files = GetReceivedFiles(false).Union(FileDetails.Select(FileStatus.FailedVerification)).ToList();
dataGridFileTransfer.AutoGenerateColumns = false;
string[] displayHeaders = new string[] { COL_NAME, COL_TYPE, COL_CREATED, COL_SIZE, COL_STATUS };
string[] displayProps = new string[] { "Filename", "FileTypeDisplayName", "Created", "Size", "FileStatus" };
for (int i = 0; i < displayHeaders.Length; i++)
{
DataGridViewTextBoxColumn col = new DataGridViewTextBoxColumn();
col.HeaderText = displayHeaders[i];
col.DataPropertyName = displayProps[i];
if (displayHeaders[i] == COL_CREATED)
col.DefaultCellStyle.Format = Constants.DDMMYYYHHMMSS;
dataGridFileTransfer.Columns.Add(col);
}
Binding bi = new Binding("DefaultCellStyle.BackColor", files, "DisplayColor");
dataGridFileTransfer.DataBindings.Add(bi);
dataGridFileTransfer.DataSource = files;
Which is generating an ArguementException:
"Cannot bind to the property
"DefaultCellStyle.BackColor' on the
target control. Parameter name:
PropertyName"
Is it the value of PropertyName that is wrong, or should I binding to an object other than the DataGridView? (i.e. a column?)
Or is the problem that PropertyName cannot be in the form X.Y? I thought I had seen/used this syntax before, but maybe it only works for DataMember?
Any help is much appreciated
I think the problem is files.DisplayColor. files is a collection an has no property DisplayColor but each item of the collection has. So you are trying to bind a not existing property. Further binding collection DataGridView.DataBindings allows you to data bind properties of the control, not of its rows. There is only one DataGridView.DefaultCellStyle.BackColor for all rows. So I believe you end up needing to bind the DefaultCellStyle of each row to the coresponding item from files and I am not sure if this is possible. It might be that the DataGridView creates and deletes rows as required - for example if you perform filtering - and this will destroy the data binding, too.
So, I am not sure if row coloring can be done with data binding, but I personaly doubt it. This would require some really smart logic recognicing 'bind the property DisplayColor of the object data bound to this row to the property DefaultCellStyle.BackColor of this row.'
You could surly implement such an smart data binding. While it would be a great thing, it will be quite complex, too. As a simple solution you could just use the RowPrepaint event to set the correct color for the row.