WPF databinding to System.Data.Linq.Table<T>, how to update the interface? - wpf

I have this combo bound to a linq table.
Is there a lightweight way to update the UI (combo values that are displayed to the user) when I insert a new record in the linq table?
Basically from what I understand I should have used an ObservableCollection, but I don't want to copy the data back & forth from the linq table to that collection, I only want to have data in the linq table.
Is that possible?
EDIT
OK Here is what i have done (and still it doesn't work):
private ObservableCollection<Categories> m_Categories;
private ObservableCollection<Categories> Categories
{
get
{
return m_Categories;
}
}
in the XAML i have:
<ComboBox Name="cmbCategory"
ItemsSource="{Binding Categories}"
DisplayMemberPath="Name"
SelectedValuePath="Id"
/>
So, pretty simple.
//if i have a new category, i want to update the combo's content
if (frmEditCategory.ShowDialog() == true)
{
//get the new category and add it to the ObservableCollection
LibraryDataStore.Instance.Categories.ToList().ForEach(p =>
{
if (!m_Categories.Contains(p))
{
m_Categories.Add(p);
}
});
//update the target? is this correct?!
BindingExpression be = cmbCategory.GetBindingExpression(ComboBox.ItemsSourceProperty);
if (be != null)
be.UpdateTarget();
}
Checked with the debugger, m_Categories contains the new category, but it doesn't show up in the combo.
Also, do you know any good tutorial or blog post about combo binding?...
Thank you in advance

What you are asking for is not possible. You need a mediator of some sort, and you have already identified the correct one- ObservableCollection. You have to move the Linq data somewhere if you want your user interface to be notified when it changes. You should have one ObservableCollection, which you add your Linq data to, and then you should bind the combo-box to the ObservableCollection rather than directly to the table. That is the simplest, most light-weight solution you can create and still receive automatic change notifications.

Related

WPF DataGrid automatically updates in-memory data?

I'm using WPF and MVVM pattern to develop a desktop application. Maybe I'm not clear about how a DataGrid control would work, but if I modify an item (text, checkbox, etc.), the modification persists even if I don't make any permanent database update (using Entity Framework). For example, I may switch to view different data, and when I come back to view the grid with modified data (but without saving to db), the change is there. Somehow the in-memory data has been changed by the DataGrid control and is not refreshed or synced with database.
In other words, the data in the DataGrid remained modified until I stop and re-run it from visual studio.
UPDATED:
Another way to ask this question would be: What actually happens when I update, say, an item of a DataGrid? If it is bound to a ViewModel's property P in two-way mode then I suppose P will be updated. But even if I refresh its value (setting the P to null then calling the data access methods again), the modified data are still there.
Does anybody have any idea of what happened?
Thanks!
UPDATED 2:
Here is the xaml code which binds a DataGrid to a property named UserList in the ViewModel.
<DataGrid
x:Name="UserList"
ItemsSource="{Binding UserList, Mode=TwoWay}"
AutoGenerateColumns="False"
AllowDrop="True"
RowBackground="Orange"
AlternatingRowBackground="#FFC4B0B0">
<!-- define columns to view -->
<DataGrid.Columns>
...
</DataGrid.Columns>
</DataGrid>
Here is the code running in the ViewModel. The method InitialiseData() is called in the constructor of the VM and before I want to do something else with persistent data, so I supposed is always refreshed.
private void InitialiseData()
{
// Retrieves user list from the business layer's response
Response userList = _userBL.GetUserList();
if (userList is FailResponse)
{
MessageBox.Show(userList.Message);
return;
}
else
{
UserList = null;
UserList = (IEnumerable<User>)((SuccessResponse)userList).Data;
}
** UPDATED 3 **:
private IEnumerable<User> _userList;
public IEnumerable<User> UserList
{
get
{
return _userList;
}
set
{
_userList = value;
NotifyOfPropertyChange(() => UserList);
}
}
If you switch back, you are switching to in-memory collection which was updated by DataGrid before. Or do you load data from dtb again?
EDIT:
Ok, now as you have posted the code, I know where is the problem.
DataGrid is not refreshed as I thought. Make sure, you will raise NotifyProperyChanged on the property UserList. Then it will work. See ObservableCollection class as well.

How to get DataGrid.SelectedIndex from another ".cs" file?(MVVM)

Earlier when I wanted to edit a row in a DataGrid then I just set the cursor on the row of a DataGtid and wrote such code in a method of a codebehind form(xxx.xaml.cs):
int k = XXXDataGrid.SelectedIndex;
and then I could retrieve data from a row of a DataGrid.
Now I try to use MVVM pattern of WPF and I have read that all my CRUD operations should pe written in Models. OKAY. I tried to take "DataGrid.SelectedIndex" but it is not possible without creating a handler in codebehind file.
I would like to know how can I take "DataGrid.SelectedIndex" and data of a row of a DataGrid from other classes situated in "Models" of MVVM.
When are value types stored in stack?
I have read a lot of books of C# and always when I read about values and references types then to my mind comes a question: When are value types stored in stack? Cause programmer cannot initializes any value type out from class.
Your ViewModel will have Properties that are populated Model objects. Here you will assign them so the View will be able to display data.
public ParentModel
{
get { return parentModel; }
private set
{
if (parentModel != value)
{
parentModel = value;
RaisePropertyChanged("ParentModel");
}
}
}
public int SelectedItemIndex
{
get { return selectedItemIndex; }
set
{
if (selectedItemIndex != value)
{
selectedItemIndex = value;
RaisePropertyChanged("SelectedItemIndex");
}
}
}
The View will contain the object to display data, be it DataGrid, ComboBox etc. You can use ItemTemplates to customize how the data is displayed, but the key is to bind your data to the control. This can allow data to flow in either the direction mode of only ViewModel to View (OneWay), or View to ViewModel (OneWayToSource), or both (TwoWay). So as the user changes the selected index, the data will flow back to the ViewModel as it is set for TwoWay below. The ItemsSource here is set as OneWay so only the ViewModel can change that for the View.
<DataGrid ItemsSource="{Binding Path=ParentModel.Items, Mode=OneWay}"
SelectedIndex="{Binding Path=SelectedItemIndex, Mode=TwoWay}" />
If ViewModels need to interact with BusinessLogic, just pass the Data. If a seperate ViewModel needs the information, you'll need to use the concept available in your framework, eg. EventAggregator (Prism), to pass data around since they won't have knowledge of each other.
Core concept of MVVM is the binding of models and WPF controls' properties like this. I think you'll want to read up more on it to fully utilize the power and design. Some very helpful questions/answers can be found on StackOverflow, MSDN has several good tutorials, etc.

Looping through (and removing) items of a bound ComboBox

I am new to WPF - and painfully aware of it. I have unsuccessfully searched for answers to this particular problem and am now seeking advice from my more knowledgeable peers!
The scenario
The application on which I am working allows users to either enter new records into a database, or amend existing ones.
I have a form containing a bound ComboBox. It is populated from the database, which is accessed by a WPF service that exposes a DTO.
From the UI perspective, the form has two modes:
1. enter new record
2. amend existing record
The ComboBox in question appears in both cases, but the requirement is to have fewer options visible when the form is in 'amend' mode.
What I am trying to do is loop through the ComboBox items when the form is in 'amend' mode and remove/hide the options that should not appear.
The XAML
<ComboBox x:Name="RecordType" Grid.Column="1" Grid.Row="1" Width="150" HorizontalAlignment="Left" SelectedValue="{Binding Path=RecordTypeID,TargetNullValue=0}"/>
The code behind - and my (feeble!) attempts so far
foreach (ComboBoxItem item in this.RecordType.Items)
{
if (IsApplicable(item.Content.ToString()) == false)
{
item.Visibility = Visibility.Hidden;
}
}
(NOTE: IsApplicable() is a simple method that compares the string it receives to a list of the options that are allowed to appear when the form is in 'amend' mode.)
The problem
As I'm sure many of you will already know ... cannot cast object of type DTO to type System.Windows.Controls.ComboBoxItem
The question(s)
Can I get at the string values in this, or a similar way? If so, how, please?
Correct way to do this would be to apply Filter on Collection View
See Automatically Filtering a ComboBox in WPF
ICollectionView view = CollectionViewSource.GetDefaultView(comboBox.ItemsSource);
view.Filter = IsApplicable
view.Refresh(); // <-- call this whenever you change the view model
It would likely be easier for you if you bound your combobox to an ObservableCollection and then removed the items from the collection when needed.
Here is an example: http://www.tanguay.info/web/index.php?pg=codeExamples&id=304

KeyDown problem with Silverlight & MVVM

First of all, I am new to the Silverlight + MVVM world. So I will try my best to explain the scenario to you guys.
I have the following AutoCompleteBox in my Silverlight 4 application which works perfect.
<myControls:AutoCompleteBox Name="acbVendorID" MinimumPrefixLength="2"
ItemsSource="{Binding VendorCodes, Mode=TwoWay}"
SelectedItem="{Binding SelectedVendorCode, Mode=TwoWay}"
ValueMemberBinding="{Binding Value}"
cmds:AutoCompletePopulating.Command="{Binding VendorID_Populating}"
cmds:AutoCompletePopulating.CommandParameter="{Binding ElementName=acbVendorID}">
Now my requirement is as follows:
When I select a particular VendorId from the AutoCompleteBox, the rest of the Vendor data should load in a gridview. For this reason, I have implemented the SelectedVendorCode in my ViewModel in the following way:
private KeyValue selectedVendorCode;
public KeyValue SelectedVendorCode
{
get { return selectedVendorCode; }
set
{
if (selectedVendorCode != value)
{
selectedVendorCode = value;
this.NotifyChanged("SelectedVendorCode");
if (selectedVendorCode != null)
{
this.VendorDisplayCode = selectedVendorCode.Value;
OnVendorCodeSelected(); //Sets the vendor collection property bound to the GridView
}
}
}
}
This works fine as well. The problem comes when the user uses the down arrow to browse through the items in AutoCompleteBox, The data in the Grid View gets reloaded as the selected item of the AutoCompleteBox changes.
I want to block this particular functionality. I want the GridView data to be fetched only when the user selects a vendor in the AutoCompleteBox and then presses Enter
I tried to achieve this using the method mentioned in this link
The KeyPressCommand doesn't fire when I use Down Arrow Or The Enter Key. Most other keys seem to fire this command.
I am a little perplexed by this. Any inputs by you giuys would be really helpful.
Thanks in advance.
~Vinod
Jeff Wilcox has written a post to do just this. The key is to implement a new ISelectionAdapter and apply to your ListBox. I have done this in the past and it works great.

C# Silverlight Datagrid - ADO.Net Data Entities CRUD?

I have followed this tutorial which allowed me to create a Silverlight DataGrid that pulled back data from an SQL Server Database. My next step is to be able to perform CRUD on this set of data (hopefully via the DataGrid by simply editing the fields for update etc. and having this post back). I have been informed that the datagrid needs to be set to have a "two-way binding mode" - However I am unsure of how to do this since the Xaml for the DataGrid is literally <data:DataGrid></data:Datagrid> - (Since the DataGrid was dropped onto the Page.xaml file from the toolbox and generated automatically).
I specifically want to continue using this ADO.Net Entity Framework implementation. Can anybody offer me some advice or point me in the right direction with some samples? I would greatly appreciate it.
Kind regards.
EDIT:
If you wouldn't mind having a quick glance over the content of the tutorial I linked to so that you understand exactly what my setup is that would be a great help. I'm completely new to Silverlight and essentially just want to know how to perform CRUD on a database using ADO.Net Entity Framework and a Silverlight Datagrid.
After Much Googling, this tutorial proved to help me do exactly what I wanted to do and was a fantastic help!
Good luck to anyone who continues with this implementation.
:-D
Goober,
I am not sure I will be answering your question, but I will show you how to bind information to the DataGrid from code, and setting that information to two-way binding.
First off, here is a link that discusses the difference between one-way, one-time and two-way bindings.
There are three ways you can change the data source after an edit took place in the data grid:
First
You can manually set the columns in the XAML and indicate that the binding to each variable is 'two-way'.
<data:DataGrid Name="data" AutoGenerateColumns="False">
<data:DataGrid.Columns>
<data:DataGridTextColumn Header="Test"
Binding="{Binding test, Mode=Two-Way}"/>
</data:DataGrid.Columns>
</data:DataGrid>
Second
You can do the same thing in the code behind the page.
rpdata.ItemsSource = info;
rpdata.Columns.Clear();
DataGridTextColumn user = new DataGridTextColumn();
user.Header = "User";
user.Binding = new System.Windows.Data.Binding("User");
user.Binding.Mode = BindingMode.TwoWay;
rpdata.Columns.Add(user);
rpdata represents the datagrid.
Third
Instead of linking each column manually, you can add the 'CellEditEnded' event to the data grid as shown below.
and in the code for the event, you can add the following code:
xt.CommitEdit();
This updates the data source.
You will have to create a new contract to your service that takes the entities that have been updated by the data grid.
[OperationContract]
public void UpdateWork(List<Assumptions> updates)
Here my entity is called 'Assumptions'.
In that Operation Contract you add the following code.
//Create a new entity datacontext
Entities ds = new Entities();
//For each of the entities in the list
foreach (Assumptions update in updates)
{
try
{
//In the datacontext find the entity with the same primary key as the updated one
var test = from x in ds.Assumptions
where x.ID.Equals(update.ID)
select x;
//Update the entity
test.First() = update;
}
catch (Exception e)
{
//If the entity does not exist, add it to the context
ds.AddToAssumptions(update);
}
}
//Save the context
ds.SaveChanges();

Resources