工作 · 2022年8月4日 0

MVVM Light 使用心得之 ListBox.SelectedItem

假设左边的用户列表代码如下:

 <ListBox Grid.Row="1" Grid.Column="0" Grid.RowSpan="10" Margin="5,2"
                     ItemsSource="{Binding Path=Staffs}"
                     SelectedItem="{Binding Path=SelectedStaff, Mode=TwoWay}"
                     IsEnabled="{Binding Path=IsEnabled}">
......

看起来很不错,但是实际上,如果选择了其中一个用户,修改用户名,然后直接选择另一个用户,此时被修改的用户名其实并没有修改成功。

因为其中牵涉到了代码执行顺序的问题。那么怎么办呢?很简单,不要直接绑定 SelectedItem,而使用 Trigger 绑定 SelectionChanged 事件。

<UserControl 

    ......

    xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
    xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.SL5"

    ......
>

......

            <ListBox Grid.Row="1" Grid.Column="0" Grid.RowSpan="10" Margin="5,2"
                     ItemsSource="{Binding Path=Staffs}"
                     SelectedItem="{Binding Path=SelectedStaff}"
                     IsEnabled="{Binding Path=IsEnabled}">
                <i:Interaction.Triggers>
                    <i:EventTrigger EventName="SelectionChanged">
                        <cmd:EventToCommand Command="{Binding SelectedStaffChangeCommand}" PassEventArgsToCommand="True" />
                    </i:EventTrigger>
                </i:Interaction.Triggers>

然后 VM 那边这样写

 #region SelectedStaffChangeCommand

        private RelayCommand<SelectionChangedEventArgs> _selectedStaffCmd = null;

        /// <summary>
        /// 获取 SelectedStaffChange 命令。
        /// </summary>
        public ICommand SelectedStaffChangeCommand
        {
            get
            {
                if (_selectedStaffCmd == null)
                {
                    _selectedStaffCmd = new RelayCommand<SelectionChangedEventArgs>(OnInvokeSelectedStaffChangeCommand);
                }

                return _selectedStaffCmd;
            }
        }

        private void OnInvokeSelectedStaffChangeCommand(SelectionChangedEventArgs e)
        {
            BackgroundWorker bgw = new BackgroundWorker();
            bgw.RunWorkerCompleted += (s, a) =>
            {
                this.SelectedStaff = e.AddedItems.Cast<Staff>().FirstOrDefault();
            };
            bgw.RunWorkerAsync();
        }

        #endregion

我们使用 BackgroundWorker 把修改 SelectedStaff 的时间拖后,这样就好了。

并且当我们在 VM 中对 SelectedStaff 进行改变时,前端会随即响应。

不建议直接在 SelectedStaff 属性中使用 BackgroundWorker 来将 SelectedStaff 值的改变拖后。仔细想想为什么~。~