How to stop automatic refresh of a WPF ListBox Databinded on a EntityFramework object - wpf

I have a Xaml Page with a Databinded ListBox and a detail grid to create or update selected element.
My Page.DataContext is binded on a ADO.NET Entity Data Model table ("Univers").
private void Page_Loaded(object sender, RoutedEventArgs e)
{
SEPDC = new Models.SEP();
universViewSource = new CollectionViewSource();
universViewSource.Source = SEPDC.Univers.Execute(System.Data.Objects.MergeOption.AppendOnly);
DataContext = universViewSource;
}
The Xaml code of the ListBox :
<ListBox DisplayMemberPath="Nom" ItemsSource="{Binding}" Name="universListBox" SelectedValuePath="IdUnivers"/>
When i select an element in the ListBox, the grid detail automatically display the information of the selected element
Here the "Nom" TextBox witch use TwoWay databinding :
<TextBox Name="nomTextBox" Text="{Binding Path=Nom, Mode=TwoWay}" />
When i modify the TextBox "Nom", the ListBox is automatically updated. Great ... But i haven't call the SaveChanges method of my SEPDC DataContext object ...
How can i stop the automatic refresh of my ListBox until i explicit call the SaveChanges method and if possible, without use the Binding UpdateSourceTrigger=Explicit option ?
Regards.

You can use two separate entity data context (SEPDC) objects. Your ListBox is bound to one and your detail grid to the other. When the SelectedValue changes in the ListBox, find the same entity in the detail grid's entity data context and set it. After saving changes from the detail grid's entity data context, refresh the one for the ListBox.

I use this technique but i have to recreate the ListBox SEPDC each time i refresh the ListBox.
List<Models.Univers> list;
using (Models.SEP dc = new Models.SEP())
list = dc.Univers.Execute(System.Data.Objects.MergeOption.AppendOnly).ToList();
universListBox.DataContext = list;
The Refresh method doesn't work.
Regards

Related

How to scroll to a given row when WPF DataGrid is loaded

I display a few thousand rows in a WPF DataGrid, but would like to automatically scroll to a specific row when it is loaded (and later also add a button to scroll to this specific row). Is it possible to achieve it via data binding or from a view model property?
Of course, I've already browsed the Internet, but could not find any corresponding property.. For exemple, SelectedCells only has a get accessor (but I don't think it would have scrolled whatsoever anyway). I don't think that the other properties, such as SelectedItem helps.
Thanks for any insights :-)
There are two options: scroll to a row without selecting it and scroll to a row and select it.
Scroll to row without selecting it
This solution works for any DataGrid.SelectionUnit value (cell or row based selection).
You set the row from a property in your view model, which binds to DataGrid.CurrentItem.
Scrolling must be handled by the DataGrid by handling the DataGrid.CurrentCellChanged event.
View model (must implement INotifyPropertyChanged)
object currentRow = this.DataSource[currentRowIndex];
this.CurrentRow = currentRow;
View.xaml
<DataGrid ItemsSource="{Binding DataSource}"
CurrentItem="{Binding CurrentRow}"
CurrentCellChanged="DataGrid_OnCurrentCellChanged" />
View.xaml.cs
private void DataGrid_OnCurrentCellChanged(object sender, EventArgs eventArgs)
{
var dataGrid = sender as DataGrid;
dataGrid.ScrollIntoView(dataGrid.CurrentItem);
}
Scroll to row by selecting it
This solution works only for DataGrid.SelectionUnit values DataGridSelectionUnit.FullRow or DataGridSelectionUnit.CellOrRowHeader (not cell-only based selection - cell-only solution must completely handle selection and scroll in view).
You set the selected row from a property in your view model, which binds to DataGrid.SelectedItem.
Scrolling must be handled by the DataGrid by handling the DataGrid.SelectionChanged event.
View model (must implement INotifyPropertyChanged)
object selectedRow = this.DataSource[currentRowIndex];
this.SelectedRow = selectedRow;
View.xaml
<DataGrid ItemsSource="{Binding DataSource}"
SelecetedItem="{Binding SelectedRow}"
SelectionChanged="DataGrid_OnCurrentCellChanged" />
View.xaml.cs
private void DataGrid_OnSelectionChanged(object sender, SelectionChangedEventArgs eventArgs)
{
var dataGrid = sender as DataGrid;
dataGrid.ScrollIntoView(dataGrid.SelectedItem);
}

TabControl - databinding TabItem order

I've got a datababound TabControl and would like to bind the index of each TabItem to a corresponding property in my view model. The ItemsSource is an ObservableCollection, and I'm using Bea Stollnitz's Drag/Drop functionality to provide tab control re-ordering.
My gut feeling is that it should be able to be handled in the data template for the tab item header, but I haven't been able to get it working.
Your TabControl.ItemsSource should be bound to your collection, so to re-arrange the order of tab items, simply re-arrange the collection.
I've worked with Bea's drag/drop code before to create a TabControl that allowed users to drag/drop the tab items, and I think most of what was needed is in the code she provides. On drop, it removes the dragged object from it's parent collection, and inserts it to its new location in the drop target collection, which in your case is the same collection.
Edit
Based on your comment below about updating your ViewModel with the Tab Index, try using the CollectionChanged event.
void MyCollection_CollectionChanged(object sender, CollectionChangedEventArgs e)
{
foreach (var item in MyCollection)
item.TabIndex = MyCollection.IndexOf(item);
}

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 Accessing Items inside Listbox which has a UserControl as ItemTemplate

I have Window that has a ListBox
ListBox(MyListBox) has a DataTable for its DataContext
ListBox's ItemSource is : {Binding}
Listbox has a UserControl(MyUserControl) as DataTemplate
UserControl has RadioButtons and TextBoxes (At first They're filled with values from DataTable and then user can change them)
Window has one Submit Button
What I want to do is, when user clicks the submit button
For each ListBox Item, get the values form UserControl's TextBoxes and RadioButtons.
I was using that method for this job :
foreach(var element in MyListBox.Items)
{
var border = MyListBox.ItemContainerGenerator.ContainerFromItem(element)as FrameworkElement;
MyUserControl currentControl = VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(VisualTreeHelper.GetChild(myBorder,0) as Border,0)as ContentPresenter,0)as MyUserControl;
//And use currentControl
}
I realised nothing when using 3-5 items in Listbox. But when I used much more items, I saw that "var border" gets "null" after some elements looped in foreach function.
I found the reason here :
ListView.ItemContainerGenerator.ContainerFromItem(item) return null after 20 items
So what can I do now? I want to access all items and get their values sitting on user controls.
Thanks
You should use objects who implement INotifyPropertyChanged and bind an ObservableCollection of it to the ItemSource
And then you can get all the list of items.
Here some quick links from MSDN to get more informations
How to: Implement Property Change Notification
Binding Sources Overview
You should google for some tutorials about this.
Zied's post is a solution for this problem. But I did the following for my project:
I realised that there's no need to use UserControl as DataTemplate in my project. So I removed ListBox's DataTemplate.
I removed MyListBox.DataContext = myDataTable and used this:
foreach(DataRow dr in myDataTable.Rows)
{
MyUserControl muc = new MyUserControl(dr);
myListBox.Items.Add(muc);
}
I took DataRow in my UserControl's constructor and did what I want.
And at last I could access my UserControls in ListBox by using :
foreach(MyUserControl muc in
myListBox)
{
//do what you want
}
Easy huh? :)

How to make sure my WPF TabControl always has a selected tab when it contains at least one tab?

I have a TabControl whose items are bound to an ObservableCollection:
<TabControl ItemsSource="{Binding MyObservableCollection}" />
The tabs are added and removed as expected as items are added and removed from the collection. However, the SelectedItem reverts to -1 (meaning there is no selected tab) whenever the collection is empty. Then, when an item is added, the SelectedItem stays at -1 and the new tab is not selected.
How do I make the TabControl select the new tab whenever an item is added to the empty collection?
There might be an easier way, but you could hook the collection changed event on the ObservableCollection in your VM and set the SelectedItem property to the new item (assuming you have the selected item bound to a property on the VM).
What you can do is to subscribe for TabControl.ItemContainerGenerator.StatusChanged event and if the status is ContainersGenerated and the SelectedIndex of TabControl is -1, then to make the SelectedIndex of TabControl 0;
// peopleCollection is an ObservableCollection<Person>
People peopleCollection = new People();
public Window1()
{
InitializeComponent();
// MyTabControl is an instance of TabControl
MyTabControl.ItemsSource = peopleCollection;
MyTabControl.ItemContainerGenerator.StatusChanged += new EventHandler(ItemContainerGenerator_StatusChanged);
}
void ItemContainerGenerator_StatusChanged(object sender, EventArgs e)
{
if((sender as ItemContainerGenerator).Status == System.Windows.Controls.Primitives.GeneratorStatus.ContainersGenerated && MyTabControl.SelectedIndex == -1)
{
MyTabControl.SelectedIndex = 0;
}
}
There are 3-rd party solutions that has this functionality out of the box. Telerik's RadTabControl selects the first item whenever the collection changes its state from empty to "containing single item".
Try the demo here: http://demos.telerik.com/silverlight/#TabControl/AddingAndRemovingTabs
Note: It is a SL demo, but it works the same in WPF.
If you are looking for a pure MVVM implementation then, add a Index property to the ViewModel and on the CollectionChanged you can set Index=0 if there is no items inside. And in the XAML you can bind that Index as below
<TabControl ItemsSource="{Binding MyObservableCollection}" SelectedIndex="{Binding Index}" />
you're best bet is to probably overwrite the "OnTabAdded" functionality to check if a new one (first one) is added and then setting the SelectedItemIndex to 0;
since you are using ObservableCollection, you know when your collection changes, so I'd subscribe to the changed event form the collection and check the number of items in it.
I had the same problem and managed to fix it by binding the selected item to the first item in the dynamic list.
<TabControl ItemsSource="{Binding MyObservableCollection}" SelectedItem="{Binding MyObservableCollection.First}" />
Worked for me :)
<TabControl ItemsSource="{Binding MyObservableCollection}" SelectedItem="{Binding MyObservableCollection[0]}" />

Resources