WPF Binding - There must be a better way than how I'm doing it!
Overview
I am using CompositeWPF to create an app using C#. This really should make a difference to the answer as my problem would exist outside of Prism.
I have an ItemsControl which is bound to an ObservableCollection containing my ViewModels. This is working fine. In my DataTemp开发者_运维技巧late I have my required controls bound in XAML. One of these controls is a button called "Configure." When clicked the button issues a DelegateCommand which passes the ViewModel as a parameter.
The DelegateCommand opens a modal dialog showing the VM looks as follows.
private void ShowDialog(object obj)
{
ComPortPropertiesPresenter presenter = _container.Resolve<ComPortPropertiesPresenter>();
ComPortViewModel vm = obj as ComPortViewModel;
presenter.ComPort = vm.Clone() as ComPortViewModel;
DialogOptions options = new DialogOptions();
options.DialogTitle = presenter.View.DisplayName;
options.HideOkCancelButtons = true;
bool? result = DialogWorkspace.ShowView(presenter.View, options, () =>
{
return true;
});
if (result == true)
{
// TODO: There must be a better way of doing this. The problem is if you bind the object the
// automated bindings cause a realtime update. If you clone the object and then set it to become obj then
// the bindings do not update. Need to investigate how an observablecollection triggers an update.
int position = ComPorts.IndexOf(obj as ComPortViewModel);
ComPorts.Remove(obj as ComPortViewModel);
ComPorts.Insert(position, presenter.ComPort);
}
My original code passed the obj to the presenter without a clone. The problem with that was that the value was being changed in real time. If you made a change and then hit cancel the dialog would be closed although because of the bindings the changes would have already happened.
I decided to clone the vm and pass that to the view. If the dialog result was then true I tried copying the cloned vm over the original. EG: obj = presenter.ComPort;
This didn't then update the values on the original view which contained the button.
I then tried things like... obj.BaudRate = presenter.ComPort.BaudRate which worked although my fear is it is long winded and if the vm was ever extended things could get missed.
My final solution was to remove the original vm and add the new one in the location of the original. This works 100% although I feel it is somewhat clumsy. Am I being overly critical or is there a better way?
My guess is that the ObservableCollection fires an INotify event when something is added/removed. The same in my vm when a property is updated it works because I raise an event.
So could the problem be that if you overwrite something in an ObservableCollection no event is raised?
There must be some clever cloggs out there who knows.
The thing you need to understand here is one of pointers and not of WPF.
Your "obj" variable is simply the stored address of an object in memory. In your example, there are 2 references to this object. The first is your obj variable, the second is your ObservableCollection.
Let's say it like this. Let's name your original object "A" and your new object "B". Currently your references look like this:
obj => A
ObservableCollection => A
presenter.ComPort => B
When you say obj = presenter.ComPort, you are really saying "I no longer want to reference the object 'obj' was pointing to... now I want to reference the object 'presenter.ComPort' is pointing to". Now everything looks like this:
obj => B
ObservableCollection => A
presenter.ComPort => B
Notice you've done nothing to the ObservableCollection. This addresses your question "if you overwrite something... no event is raised"... you've not "overwritten" anything.
This is why you've had to do the Remove + Add to change the ObservableCollection. When you do this, NOW you've changed something in the ObservableCollection:
obj => B
ObservableCollection => B
presenter.ComPort => B
There is not a way around this really and what you have done is correct. This is the nature of references.
There are ways around this by using "reference references" (passing a reference with the "ref" keyword), but I really like the explicit nature of what you are doing here and I would advise you to keep it.
精彩评论