Adding Row to DataGrid Silverlight - c#

I have a Datagrid which show data from a database.
each time i load the data from my data vase the old data is earsed and the is replaced with the new data.
What i want is for the pervious row to remain and the new data to be appened to the end of the datagrid.
my code is as shown below :
public MainPage()
{
InitializeComponent();
dataGrid1.Columns.Add(new DataGridTextColumn { Header = "Year", Binding = new System.Windows.Data.Binding("year") });
dataGrid1.Columns.Add(new DataGridTextColumn { Header = "One", Binding = new System.Windows.Data.Binding("One") });
dataGrid1.Columns.Add(new DataGridTextColumn { Header = "Two", Binding = new System.Windows.Data.Binding("Two") });
dataGrid1.Columns.Add(new DataGridTextColumn { Header = "Three", Binding = new System.Windows.Data.Binding("Three") });
}
void client_DoWorkCompleted(object sender, DoWorkCompletedEventArgs e)
{
dataGrid1.ItemsSource = e.Result;
}
how do i append the new data from the database rather than overwriting the data ?? .

One very simple and no-frills way to do it (because you don't appear to be using a regular MVVM approach), is to create a new list (IEnumerable) which unions both the old list and the new list, then assign it back to the ItemsSource property:
List<MyObjects> abc = new List<MyObjects>(dataGrid2.ItemsSource).Union(e.Result);
dataGrid2.ItemsSource = abc;
I've broken this on to two lines to make it easier to understand.
If you were using a ViewModel, then the property that is bound to the DataGrid would be a List<>, and then you can just do an AddRange(e.Result) to that property, and due to the beauty of data binding (and notifying) it will automatically show up in your datagrid.

Related

DataGridView BackColor not setting on initialise, but sets on refresh

So I'm creating a simple address book project which will fetch contacts from sql server and display these in a datagridview. When the program first loads, it automatically gets the contacts from the database and populates the grid. I have a method that styles the grid after population - it simply sets some grid properties which all seem to have an affect except the default cell style back color which at first does nothing.
However, when I clear the grid through a custom method and then repopulate the grid through calling the same method as I do when the program first loads, the back color of the grid sets correctly which seems absolutely bizarre. Here's my code:
private void BindGridWithContacts() //Populate grid with list of contact objects - ordered by address id
{
bindingSource.DataSource = typeof(Contacts);
List<Contacts> sortedList = contacts.OrderBy(x => x.AddressId).ToList();
contacts = new BindingList<Contacts>(sortedList); //Orders the list of contacts by ID
foreach (var contact in contacts) //Add each contact object to binding source
{
bindingSource.Add(contact);
}
grdContacts.DataSource = bindingSource; //data grids datasource = binding source
grdContacts.StyleGrid();
}
I've read elsewhere on stack overflow that it can be tricky to style datagrids that are bound to a datasource so at first I just accepted it wouldn't be possible. But then once I called my clear grid method:
private void ClearGrid()
{
grdContacts.DataSource = null;
bindingSource.DataSource = null;
bindingSource.Clear();
}
And then call the BindGridWithContacts() method again, the grid styles with my desired colours. Here is my StyleGrid() method:
public void StyleGrid()
{
//Colour rows - currently not working
for (int i = 0; i < this.RowCount; i++)
{
this.Rows[i].DefaultCellStyle.BackColor = i % 2 != 0 ? Color.Lavender : Color.AliceBlue;
}
//Set Column Widths
this.Columns["AddressId"].Width = 70;
this.Columns["EmailAddress"].Width = 150;
this.Columns["Hobby"].Width = 148;
this.Columns["Forename"].Width = 115;
this.Columns["Surname"].Width = 115;
this.ReadOnly = true;
this.RowHeadersVisible = false;
this.RowHeadersDefaultCellStyle.BackColor = Color.Lavender;
}
Note: the grid will style properly every time the style grid method is called except the first time.
Any suggestions?? Thanks

CheckBox rendered as a Label

I have a dynamically generated ListView, that uses data binding to allow editing some Boolean values through CheckBox. I use a IValueConverter to generate the ListView's columns (like in this answer):
public object Convert (object Value, Type EntryType, object Parameter, System.Globalization.CultureInfo Culture)
{
var Config = Value as ColumnConfig;
if (Config != null)
{
var GridView = new GridView ();
Binding NameBinding = new Binding ("Name");
GridViewColumn BaseColumn = new GridViewColumn { Header = "Settings",
DisplayMemberBinding = NameBinding,
Width = 125,
CellTemplate = new DataTemplate ()};
GridView.Columns.Add (BaseColumn);
foreach (Column CurrentColumn in Config.Columns)
{
Binding NewBinding = new Binding (CurrentColumn.DataField);
FrameworkElementFactory FEF = new FrameworkElementFactory (typeof (CheckBox));
FEF.SetBinding (CheckBox.IsCheckedProperty, NewBinding);
GridViewColumn GVColumn = new GridViewColumn
{
Header = CurrentColumn.Header,
DisplayMemberBinding = NewBinding
};
var DTemplate = new DataTemplate ();
DTemplate.VisualTree = FEF;
GVColumn.CellTemplate = DTemplate;
GridView.Columns.Add (GVColumn);
}
return GridView;
}
return Binding.DoNothing;
}
Which is used like so in the XAML:
<ListView Margin="2" ItemContainerStyle="{StaticResource LineHighlightListView}"
ItemsSource="{Binding InMatrixList}"
View="{Binding InMatrixColumns, Converter={StaticResource ConvertItemsToDynamicGridView}}" />
The columns' headers are generated elsewhere. The code should take in a ColumnConfig items, and create GridViewColumn objects that have a ChechBox databound to some other value elsewhere. However, all I am getting is columns with text in place of the CheckBoxes. The text is correct, so the data binding is valid, but the FrameworkElementFactory object is not working as expected.
Why are the checkboxes rendered/converted to textboxes?
Rule: avoid that way to dynamically compose a template.
I had a similar problem and I have solved as follows:
//see: http://www.codeproject.com/Articles/444371/Creating-WPF-Data-Templates-in-Code-The-Right-Way
private static DataTemplate CreateTemplate(UniprogCellVM cell)
{
var tcell = cell.GetType();
var sb = new StringBuilder();
sb.AppendFormat("<DataTemplate DataType=\"{{x:Type local:{0}}}\">", tcell.Name);
sb.Append("<local:UniprogCellControl ");
sb.Append("Content=\"{Binding Path=.}\" ");
sb.Append("Header=\"{Binding Path=.}\" ");
sb.AppendFormat("Style=\"{{DynamicResource Root{0}BoxStyleKey}}\" ", cell.Interaction);
sb.Append(">");
sb.Append("</local:UniprogCellControl>");
sb.Append("</DataTemplate>");
var context = new ParserContext();
context.XamlTypeMapper = new XamlTypeMapper(new string[0]);
context.XamlTypeMapper.AddMappingProcessingInstruction("local", tcell.Namespace, tcell.Assembly.FullName);
context.XmlnsDictionary.Add("", "http://schemas.microsoft.com/winfx/2006/xaml/presentation");
context.XmlnsDictionary.Add("x", "http://schemas.microsoft.com/winfx/2006/xaml");
context.XmlnsDictionary.Add("local", "local");
var template = (DataTemplate)XamlReader.Parse(sb.ToString(), context);
return template;
}
Basically, you should compose a fully-valid XAML of your template, then parse it with the parser.
Since the text-composition is a trivial task, you may pass any parameter in the creation function (as in my example above).
Just a final note: this approach is useful, but requires a computational effort because the runtime parsing and compilation. Avoid a large number of items created in such way.

CSharp Android Xamarin SQL ListView

I'm trying to read the contents from a SQL query and store them into a listView (Android-C#). For whatever reason the listView is only showing the last item. When I debug through the code, I see that it is going through every result, however, I cannot get the adapter to display all the results. Maybe I am doing something wrong with the adapter. Any ideas?
Code:
while (rdr.Read ()) {
string[] Text = new string[] { (rdr[0])+ System.Environment.NewLine} ;
ListView mylistview = FindViewById<ListView> (Resource.Id.listView1);
var myAdapter = new ArrayAdapter(this, Android.Resource.Layout.SimpleListItem1 , Text);
mylistview.Adapter = myAdapter;
}
For whatever reason the listView is only showing the last item.
Because in while every time new Text Array is created and pass to ArrayAdapter so last is Array data is showing in ListView.
To show all items in ListView,create Adapter object outside while loop and use ArrayList which will grow according to data-size:
ArrayList listText = new ArrayList();
while (rdr.Read ()) {
listText.add((rdr[0])+ System.Environment.NewLine);
}
ListView mylistview = FindViewById<ListView> (Resource.Id.listView1);
var myAdapter = new ArrayAdapter(this,
Android.Resource.Layout.SimpleListItem1,listText);
mylistview.Adapter = myAdapter;

CollectionViewSource.Filter doesn't work

I have a window in my application with following resources:
<Window.Resources>
<ResourceDictionary>
<Data:IssueRecords x:Key="DataSource"/>
<CollectionViewSource x:Key="DataCollection" Source="{StaticResource DataSource}"
Filter="CollectionViewSource_Filter">
</CollectionViewSource>
</ResourceDictionary>
</Window.Resources>
There is a standard event handler - a method, called CollectionViewSource_Filter and DataGrid, to apply filter to. After my window loads, everything works perfectly, including filters.
For applying filters, I call a ReloadGrid method...
private void ReloadGrid(object sender, RoutedEventArgs e)
{
CollectionViewSource.GetDefaultView(GridData.ItemsSource).Refresh();
}
But, when user does any action, which makes changes to my database (delete, modify or create new), I need to reload those data sources, so I call...
private void ReloadDataSources()
{
var dataSource = this.FindResource("DataSource") as IStockRecords;
dataSource.ReloadData();
var dataCollection = this.FindResource("DataCollection") as CollectionViewSource;
dataCollection = new CollectionViewSource() { Source = dataSource };
dataCollection.Filter += new FilterEventHandler(CollectionViewSource_Filter);
Binding binding = new Binding() { Source = dataCollection };
BindingOperations.SetBinding(GridData, DataGrid.ItemsSourceProperty, binding);
}
I think, I do everything, what is needed to read actual data from database and reload the datasources in my window. But when I use any filter, after I call ReloadDataSources(), the filter event is not being used anymore. I debugged a source code and Refresh method doesn't invoke CollectionViewSource_Filter, even when I set FilterEventHandler...
Am i missing anything?
Thanks, JiKra
You need to reset your default view after you modify the data source.
CollectionViewSource.GetDefaultView(GridData.ItemsSource).Refresh();
Try this....
private void ReloadDataSources()
{
var dataSource = this.FindResource("DataSource") as IStockRecords;
dataSource.ReloadData();
var dataCollection = this.FindResource("DataCollection") as CollectionViewSource;
// Remove the current event handler
dataCollection.Filter -= new FilterEventHandler(CollectionViewSource_Filter);
// Set your new data source
dataCollection = new CollectionViewSource() { Source = dataSource };
// Read your handler
dataCollection.Filter += new FilterEventHandler(CollectionViewSource_Filter);
// Now reset your filter
dataCollection .GetDefaultView(GridData.ItemsSource).Refresh();
Binding binding = new Binding() { Source = dataCollection };
BindingOperations.SetBinding(GridData, DataGrid.ItemsSourceProperty, binding);
}
OK, there seems to be a problem when I recreated CollectionViewSource object. So, the final version is...
private void ReloadDataSources()
{
var dataSource = this.FindResource("DataSource") as IStockRecords;
dataSource.ReloadData();
var dataCollection = this.FindResource("DataCollection") as CollectionViewSource;
//here I just had to refresh collection's view, not to create a new one
dataCollection.View.Refresh();
Binding binding = new Binding() { Source = dataCollection };
BindingOperations.SetBinding(GridData, DataGrid.ItemsSourceProperty, binding);
}
Thank you both for your effort...
JiKra

Binding a ListBox's SelectedItem in the presence of BindingNavigator

I'm trying to bind a ListBox's SelectedItem data to a property. The following code is an example:
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace BindingFailure
{
static class Program
{
class OuterObject
{
public string selected { get; set; }
public List<string> strings { get; set; }
}
public static void Main()
{
List<OuterObject> objs = new List<OuterObject>()
{
new OuterObject(), new OuterObject()
};
objs[0].strings = new List<string> { "one", "two", "three" };
objs[1].strings = new List<string> { "four", "five", "six" };
Form form = new Form();
BindingSource obs = new BindingSource(objs, null),
ibs = new BindingSource(obs, "strings");
BindingNavigator nav = new BindingNavigator(obs);
ListBox lbox = new ListBox();
lbox.DataSource = ibs;
lbox.DataBindings.Add(new Binding("SelectedItem", obs, "selected"));
form.Controls.Add(nav);
form.Controls.Add(lbox);
lbox.Location = new System.Drawing.Point(30, 30);
Application.Run(form);
}
}
}
If you just select an item, move forward, select an item and then exit, it works as expected. But if you switch back and forth between the two outer objects with the navigator, the selected item seems to be overwritten with an incorrect value. It appears that every time the BindingNavigator moves to an element, the ListBox is told to move to the first item in its collection and thus overwrites whatever value used to be in the variable bound to its SelectedItem.
Ideas on how to fix this? Thanks in advance.
EDIT: Here is an archive of the example project, including a debug binary.
http://www.mediafire.com/?dzmqmz0mynj
EDIT: Here is the helper function based on the accepted answer:
public static void Bind(ListControl list, BindingSource outersource, string dataMember)
{
Binding bindSel = new Binding("SelectedItem", outersource, dataMember);
list.DataBindings.Add(bindSel);
outersource.CurrentChanged += delegate
{
list.BeginInvoke(new MethodInvoker(bindSel.ReadValue));
};
}
It happens because SelectedItem update happens before listbox is updated. So on the first stage listbox cannot "accept" value from different form record since it contains no such record and then it cannot restore selection after listbox items are repopulated.
The solution (one of) is to force SelectedItem binding to reapply after current form record is changed. First we give the binding a name:
//lbox.DataBindings.Add(new Binding("SelectedIndex", obs, "Index"));
var selItemBinding = new Binding("SelectedItem", obs, "selected");
lbox.DataBindings.Add(selItemBinding);
Than we have to reapply binding (notice BeginInvoke is essential to apply binding after listbox is repopulated):
obs.CurrentChanged += delegate
{
form.BeginInvoke(new MethodInvoker(selItemBinding.ReadValue));
};
You could also create simple helper method that will do all this tricks in one call.

Categories

Resources