views:

73

answers:

4

Hi, I'm just getting started with Linq-to-SQL and data binding in WPF, most of which works like a dream so far!

I've got (what I though was) a common scenario:

a) Query list of records from a table via datacontext and bind to the current user control

this.DataContext = db.ClientTypes;

b) Have the user see a bound ListView and some bound detail controls to make changes to the existing records, with a db.SubmitChanges(ConflictMode.FailOnFirstConflict); to push the changes back to the DB. No problem.

c) User wants to add a new record, so we:

ClientType ct = new ClientType();
ct.Description = "<new client type>";
db.ClientTypes.InsertOnSubmit(ct);

However at this point I dont want to call db.SubmitChanges as I want the user to be able to update the properties of the object (and even back out of the operation entirely), but I want them to be able to see the new record in the bound ListView control. Thinking I just needed to re-run the query:

ClientType ct = new ClientType();
ct.Description = "<new client type>";
db.ClientTypes.InsertOnSubmit(ct);

// Rebind the WPF list?
this.DataContext = db.ClientTypes;
listView1.SelectedItem = ct;
listView1.ScrollIntoView(ct);

However this doesn't work, the newly created record is not part of the returned list. I'm not sure if this is because of caching within L2S or if I'm just going about this the wrong way. Is there a better way to accomplish this?

Thanks.

A: 

I think it's just because you're assigning the same reference to the DataContext. Hence, WPF doesn't see the need to refresh the binding. The easiest way around this is to:

// rebind
this.DataContext = null;
this.DataContext = db.ClientTypes;

HTH,
Kent

Kent Boogaart
Sorry Kent, that doesn't work.
Kieran Benton
+1  A: 

It may be worth looking into the MVVM pattern. In MVVM you have a ViewModel which wraps your Model, so you would have a ClientTypeViewModel class.

public class ClientTypeViewModel : INotifyProperyChanged
{
    public ClientTypeViewModel(ClientType dataModel)
    {
        this.dataModel = dataModel;
    }

    public string Description
    {
        get { return this.dataModel.Description; }
        set 
        { 
            this.dataModel.Description = value; 
            // Raise PropertyChanged event
        }
    }

    private ClientType dataModel;
}

And something like an ApplicationView model, which would contain an ObservableCollection of ClientTypeViewModels.

public ApplicationViewModel
{
    public ObservableCollection<ClientTypeViewModel> ClientTypes { get; private set; }
}

You then bind to ApplicationViewModel.ClientTypes instead of the plain data model. This way, your view will be automatically updated whenever a new item is added to ClientTypes, or a property is changed on the ClientType view model. ApplicationViewModel can listen for changes on the ClientTypes collection and automatically add newly added items to the DataContext.

You may think it's overkill for your application, I don't know - but MVVM is definitely somthing worth learning. If it feels like you're struglling or fighting with WPF, MVVM is likely where to look ;)

Groky
Thanks for the link to MVVM - have heard of it before in reference to WPF but havent really paid much attention till now! Merry christmas.
Kieran Benton
A: 

Look at CreateBindingList.

leppie
+2  A: 

Instead of setting your Control.DataContext = db.ClientTypes, store db.ClientTypes somewhere else and bind to an ObservableCollection that wraps it.

var somewhereElse = db.ClientTypes;
var toBind = new ObservableCollection<ClientType>(somewhereElse);
toBind.CollectionChanged += (object sender, NotifyCollectionChangedEventArgs e) =>
{
    if (e.Action == NotifyCollectionChangedAction.Add)
        types.InsertAllOnSubmit<AddressType>(e.NewItems.Cast<AddressType>());
};
this.DataContext = toBind;

Then, when the user wants to add a new item:

ObservableCollection<ClientType> toBind = this.DataContext as ObservableCollection<ClientType>;
System.Diagnostics.Debug.Assert(toBind != null);

ClientType ct = new ClientType();
ct.Description = "<new client type>";
toBind.Add((ct);

Calling toBind.Add will cause the CollectionChanged event handler above to call InsertOnSubmit on the original Table instance, so you can call SubmitChanges() when convenient. Obviously, you'd probably want to do the same with Remove ...

Hope that helps :)

You don't really need the "somewhereElse" and "toBind" variables; they're just used as aliases ...
Thanks! This is pretty much exactly what I was looking for, apart from the fact I can't pass somewhereElse directly into the constructor for ObservableCollection<> on .NET 3.5 anyway - covariant changes in 4.0 might make this work?
Kieran Benton