How to rollback selected SelectedValue of the comb

2020-07-11 04:39发布

I have something like it will pop to the user for getting confirmation of changes. If he clicks no I am setting the selectedValue in view model to the previous selection. But its not getting displayed correctly in view. Please help.

标签: wpf mvvm
8条回答
孤傲高冷的网名
2楼-- · 2020-07-11 04:45

What if you tried to raise the property changed event asynchronously? This is similar the examples from shaun and NebulaSleuth.

  public int SomeProperty
  {
     get { return m_someProperty; }
     set
     {
        if (value == m_someProperty)
           return;

        if (doYesNo() == No)
        {
           // Don't update m_someProperty but let the UI know it needs to retrieve the value again asynchronously.
           Application.Current.Dispatcher.BeginInvoke((Action) (() => NotifyOfPropertyChange("SomeProperty")));
        }
        else
        {
           m_someProperty = value;
           NotifyOfPropertyChange("SomeProperty");
        }
     }
  }
查看更多
Animai°情兽
3楼-- · 2020-07-11 04:46

In most WPF applications you bind a view model to the user interface with a TwoWay mode and then you're set to go.

However this goes against the typical user experience, where when you edit something and you don't save, you don't see that editing reflected throughout your entire application, even if you don't save your changes to the Database.

The mechanism available in WPF is the UpdateSourceTrigger property of the Binding. With this property you can control when the User Interface updates the ViewModel that it is bound to. This allows you to update only when the user saves what he's editing or something similar.

An example of a XAML Binding with the UpdateSourceTrigger set to Explicit:

"{Binding Path=Privado, UpdateSourceTrigger=Explicit, Mode=TwoWay}"

And when you want to really save to the ViewModel you call:

UpdateSource();
查看更多
ら.Afraid
4楼-- · 2020-07-11 04:50

If the user clicks no and you try to revert the value and then call OnPropertyChanged, WPF will swallow the event since it is already responding to that event. One way to get around this is to use the dispatcher and call the event outside of the current event context.

System.Windows.Threading.Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() => { OnPropertyChanged("ComSelectedValue"); }), null);
查看更多
欢心
5楼-- · 2020-07-11 04:54

WPF seems to validate that the bound property has changed before updating the UI. So simply invoking an NotifyPropertyChanged()/OnPropertyChanged() doesn't do the trick.

The problem is that since the property hasn't changed, WPF doesn't think it needs to refresh the combo box.

here is the incredibly hackish way I handle it in my ViewModels;

private int _someProperty = -1;
public int SomeProperty
{
    if (_someProperty != -1 && doYesNo() == Yes)
    {
       _someProperty = value;
    }
    else if (_someProperty != -1)
    {
       int oldValue = _someProperty;
       _someProperty = -1; // Set to a different/invalid value so WPF sees a change
       Dispatcher.BeginInvoke(new Action(() => { SomeProperty = oldValue; }));
    }
    else 
    {
       _someProperty = value;
    }
    NotifyPropertyChanged("SomeProperty");
}

Not pretty but it works reliably.

查看更多
Bombasti
6楼-- · 2020-07-11 04:55

I realize this is an old post but it seems no one has done this the right way. I used System.Interactivity.Triggers and Prism to process the SelectionChanged event and manually trigger the SelectedItem. This will prevent undesired Selected Item Changes in both the UI and the View-Model.

My view:

<Window x:Class="Lind.WPFTextBlockTest.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:vm="clr-namespace:Lind.WPFTextBlockTest"
    xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
    xmlns:prism="clr-namespace:Microsoft.Practices.Prism.Interactivity;assembly=Microsoft.Practices.Prism.Interactivity"
    Title="MainWindow" Height="649" Width="397">
<Window.DataContext>
    <vm:MainWindowViewModel/>
</Window.DataContext>
<StackPanel>
    <ComboBox ItemsSource="{Binding Data}" SelectedItem="{Binding SelectedData, Mode=OneWay}">
        <i:Interaction.Triggers>
            <i:EventTrigger EventName="SelectionChanged">
                <prism:InvokeCommandAction Command="{Binding TryChangeSelectedData}"/>
            </i:EventTrigger>
        </i:Interaction.Triggers>
    </ComboBox>
</StackPanel>

My View-Model (BindeableBase and DelegateCommand from Prism 5):

public class MainWindowViewModel : BindableBase
{
    public ObservableCollection<string> Data { get; private set; }
    private string selectedData;
    public string SelectedData
    {
        get { return selectedData; }
        set
        {
            SetProperty(ref selectedData, value);
        }
    }
    public DelegateCommand<SelectionChangedEventArgs> TryChangeSelectedData { get; private set; }
    public MainWindowViewModel()
    {
        Data = new ObservableCollection<string>() { "Foo", "Bar", "Dick", "Head" };
        SelectedData = Data.First();
        TryChangeSelectedData = new DelegateCommand<SelectionChangedEventArgs>(args =>
        {
            var newValue = args.AddedItems.Cast<string>().FirstOrDefault();
            if (newValue == "Dick")
                this.OnPropertyChanged(() => this.SelectedData);
            else
                SelectedData = newValue;
        });
    }
}
查看更多
Ridiculous、
7楼-- · 2020-07-11 04:56

Assumptions:
- You show a dialog box (with a message and OKCancel buttons) when a user selects some value from ComboBox.
- If user presses OK, everything is OK. :)
- If user presses Cancel, you say vmPropSelectedValue=previousValue.

This won't work. Why?

Don't have exact answer, but I believe when you show the dialog the system has just changed the selected value and has just notified the Source (via binding infrastructure) about the changed value . If at this moment (when source has control) you now change the value of ViewModel property from your VM code, which you expect would trigger OnPropertyChanged of INotifyPropertyChanged, which you expect would ask the WPF to update the target with your requested value. However, the WPF has not yet completed the cycle - its still waiting for the Source to return the control back to it. So it simply rejects your request (otherwise it would go in infinite loop).

If this was confusing, try this:
Cycle starts:
1. User changes value on UI. WPF changes target.
2. binding infrastructure requests Source to update itself.
3. Source updates itself (VM property).
4. Source returns control back to binding infra.
Cycle End.

Experts: Can't find some documentation in this regard. Above is my belief how things work. Please rectify if incorrect.


Short Answer:
AFAIK, this can't be done via pure VM code alone. You will have to put some code-behind code.
Here's one way: http://www.amazedsaint.com/2008/06/wpf-combo-box-cancelling-selection.html

查看更多
登录 后发表回答