Rebinding Observable collection wpf - wpf

I created a WPF window to bind data in Datagrid based on the selection of date by the user.BY default it loads for a specific date which works fine. But When the date is changed, the grid is showing empty rows.but the observable collection I used is having data. The observable collection is of DataTable type.
Note: I used to set itemsource=null when there are no records since I am using the same grid for 2 different tables based on a radio button check.
I have set the public variable as Binding variable in the XAML, used INOtifyChanged interface.
My issues is when the same collection reloads, the Datagrid failed to bind and shows empty rows but generating columns. When the previous attempt set the itemsource as null, the current loading failed to load the column also.
Any generic scenario, I am facing,? Please help
My Code:
private ObservableCollection<DataTable> custInfoCol = new ObservableCollection<DataTable>();
public event PropertyChangedEventHandler PropertyChanged = delegate { };
private DataTable _CustInfo;
public DataTable CustInfo
{
get { return _CustInfo; }
set
{
_CustInfo = value;
PropertyChanged(this, new PropertyChangedEventArgs("CustInfo"));
}
}
private void rbPrev_Checked(object sender, RoutedEventArgs e)
{
custInfoCol.Clear();
custInfoCol.Add(CustInfo = showcustomer(cid));
}

Sounds like you might need to change the UpdateSourceTrigger in your binding:
Height="{Binding Height, ElementName=Day, UpdateSourceTrigger=PropertyChanged}"
I've had problems with the default behavior of it before, so now I just type it out every time.
https://msdn.microsoft.com/en-us/library/system.windows.data.binding.updatesourcetrigger%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396

Based on this piece of code you provided in the comment:
<DataGrid ItemsSource="{Binding custInfo,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
DataContext="{Binding RelativeSource={RelativeSource AncestorType=Window}}"
I think there is typo in the Xaml. You should use ... Binding CustInfo... with capital C.
Of course, if AutoGenerateColumns is true, then I guess this is not the problem, because you wrote
Datagrid failed to bind and shows empty rows but generating columns
Which the Binding works. Therefore, you need to check showcustomer(cid) and make sure it contains the required DataRows.

I fixed the issue which is very simple. I had created the datatable instance as global and i changed it to local just before loading the executereader() method.It resolved the issue.

Related

MVVM WPF databound checkbox won't firing events to the ViewModel for Checked and Unchecked states

I have a databound CheckBox inside a DataGrid, using WPF and MVVM;
<DataGridTemplateColumn Width="80" Header="Enabled">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding Path=IsEnabled, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Name="theCheckbox" HorizontalAlignment="Center"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
This works fine and the CheckBox is being checked when IsEnabled is set. IsEnabled is a property in my collection of objects which I have bound to the DataGrid. What I want to do is to be able to validate if a specific rows CheckBox within the DataGrid should be allowed to be checked when the user selects it and if not remove their check and display a warning message something like "Row 1 cannot be checked without rows 5 and 9 being checked". I found out how to do this using code behind using the Checked and Unchecked properties of the CheckBox, but I am using MVVM and therefore want to handle things in the ViewModel associated with the View the DataGrid and CheckBox are in. How do I do this? I need a way of passing the Id field of the DataRow through too in order to identify which row I am working on, for arguments sake lets say the Id field is called BorderId.
Implement IDataErrorInfo on your objects, which is WPF's default interface for validation, and setup your validation code to validate if the checkbox can be checked or not
This is actually a bit trickier than normal because you are validating a property on your data item using data that doesn't exist on the data item, which means you need to provide some way of attaching unrelated validation to your object at run time.
The solution I usually use is to expose a ValidationDelegate from my object, which other objects can use to attach additional validation to the object. The code for it is posted on my blog, and it allows you to attach validation to your object like this:
public class MyViewModel
{
// Keeping this generic to reduce code here, but it
// should be a full property with PropertyChange notification
public ObservableCollection<SomeObject> SomeCollection { get; set; }
public MyViewModel()
{
SomeCollection = LoadDataGridObjects();
// Add the validation delegate to each object
foreach(var item in SomeCollection)
item.AddValidationDelegate(ValidateObject);
}
// Validation Delegate to verify the right checkboxes are checked
private string ValidateObject(object sender, string propertyName)
{
if (propertyName == "IsChecked")
{
var item = (SomeObject)sender;
if (item.Id == 1
&& !SomeCollection.First(p => p.Id == 5).IsChecked
&& !SomeCollection.First(p => p.Id == 9).IsChecked)
{
return "Row 1 cannot be checked without rows 5 and 9 being checked";
}
}
return null;
}
}
Could you add to the PropertyChanged event handler of each item in your list? Something like this:
foreach (Object x in List)
{
x.PropertyChanged += OnItemChanged;
}
Then in the change listener you can do whatever you like with the sender object including changing the IsEnabled property.
void OnItemChanged(object sender, PropertyChangedEventArgs e)
{
// change sender object properties or set bound label text here
}
If I understand your question, you have a checkbox and you want to control if it can be checked or not. Instead of letting the user check it at all times and push back an error message if you don't want to allow it, make your checkbox inactive in the first place if you don't want the user to check it.
To do this through your ViewModel (which is of course your DataContext), you need to bind the Checkbox's "Command" to a custom command in your ViewModel.
A WPF Command will provide the following functionality:
Automatically enable/disable a control based on custom logic
Handles the "action" event of your control (on a button of checkbox, it would be "click").

Why does one of MY WPF DataGrids give the "'EditItem' is not allowed for this view" exception?

I have read all the Q&A I could find here and on the MS forums about this exception, and tried most of the suggestions that I understood, and a few others. It seems that this exception can come up for a wide range of causes.
As with others, I have a WPF DataGrid bound to a collection, which throws this exception when one tries to edit one of the cells. They are set to be write-able, the collection is an ObservableCollection, I've implemented get and set handlers which send notification messages.
The suggestions I haven't tried are the ones involving implementing IList's non-generic interface, because I have no idea what I would do to do that. Also, I have many DataGrids bound to various lists and collections in my app which work, and this one used to work when it was bound to a LINQ collection.
Please help me figure out what I need to do here.
The Data Grid is:
<DataGrid Name="dgIngredients" Margin="567,32,0,44" Width="360" ItemsSource="{Binding}" IsReadOnly="False"
AutoGenerateColumns="False" HorizontalAlignment="Left" CanUserAddRows="False" CanUserDeleteRows="False">
<DataGrid.Columns>
<DataGridTextColumn Width="63" Header="Percent" Binding="{Binding Preference}" IsReadOnly="False" />
<DataGridTextColumn SortDirection="Descending" Width="301" Header="Ingredient" Binding="{Binding Ingredient}" IsReadOnly="True" CanUserSort="True" CanUserReorder="False" />
</DataGrid.Columns>
</DataGrid>
The column being edited is the non-read-only one, Preference.
The collection is:
private ObservableCollection<RAM_Ingredient> MemberIngredientPrefs = new ObservableCollection<RAM_Ingredient>();
The binding is:
dgIngredients.DataContext = MemberIngredientPrefs.OrderBy("Ingredient",true);
RAM_Ingredient is:
public class RAM_Ingredient : INotifyPropertyChanged
etc.
Where RAM_Ingredient.Preference is:
private int _Preference;
public int Preference
{
get
{
return _Preference;
}
set
{
// This is needed to send notification of changes (and to not throw an exception on grid edit!):
if ((_Preference != value))
{
SendPropertyChanging();
_Preference = value;
SendPropertyChanged("Preference");
}
}
}
The exception is:
System.InvalidOperationException was unhandled
Message='EditItem' is not allowed for this view.
Source=PresentationFramework
StackTrace:
at System.Windows.Controls.ItemCollection.System.ComponentModel.IEditableCollectionView.EditItem(Object item)
at System.Windows.Controls.DataGrid.EditRowItem(Object rowItem)
at System.Windows.Controls.DataGrid.OnExecutedBeginEdit(ExecutedRoutedEventArgs e)
etc...
I have also this problem, And found that the point here is that we can not edit a IEnumerable in a DataGrid, only a list can be edited.
therefore we didn't need to create a new class, its works also on a LINQ query with anonymous return type. it's need only to be a list.
here is a sample of my code:
dtgPrdcts.ItemsSource= ProductLists.Select(Function(x) New With {.ListTitle = x.ListTitle, .ProductID = x.ProductID, .License = "", .ForRemove = True}).ToList
I still don't know what specifically caused the problem, but I managed to work around it, and I'm not sure how much of what I did was overkill, but it works.
I created a new class just for the purpose of holding the data in the DataGrid rows. I make a List of objects of this class and fill it in and bind it to the DataGrid as I was doing before. I also added the usual stuff and nonsense for getting Change Notification to work (probably overkill) and I had to re-define a comparison function in a different way to get it to sort because of that whole comedy situation.
i.e.
List<UsablePref> MemberIngredientPrefs = new List<UsablePref>();
...
foreach (RAM_Ingredient ingredient in App.Ingredients)
{
ingredient.GetPreferences(EditorMember);
UsablePref pref = new UsablePref();
pref.Ingredient = ingredient.Ingredient;
pref.IngredientID = ingredient.IngredientID;
pref.Preference = ingredient.Preference;
MemberIngredientPrefs.Add(pref);
}
// Sort alphabetically by ingredient name,
MemberIngredientPrefs.Sort(UsablePref.CompareByName);
// and bind the ingredient prefs DataGrid to its corresponding List
dgIngredients.DataContext = MemberIngredientPrefs;
I had this same problem trying to create a list of rows from a join; since the LINQ query returns an IEnumerable, I had the DataGrid bound to that IEnumerable; this worked fine for readonly and oddly worked with ComboBoxes and some other custom controls I used, but plain text editing threw the InvalidOperationException. The solution was an ObservableCollection in place of the IEnumerable; basically from:
BoundView = (/*LINQ QUERY*/); // is IEnumerable<CustomJoinObject>
to
BoundView = new ObservableCollection<CustomJoinObject>(/*LINQ QUERY*/);
In both cases BoundView is the DataContext for the DataGrid.
I'm assuming this happens because IEnumerable doesn't have the machinery to support a datagrid, whereas ObservableCollection does.
The model class needs to implement the interface INotifyPropertyChanged coming from the namespace System.ComponentModel.
Class example:
public class Exemple : INotifyPropertyChanged
{
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
#endregion INotifyPropertyChanged Members
}
even if this thread is old, it could help someone.
The issue is that LINQ returns a IEnumerable, as said VaelynPhi, but the cause is that editing a datagrid requires a source that implements IEditableCollectionView.
You can then use a list, an observableCollection or any collection implementing this interface.
I found this solution thanks to Shoe who gave an answer on this tread.
In my case this exception was thrown when I wanted to edit some cells. The problem was of wrong collection type bound to ItemSource => when I switched from IEnumerable<T> to ObservableCollection<T> everything works correctly.

WPF Toolkit datagrid /doesn’t refresh data

H I use SQL CE and LINQ. I bind property typeof Table on ItemSource of Datagrid control from WPF Toolkit.
Something like this.
public Table<TestNick> MySource
{
get { return _tab; }
set
{
_tab = value;
NotifyPropertyChanged("MySource");
}
}
<Controls:DataGrid Name="Dg"
ItemsSource="{Binding Path=MySource, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"
Grid.Row="0"/>
I retrieve data from DB with LINQ:
const string connStr = #"Spiri_SQL_CE_DB.sdf";
_dc = new Spiri_SQL_CE_DB(connStr);
MySource = _dc.TestNick;
If I add a breakpoint on last line I see all values from tables TestNick, but it doesn’t load this data in DataGrid.
What is bad?
EDITED:
I check the ItemSource of DataGrid control in code behind, the item source is correct but I see in DataGrid (view) "old data".
So binding must be correct, problem is that DataGrid control doesn’t refresh data.
Make sure datagrid autogeneratecolumns is true
While running check the output window if there are any binding issues
Another trick is to put a button on the view and write a code behind function on click of that button to debug whats the datagrid itemsource, if its empty try to invoke viewModel/Model's getDatagridData function and then see if it loads, in case it loads that means your NotifyPropertyChanged is not yet functional

WPF bindings not refreshing

I have a listbox on my form that looks like this:
<ListBox Name="lbResults" SelectionChanged="lbResults_SelectionChanged"/>
I am binding the following collection to it:
ObservableCollection<Hand> oHands = new ObservableCollection<Hand>();
using the following code:
lbResults.DataContext = oHands;
Binding binding = new Binding();
lbResults.SetBinding(ListBox.ItemsSourceProperty, binding);
The oHands collection gets populated via a background worker that announces via an event whenever a new Hand object is available. The ListBox refreshes perfectly when something is added. The ToString() result of the Hand object is displayed and that is what I want - so far so good. However, when the background worker finishes
void finder_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
oHands = new ObservableCollection<Hand>(oHands.OrderBy(o => o.PotSize));
lbResults.SetBinding(ListBox.ItemsSourceProperty, new Binding());
}
The items in the list are still showing up in the original order. I can confirm that the list is re-ordered but the items are still showing up in the original order. How do I refresh this binding?
you dont want to assign oHands a new collection. just Clear() the collection then add the results from the operation. don't update the binding
instead of replacing the entire observable collection, you could just clear it and add all your new items. that wouldn't affect your binding.
You could also use a CollectionViewSource as your binding, and set the order on that instead of reordering the whole collection.
Wouldn't it be a lot easier to just set the itemsource directly?
lbResults.ItemsSource = oHands;
You're really just supposed to inherit from the INotifyPropertyChanged interface, but heres another way to force an update to a binding:
BindingExpression exp = BindingOperations.GetBindingExpression(lbResults, Listbox.ItemsSourceProperty)
exp.UpdateTarget()
Edit: I also just noticed you aren't setting any binding in the XAML and appear to be doing it programmatically with an empty Binding. I haven't tried that way before, so see if changing your XAML to this might help:
<ListBox Name="lbResults" SelectionChanged="lbResults_SelectionChanged" ItemsSource="{Binding Path=oHands}"/>
Then you set lbResults.DataContext to point to the class that has the member oHands. This is what worked for me in my project (in IronPython, so forgive me if my examples didn't convert to C# perfectly).

WPF Datagrid using MVVM.. is two way binding to DataTable possible?

I have a datagrid in my View with it's ItemSource bound to a DataTable in my ViewModel. When I update the DataTable programmatically (adding a column through a command) the changes are not populated to the View. Also, if I invalidate the View, by switching to another tab and then switching back, the changes made are shown.
My ViewModel inherits from INotifyPropertyChanged, and I am raising the PropertyChanged event correctly since I use the same process for other bound properties in the ViewModel and they work as expected.
Is it possible to get the datagrid to reflect changes I've made to the bound DataTable using the MVVM pattern?
Is there a datagrid event I can use to refresh the datagrid in the view's code behind?
Thanks for your help!
-Steven
While modifying rows and editing cell contents in the DataTable get reflected in the DataGrid (works for me) you're right that ColumnChanges don't seem to be. If you're using the AutoGenerateColumns option then I imagine it does so at initialization but doesn't watch for changes afterwards.
If you can find an event which fires (I haven't noticed one) when a column is added to the DataTable you could then add it manually in the code behind. Another hack which may or may not be practical would be to set your DataTable propety to null, and then re-set your property to the DataTable, with OnPropertyChanged being called each time. That should force the rebuilding of the DataGrid.
private DataTable _myDataTable;
public DataTable MyDataTable
{
get { return _myDataTable; }
set
{
_myDataTable = value;
OnPropertyChanged("MyDataTable");
}
}
void SomeMethod()
{
....results in column changes
DataTable holder;
holder = MyDataTable
MyDataTable = null;
MyDataTable = holder;
}
You must use ObservableCollection<> type to binding with DataGrid. Until do this, your DataGrid can update the change of DataTable in ViewModel.
Raising the PropertyChanged event seem useless when you bind with the type rather than ObservableCollection<>.

Resources