Initial DataGrid Sorting - wpf

I have a user control that contains a WPF toolkit DataGrid. This control is used in many different places in my app. The grid has no knowledge as to the type of data that will show. Is there a way to initially sort the grid by the first column in ascending order no matter what data the grid is populated with? I don't think I can use a CollectionViewSource because I don't know the PropertyName of the property bound to the first column.

You could hook to an event:
dataGrid.AutoGeneratedColumns += dataGrid_AutoGeneratedColumns;
and sort the first column:
void dataGrid_AutoGeneratedColumns(object sender, EventArgs e)
{
var firstCol = dataGrid.Columns.First();
firstCol.SortDirection = ListSortDirection.Ascending;
dataGrid.Items.SortDescriptions.Add(new SortDescription(firstCol.SortMemberPath, ListSortDirection.Ascending));
}
I would suggest you to create a derived separate DataGrid control, placing this logic there and using the new control to avoid repeating the code every time.
public class CustomDataGrid : DataGrid
{
public DynamicDataGrid()
{ ... }
...
}

Related

DataGrid append another column sort

I have a WPF project - a datagrid with four columns: ColumnOne, ColumnTwo, columnThree, ColumnFour. Is it possible that when user sorts by ColumnOne or ColumnTwo - then code behind adds sorting by ColumnThree as well, so it gets sorted like SortBy("ColumnOne").ThenBy("ColumnThree"). If that matters, ItemsSource for my DataGrid is PagedCollectionView, which supports SortDescriptors.
You have to override DataGrid.OnSorting like in this simple example (but please extend it to your full requirements) and use the custom control instead of the standard DataGrid in your XAML.
public class MyDataGrid : DataGrid
{
protected override void OnSorting(DataGridSortingEventArgs eventArgs)
{
base.OnSorting(eventArgs);
var test = eventArgs.Column;
if (test.Header.ToString() == "ColumnOne" && test.SortDirection.HasValue
&& test.SortDirection.Value.Equals(ListSortDirection.Ascending)
)
{
ICollectionView view = CollectionViewSource.GetDefaultView(this.ItemsSource);
view.SortDescriptions.Add(new SortDescription("ColumnThree", ListSortDirection.Ascending));
view.Refresh();
this.Columns[2].SortDirection = ListSortDirection.Ascending;
}
}
}
The above code handles both the colletion sorting and the SortDirection property setting for ColumnThree in just one case: when the user orders by ColumnOne ascending.

Where is WPF DataGrid DataBindingComplete event?

I need to take some action (e.g. make some cells readonly based on some other cells) after data biinding is completed. In WinForm DataGridView, I used to do it in DataBindingComplete event. However, I couldn't find such an event in WPF DataGrid. What else can I use?
This is what I figured out: DataContextChanged event is the right event to use. The only problem is that the datagrid is not quite ready to be consumed in my code inside this event. However, it works fine if I use Dispatcher.BeginInvoke like this:
Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() => DoSomethingWithGrid()));
Can anybody explain why this is necessary?
Actually, when dealing with WPF DataGrid, I had to use Dispatcher in quite a few cases in order to make it work. Why?
DataContextChanged.
May be you are using threads and datagrid is not thread-safe as all UI components.
I wanted to color my rows depending on their property values, and I tried a lot of events (DataGrid.Initialized, DataContextChanged, AddingNewItem, RowLoaded etc.) along with the BeginInvoke thing, but nothing worked. Then i found:
Loaded
This event did the trick, as it allowed me to iterate through my rows and color them as I wanted.
private void SubjectsList_Loaded(object sender, RoutedEventArgs e)
{
Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(() => ColorMyRows()));
}
The CollorMyRows looks pretty similar to tihs:
private void ColorMyRows()
{
DataGridRow row = null;
for (int i = 0; i < SubjectsList.Items.Count; i++)
{
// get one row
row = SubjectsList.ItemContainerGenerator.ContainerFromIndex(i) as DataGridRow;
if (myConditionIsFulfilled)
{
row.Background = Brushes.PaleGoldenrod; // black'n gold baby
row.ToolTip = "This item fulfills the condition.";
}
else
{
row.Background = Brushes.PaleGreen;
row.ToolTip = "This item does not.";
}
}
}
Note: If You have an ObservableCollection bound to a DataGrid, the index in the loop (index of a DataGrid's row) will correspond to the index in the collection :)
you can bind cells readonly property to a property that changes when other properties of the model changes.
I was thinking exactly like you while ago, but I started to think more in the model more than the view I am not interested in DataGrid but the list that is bounded to, you can do the same
I used it before in a similar situation
public class Model : INotifyPropertyChanged
{
public bool IsChecked
{
get { return isChecked; }
set
{
isChecked = value;
RaisePropertyChanged("IsChecked");
RaisePropertyChanged("Visibilty");
}
}
public Visibility Visibilty
{
get
{
return IsChecked ? Visibility.Visible : Visibility.Hidden;
}
}
}
it was a checkbox in datagrid bound to IsChecked Property and other cells bounded to visibility and it worked for me.
hope this help you.
You can declare a BackgroundWorker and try to fill your GridView in the DoWork event and write your code in the RunWorkerCompleted event

How Can Determine Selected Cell's Value In DataGrid? (WPF)

How can I determine SelectedCell's Value In DataGrid? (WPF)
My DataGrid has 9 coloums and 5 rows and I want to know the Value of clicked row[0]'s Value.
I used this code in Windows Form:
private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
{
var a = dataGridView1[e.ColumnIndex, e.RowIndex].Value;
}
but I don't know an equivalent code in wpf.
You should use DataGrid_SelectedCellsChanged event.
private void dataGrid_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
{
foreach (var item in e.AddedCells)
{
var col = item.Column as DataGridColumn;
var fc = col.GetCellContent(item.Item);
if (fc is CheckBox)
{
Debug.WriteLine("Values" + (fc as CheckBox).IsChecked);
}
else if(fc is TextBlock)
{
Debug.WriteLine("Values" + (fc as TextBlock).Text);
}
//// Like this for all available types of cells
}
}
HTH
Determining a selected cell's value is more of a WinForms thing. WPF is designed to work differently; your UI is meant to be separated from logic. The DataGrid thus becomes an instrument for presentation, not something to be poked and prodded for values.
Instead, with WPF, you want to deal with the objects you have bound to the grid themselves, independent of how they are displayed. Forget the grid - just find the object that is currently "selected" by the user out of a list of bound objects.
The SelectedItem is a property on the grid itself and thanks to WPF's superior binding mechanisms, you can bind this value to a property on a ViewModel via XAML:
ItemsSource="{Binding Orders, Mode=OneWay}"
SelectedItem="{Binding SelectedOrder, Mode=TwoWay}"
When the user selects an item in the grid, the two-way binding will update the SelectedItem property on the ViewModel with the value of that object in that row.
In that way, you don't even have to deal with the knowledge of the grid or the UI.
I hope that makes sense. I know it's a different approach and a different way of thinking coming from WinForms.
I found a solution posted by others in another thread in stackoverflow: WPF Toolkit DataGrid SelectionChanged Getting Cell Value
Try it.
private void dataGrid1_SelectedCellsChanged(object sender, SelectedCellsChangedEventArgs e)
{
var item = e.AddedCells[0];
{
var col = item.Column as DataGridColumn;
var fc = col.GetCellContent(item.Item);
if (fc is CheckBox)
{
}
else if (fc is TextBlock && col.DisplayIndex == 0)
{
textBlock1.Text = (fc as TextBlock).Text;
}
}
}
sometimes binding to SelectedItem doesn't work (depending how crazy your Model has to be. I have to transpose the model, so everything is upside down and normal defaults don't work all the time.
given that,
in dataGrid selectedCellChanged you could access the bound object by:
assuming from previous example of Orders[] where each Order will have an array of SubOrders
foreach (var selectedCell in e.AddedCells)
{
var order = (Order)selectedCell.Item;
var subOrder = order.SubOrders[selectedCell.Column.DisplayIndex-1];
var someValue = subOrder.Value;
}

How to implement "live" filtering in Silverlight

I'm using the new PagedCollectionView as my ItemsSource.
I've bound the CollectionView to a DataGrid, and need the grid to only show values that passes my filter like this:
var oc = new ObservableCollection<User>();
var pc = new PagedCollectionView(oc);
dataGrid.ItemsSource = pc;
Where User class implements INotifyPropertyChanged. I'm applying a filter like this:
private void CheckBox_Checked(object sender, RoutedEventArgs e)
{
pc.Filter += Include;
}
private bool Include(object obj)
{
return (obj as User).Name == filterText.Text;
}
And it works. Problem is that if I update a user the filter result is not updated. If a clear and reapply the filter it works.
Of course I can call the Refresh() method on the PagedCollectionView, but I thought this was possible to do without refreshing the view manually.
What I need is a "live filtering" mechanism. Any idea on how to code?
Scenario is: A application for view live alarms on a system. Say a user has selected to view only rows with "Error" or "Warning" in column0 (that is my filter). When column0 in a row then changes from "Info" to "Warning" that row should be automagically visible (it passes the filter) without me calling Refresh().
Thanks Larsi
Couldn't you just use the AutoCompleteBox control in the Silverlight 3 SDK?

How can I put a Silverlight 3 DataGridCell into edit mode in code?

I want to be able to pick a specific cell in a Silverlight 3.0 DataGrid and put it into edit mode. I can use the VisualTreeManager to locate the cell. How do I switch to edit mode?
Each DataGridCell looks like this in the VisualTreeManager:
System.Windows.Controls.DataGridCell
System.Windows.Controls.Grid
System.Windows.Shapes.Rectangle
System.Windows.Controls.ContentPresenter
System.Windows.Controls.TextBlock
System.Windows.Shapes.Rectangle
System.Windows.Shapes.Rectangle
with the TextBlock containing the text I want to edit.
Update
Following #AnthonyWJones' suggestion, here's how I tried to do this using BeginEdit().
I wanted to keep it simple so I thought I'd pick a column in the first row. Even that proved beyond my SL knowledge! In the end, I get the first row by creating a field called firstRow to hold it:
private DataGridRow firstRow;
added a LoadingRow handler to the DataGrid:
LoadingRow="computersDataGrid_LoadingRow"
and
private void computersDataGrid_LoadingRow(object sender, DataGridRowEventArgs e)
{
if (this.firstRow == null)
this.firstRow = e.Row;
}
and then adding a button to the panel to trigger the edit:
private void Button_Click(object sender, RoutedEventArgs e)
{
this.dataGrid.SelectedItem = this.firstRow;
this.dataGrid.CurrentColumn = this.dataGrid.Columns[4];
this.dataGrid.BeginEdit();
}
I click the button and the correct cell is selected but it doesn't go into edit on the cell. It takes a manual click to achieve that.
I'm not sure why you need to find the DataGridCell using VisualTreeManager nor do I know currently how you would properly start editing . You may get away with simply setting the cell's visual state to editing.
VisualStateManager.GoToState(myDataGridCell, "Editing", true);
I'm not sure how the grid behaves when you do something like the above. You may find things goe a bit pearshaped if you need DataGrid to help you revert changes to a row.
The "standard" approach would be to set the DataGrid SelectedItem property to the item represented by the row, set the CurrrentColum property to the DataGridColumn object that represents to the column in which the cell is found. Then call the BeginEdit method.
I am not able to understand your problem properly, but I had a similar problem
I wanted to make only few of the Grid Cells editable and rest were not. Instead of creating a logic and assigning ReadOnly as true/ false, I did the simple thing.
Mark the whole Grid's cells are writable, IsReadOnly as false
Set the event PreparingCellForEdit and send a callback
When you double click on a cell, it gets in the edit mode
Check whether this cell you want to be editable
If it is allowed to be edited, go ahead
If that cell is ReadOnly, then call CancelEdit
The sample code goes like
namespace foo
{
public class foobar
{
public foobar()
{
sampleGrid = new DataGrid();
sampleGrid.IsReadOnly = false;
sampleGrid.PreparingCellForEdit += new EventHandler<DataGridPreparingCellForEditEventArgs>(sampleGrid_PreparingCellForEdit);
}
void sampleGrid_PreparingCellForEdit(object sender, DataGridsampleGrid_PreparingCellForEditEventArgs e)
{
if (sampleGrid.SelectedItem != null)
{
bool isWritableField = CheckIfWritable()
if (isWritableField == false)
{
sampleGrid.CancelEdit();
}
// continue with your logic
}
}
private DataGrid sampleGrid;
}
}

Resources