i need to get selected value of RepositoryItemRadioGroup
how can i get it ??
for (int i = 0; i < gridView1.RowCount; i++)
{
int rowHandle = gridView1.GetVisibleRowHandle(i);
QuestionsAndAnswers row = (QuestionsAndAnswers)((GridView)gridControl1.MainView).GetRow(rowHandle);
// row.RadioGroup> represent RepositoryItemRadioGroup
//i need to get selected value in row.radiongroup
}
It is not quite clear to me why you need to get access to the RadioGroup directly. If you have bound your GridView to the collection of the QuestionsAndAnswers objects you should not iterate GridView rows itself due to the fact that all the values are already mapped onto the QuestionsAndAnswers properties in a two-way manner. Thus iterate your QuestionsAndAnswers collection directly:
List<QuestionsAndAnswers> qaList = new List<QuestionsAndAnswers> {
new QuestionsAndAnswers(){ Question ="How are you?" }
}
// data binding
gridControl1.DataSource = qaList;
...
void getAnswersBtn_Click(object sender, EventArgs e) {
foreach (QuestionsAndAnswers qa in qaList){
var answer = qa.Answer;
// do something
}
}
If you are bound the GridView in another manner, please, update your question with the detailed description:
- how you bind the GridView to data;
- whether or not you use the approach demonstrated by #Alex.T in your previous question;
- whether or no you read and understand the documentation;
BTW, the main question is - have you contacted the DevExpress team direcly and what their guys said?
for (int i = 0; i < gridView1.RowCount; i++)
{
// can't get answer of last record so i added test line in the last record to get all answers without new last record
if (i==gridView1.RowCount-1)
{
continue;
}
int rowHandle = gridView1.GetVisibleRowHandle(i);
string ResultAnswer = (string)gridView1.GetRowCellValue(rowHandle, "Answer");
string ResultQuest = (string)gridView1.GetRowCellValue(rowHandle, "Question");
}
Related
I want to get selected DataGrid values.
The following codes work correctly, but when the number of rows selected exceeds 10 items, it encounters an error.
error message: "Object reference not set to an instance of an object"
please Help Me.
for (int i = 0; i < MyGrid.SelectedItems.Count; i++)
{
var item = MyGrid.SelectedItems[i];
if (item == null) { return; }
string Name = (MyGrid.Columns[1].GetCellContent(item) as TextBlock).Text;
MessageBox.Show(Name);
}
The following codes work correctly, but when the number of rows selected exceeds 10 items, it encounters an error ...
That's because the visual containers for the items that you don't currently see on the screen have been virtualized away for performance reasons.
What you should do is to cast SelectedItems[i] to whatever your data object type is and then access the property of this object:
for (int i = 0; i < MyGrid.SelectedItems.Count; i++)
{
var item = MyGrid.SelectedItems[i] as YourClass;
if (item == null) { return; }
string name = YourClass.Name;
MessageBox.Show(name);
}
YourClass refers to the type of the items in the DataGrid's ItemsSource:
dataGrid.ItemsSource = new List<YourClass>() { ... };
I am working with the WPF Datagrid and I am trying to enhance/change the copy & paste mechanism.
When the user selects some cells and then hit CTRL + C, the underlying controls is able to catch the CopyingRowClipboardContent event.
this.mainDataGrid.CopyingRowClipboardContent
+= this.DatagridOnCopyingRowClipboardContent;
In this method, some cells are added to both the header and the rows, hence resulting in a "wider" grid.
private void DatagridOnCopyingRowClipboardContent(
object sender,
DataGridRowClipboardEventArgs dataGridRowClipboardEventArgs)
{
// this is fired every time a row is copied
var allContent = dataGridRowClipboardEventArgs.ClipboardRowContent;
allContent.Insert(0, new DataGridClipboardCellContent(
null,
this.mainDataGrid.Columns[0],
"new cell"));
}
At this point I am stuck because I am trying to add an additional row before the header and two after the last row (see image below).
Any ideas? Suggestions?
Please note I am not interested in an MVVM way of doing it here.
Here is a code snippet that might help you.
This snippet is mainly used to retrieve all of your selected data, including headers (I removed the RowHeaders part since you apparently don't need it).
If you have any question please let me know. I left a few part with comments written in capital letters: this is where you should add your own data
The good part of this approach is that it directly works with your DataGrid's ItemsSource and NOT the DataGridCell. The main reason being: if you use DataGridCell on a formatted number for example, you will NOT get the actual value, but just the formatted one (say your source is 14.49 and your StringFormat is N0, you'll just copy 14 if you use a "regular" way)
/// <summary>
/// Handles DataGrid copying with headers
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnCopyingDataGrid(object sender, ExecutedRoutedEventArgs e)
{
// First step: getting the coordinates list of all cells selected
IList<Tuple<int, int>> cellsCoordinatesList = new List<Tuple<int, int>>();
HashSet<int> rowList = new HashSet<int>();
HashSet<int> columnList = new HashSet<int>();
foreach (System.Windows.Controls.DataGridCellInfo cell in this.SelectedCells)
{
int column = cell.Column.DisplayIndex;
int row = this.Items.IndexOf(cell.Item);
cellsCoordinatesList.Add(new Tuple<int, int>(row, column));
if (!rowList.Contains(row))
{
rowList.Add(row);
}
if (!columnList.Contains(column))
{
columnList.Add(column);
}
}
// Second step: Create the table to copy/paste
object[,] arrayToBeCopied = new object[rowList.Count, columnList.Count + 1];
IList<string> colHead = this.ColumnHeaders.Cast<object>().Select(h => h.ToString()).ToList();
for (int row = 0; row < arrayToBeCopied.GetLength(0); row++)
{
for (int column = 0; column < arrayToBeCopied.GetLength(1); column++)
{
if (row == 0)
{
arrayToBeCopied[row, column] = colHead[columnList.ElementAt(column - 1)];
}
else
{
arrayToBeCopied[row, column] = // WHATEVER YOU WANT TO PUT IN THE CLIPBOARD SHOULD BE HERE. THIS SHOULD GET SOME PROPERTY IN YOUR ITEMSSOURCE
}
}
}
// Third step: Converting it into a string
StringBuilder sb = new StringBuilder();
// HERE, ADD YOUR FIRST ROW BEFORE STARTING TO PARSE THE COPIED DATA
for (int row = 0; row < arrayToBeCopied.GetLength(0); row++)
{
for (int column = 0; column < arrayToBeCopied.GetLength(1); column++)
{
sb.Append(arrayToBeCopied[row, column]);
if (column < arrayToBeCopied.GetLength(1) - 1)
{
sb.Append("\t");
}
}
sb.Append("\r\n");
}
// AND HERE, ADD YOUR LAST ROWS BEFORE SETTING THE DATA TO CLIPBOARD
DataObject data = new DataObject();
data.SetData(DataFormats.Text, sb.ToString());
Clipboard.SetDataObject(data);
}
Are you trying to copy the content into e.g. Excel later?
If so, here's what I did:
/// <summary>
/// Copy the data from the data grid to the clipboard
/// </summary>
private void copyDataOfMyDataGridToClipboard(object sender, EventArgs e)
{
// Save selection
int selectedRow = this.myDataGrid.SelectedRows[0].Index;
// Select data which you would like to copy
this.myDataGrid.MultiSelect = true;
this.myDataGrid.SelectAll();
// Prepare data to be copied (that's the interesting part!)
DataObject myGridDataObject = this.myDataGrid.GetClipboardContent();
string firstRow = "FirstRowCommentCell1\t"+ this.someDataInCell2 +"..\r\n";
string lastTwoRows = "\r\nBottomLine1\t" + yourvariables + "\r\nBottomLine2";
string concatenatedData = firstRow + myGridDataObject.GetText() + lastTwoRows;
// Copy into clipboard
Clipboard.SetDataObject(concatenatedData);
// Restore settings
this.myDataGrid.ClearSelection();
this.myDataGrid.MultiSelect = false;
// Restore selection
this.myDataGrid.Rows[selectedRow].Selected = true;
}
In my case I had some static header's which could be easily concatenated with some variables. Important to write are the \t for declaring another cell, \r\n declares the next row
I realize this an older post, but I post this solution for completeness. I could not find a more recent question on copying datagrid rows to clipboard. Using Clipboard.SetData belies the ClipboardRowContent intention.
For my needs, I'm re-pasting back into the e.ClipboardRowContent the row I would like. The cell.Item is all the information I need for each selected row.
Hint: I was getting duplicates without doing an e.ClipboardRowContent.Clear(); after using the e.ClipboardRowContent . I was clearing before and using DataGrid.SelectedItems to build the rows.
private void yourDataGrid_CopyingRowClipboardContent(object sender, DataGridRowClipboardEventArgs e)
{
var dataGridClipboardCellContent = new List<DataGridClipboardCellContent>();
string prevCell = "";
string curCell = "";
foreach (DataGridClipboardCellContent cell in e.ClipboardRowContent)
{
//Gives you access item.Item or item.Content here
//if you are using your struct (data type) you can recast it here curItem = (yourdatatype)item.Item;
curItem = cell.Item.ToString();
if (curCell != prevCell)
dataGridClipboardCellContent.Add(new DataGridClipboardCellContent(item, item.Column, curCell));
prevCell = curCell;
}
e.ClipboardRowContent.Clear();
//Re-paste back into e.ClipboardRowContent, additionally if you have modified/formatted rows to your liking
e.ClipboardRowContent.AddRange(dataGridClipboardCellContent);
}
Using datagridview bound to BindingSource control bound to a LINQ to SQL class, I wonder how to position the bindingSource to a specific record, that is, when I type a Product name in a textbox, the bindingsource should move to that specific product. Here is my code:
In my form FrmFind:
NorthwindDataContext dc;
private void FrmFind_Load(object sender, EventArgs e)
{
dc = new NorthwindDataContext();
var qry = (from p in dc.Products
select p).ToList();
FindAbleBindingList<Product> list = new FindAbleBindingList<Product>(qry);
productBindingSource.DataSource = list.OrderBy(o => o.ProductName);
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
TextBox tb = sender as TextBox;
int index = productBindingSource.Find("ProductName", tb.Text);
if (index >= 0)
{
productBindingSource.Position = index;
}
}
In the program class:
public class FindAbleBindingList<T> : BindingList<T>
{
public FindAbleBindingList()
: base()
{
}
public FindAbleBindingList(List<T> list)
: base(list)
{
}
protected override int FindCore(PropertyDescriptor property, object key)
{
for (int i = 0; i < Count; i++)
{
T item = this[i];
//if (property.GetValue(item).Equals(key))
if (property.GetValue(item).ToString().StartsWith(key.ToString()))
{
return i;
}
}
return -1; // Not found
}
}
How can I implement the find method to make it work?
You can combine the BindingSource.Find() method with the Position property.
For example, if you have something like this in your TextBox changed event handler:
private void textBox1_TextChanged(object sender, EventArgs e)
{
TextBox tb = sender as TextBox;
int index = bs.Find("Product", tb.Text);
if (index >= 0)
{
bs.Position = index;
}
}
This of course will depend on a lot of things like the particular implementation of the Find method the data source for the binding source has.
In a question you asked a little while ago I gave you an implementation for Find which worked with full matches. Below is a slightly different implementation that will look at the start of the property being inspected:
protected override int FindCore(PropertyDescriptor property, object key)
{
// Simple iteration:
for (int i = 0; i < Count; i++)
{
T item = this[i];
if (property.GetValue(item).ToString().StartsWith(key.ToString()))
{
return i;
}
}
return -1; // Not found
}
Do note that the above method is case sensitive - you can change StartsWith to be case insensitive if you need.
One key thing to note about the way .Net works is that the actual type of an object is not sufficient all the time - the declared type is what consuming code knows about.
This is the reason why you get a NotSupported exception when calling the Find method, even though your BindingList implementation has a Find method - the code that receives this binding list doesn't know about the Find.
The reason for that is in these lines of code:
dc = new NorthwindDataContext();
var qry = (from p in dc.Products
select p).ToList();
FindAbleBindingList<Product> list = new FindAbleBindingList<Product>(qry);
productBindingSource.DataSource = list.OrderBy(o => o.ProductName);
When you set the data source for the binding source you include the extension method OrderBy - Checking this shows that it returns IOrderedEnumerable, an interface described here on MSDN. Note that this interface has no Find method, so even though the underlying FindableBindingList<T> supports Find the binding source doesn't know about it.
There are several solutions (the best is in my opinion to extend your FindableBindingList to also support sorting and sort the list) but the quickest for your current code is to sort earlier like so:
dc = new NorthwindDataContext();
var qry = (from p in dc.Products
select p).OrderBy(p => p.ProductName).ToList();
FindAbleBindingList<Product> list = new FindAbleBindingList<Product>(qry);
productBindingSource.DataSource = list;
In WinForms there are no entirely out of the box solutions for the things you are trying to do - they all need a little bit of custom code that you need to put together to match just your own requirements.
I took a different approach. I figured, programmatically, every record must be checked until a match is found, so I just iterated using the MoveNext method until I found a match. Unsure if the starting position would be the First record or not, so I used the MoveFirst method to ensure that is was.
There is one assumption, and that is that what you are searching for is unique in that column. In my case, I was looking to match an Identity integer.
int seekID;
this.EntityTableBindingSource.MoveFirst();
if (seekID > 0)
{
foreach (EntityTable sd in EntityTableBindingSource)
{
if (sd.ID != seekID)
{
this.t_EntityTableBindingSource.MoveNext();
}
else
{
break;
}
}
}
I didn't really care for either answer provided. Here is what I came up with for my problem:
// Create a list of items in the BindingSource and use labda to find your row:
var QuickAccessCode = customerListBindingSource.List.OfType<CustomerList>()
.ToList().Find(f => f.QuickAccessCode == txtQAC.Text);
// Then use indexOf to find the object in your bindingSource:
var pos = customerListBindingSource.IndexOf(QuickAccessCode);
if (pos < 0)
{
MessageBox.Show("Could not find " + txtQAC.Text);
}
else
{
mainFrm.customerListBindingSource.Position = pos;
}
I have my custom grid which inherits from DataGrid (from WPFToolkit) that has around 10000 items in it. The built-in sort is very slow. As such I have written a separate class that keeps a running sort of all the DataRowView items for each column (this works because additions and removals from the grid are extremely seldom, if ever).
The grid has AutoGenerateColumns='True' and is bound to the DefaultView of a DataTable.
I override the OnSorting to know when a column header is clicked and try to replace the ItemsSource of the grid with my sorted list of DataRowView. Below is the method:
private void RefreshItems()
{
if (_updating || _multiIndexer.Count == 0)
return;
try
{
_updating = true;
this.AutoGenerateColumns = false;
// replace the itemssource with my maintained and sorted list of
// DataRowView items
this.ItemsSource = _multiIndexer.ToList();
}
finally
{
//this.AutoGenerateColumns = true;
_updating = false;
}
}
The problem is that I destroy the columns that existed from the auto-generation. Also, I'm only left with columns that match the properties from DataRowView.
I believe the best approach would be to create a DataView from my sorted list of DataRowView and pass that to the ItemsSource but I have yet to have any success.
Any ideas how to pass a new list of rows to the ItemsSource or Items without destroying the auto-generated columns? Generating all my columns manually is not an option.
Cheers,
Sean
I'm not aware of a way to get rid of re-creation of the columns. I'm having a similar problem, and usually I'm using ICollectionView and DeferRefresh. Because you don't have to do it in your actual model object. But I'm not sure if it works with DataViews as well as good as with List.
what do you mean by "its very slow"?
if i take a datagrid and bind a datatable with 50000row and 20columns to it. it seems fast to me
viewmodel
public class Viewmodel
{
public DataTable MyData { get; set; }
public Viewmodel()
{
this.MyData = new DataTable();
for (int i = 0; i < 20; i++)
{
this.MyData.Columns.Add("Col" + i);
}
for (int i = 0; i < 50000; i++)
{
var row = MyData.NewRow();
for (int j = 0; j < 20; j++)
{
row["Col" + j] = string.Format("{0} Row, Col {1}", i, j);
}
this.MyData.Rows.Add(row);
}
}
}
window
<DataGrid ItemsSource="{Binding MyData}" AutoGenerateColumns="True">
</DataGrid>
edit: its .net4.0
I have a List<T> bound to a datagrid and am trying to figure out how to save all the changes to the database (using Entity Framework), not just one row at a time; OR, at least a better way to commit changes from the datagrid to the database. I am using the MVVM pattern.
Here is what I have as far as saving one row:
private static PROJECT_EXPENSEEntities _service;
//The default constructor
public ProjectExpenseItemsRepository()
{
if (_service == null)
{
_service = new PROJECT_EXPENSEEntities();
}
}
public void AddProjectExpenseItems(int projectExpenseID)
{
project_expense_items p = new project_expense_items();
if (entityList != null)
{
p.project_expense_id = projectExpenseID;
p.item_number = entityList.ItemNumber;
p.item_description = entityList.ItemDescription;
p.item_unit_price = entityList.ItemUnitPrice;
p.item_qty = entityList.ItemQty;
p.supplier_name = entityList.SupplierName;
p.create_date = System.DateTime.Today;
_service.AddObject("project_expense_items", p);
_service.SaveChanges();
}
}
However, I would prefer to send the changes to all rows in the datagrid at once:
public void AddProjectExpenseItems(List<ProjectExpenseItemsBO> entityList, int projectExpenseID)
{
project_expense_items p = new project_expense_items();
if (entityList != null)
{
// not sure what goes here, do I need a loop for each item in the list?
// I have a feeling I am going in the wrong direction with this...
_service.AddObject("project_expense_items", entityList);
_service.SaveChanges();
}
}
I haven't found many good examples on the web. If someone could point me to an example I'd appreciate it.
Yes. You can simply loop through your list and SaveChanges once after adding/modifying/deleting the items.
Of course, you would likely want to load and display items from the database, so binding directly to EntityCollection<project_expense_items> _service.project_expense_items would be more effective than using a list of detached entities. Entity Framework will track changes for you.
See this: http://msdn.microsoft.com/en-us/library/bb896269.aspx