Выбранный элемент в выпадающем списке не меняет своего значения после обновления веб-службы
У меня есть пользовательский элемент управления WPF, который содержит поле со списком:
<UserControl
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation" x:Class="Hartville.SalesScriptApplication.Views.SalesScriptEditorGroups"
mc:Ignorable="d"
xmlns:events="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:cmd="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras.WPF4"
xmlns:hartvm="clr-namespace:Hartville.SalesScript.ViewModels;assembly=Hartville.SalesScript.ViewModels"
d:DesignHeight="106" d:DesignWidth="909" Background="#FFF3EDED">
<UserControl.Resources>
<ResourceDictionary>
<hartvm:ViewLocator x:Key="HartvilleLocator" d:IsDataSource="True" />
</ResourceDictionary>
</UserControl.Resources>
<UserControl.DataContext>
<Binding Source="{StaticResource HartvilleLocator}" Path="ScriptEditorGroups" />
</UserControl.DataContext>
<Border>
<Grid Margin="5,10,0,20">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<StackPanel Orientation="Horizontal" Margin="0,10,0,20">
<TextBlock TextWrapping="Wrap" Text="Selected Script Group:"/>
<ComboBox Width="250" Margin="10,0,0,0" HorizontalAlignment="Left" ItemsSource="{Binding ScriptGroups}" SelectedItem="{Binding SelectedScriptGroup}"
DisplayMemberPath="Name" >
<events:Interaction.Triggers>
<events:EventTrigger EventName="SelectionChanged">
<cmd:EventToCommand Command="{Binding ScriptGroupSelectedCommand}" PassEventArgsToCommand="False" />
</events:EventTrigger>
</events:Interaction.Triggers>
</ComboBox>
</StackPanel>
</Border>
</UserControl>
ПРИМЕЧАНИЕ: мы используем легкие рамки MVVM. Локатор представлений, который вы видите, является просто классом, который создает модель представления, на которую вы будете ссылаться в части контекста данных.
Когда что-то выбрано из поля со списком и нажата кнопка редактирования, пользователь может обновить имя и сохранить его. Тем не менее, когда пользователь нажимает кнопку Сохранить, вы можете увидеть измененный выбор в комбинированном списке, но выбранный элемент по-прежнему содержит исходное имя. Так, например, я выбираю опцию "Hello World" из поля со списком. Я меняю имя на "FOOBAR" и нажимаю "Сохранить". Я обновляю выбранный элемент в коде (и вижу, что свойство меняется). Когда я проверяю поле со списком, я вижу "FOOBAR", но выбранное значение по-прежнему говорит "Hello World". Кроме того, "Hello World" больше не существует в выпадающем списке (очевидно, потому что я только что обновил его.
Вот код для модели представления:
using System.Windows.Input;
using System.Collections.ObjectModel;
using GalaSoft.MvvmLight.Command;
using Hartville.Common.Controls.ViewModels;
using Hartville.Common.Controls.ViewModels.Validation;
using Hartville.SalesScript.ViewModels.Messages;
using Hartville.Values.Sales;
using System.Linq;
namespace Hartville.SalesScript.ViewModels.Scripts
{
public class ScriptEditorGroups: CommonViewModelBase
{
private ObservableCollection<ScriptHeader> _scriptGroups;
private ObservableCollection<Script> _scripts;
private ScriptHeader _selectedScriptGroup;
private Script _selectedScript;
private bool _isScriptGroupActive;
private string _groupName;
private bool _shouldEnableScriptGroup;
private bool _shouldShowGroupEditPanel;
private bool _shouldUseDefaultScript;
private bool _shouldShowScriptSelection;
public ICommand EditSelectedCommand { get; set; }
public ICommand NewGroupCommand { get; set; }
public ICommand SaveCommand { get; set; }
public ICommand ScriptGroupSelectedCommand { get; set; }
public ObservableCollection<ScriptHeader> ScriptGroups
{
get { return _scriptGroups; }
set { SetPropertyValue(ref _scriptGroups, value); }
}
public ObservableCollection<Script> Scripts
{
get { return _scripts; }
set { SetPropertyValue(ref _scripts, value); }
}
public ScriptHeader SelectedScriptGroup
{
get { return _selectedScriptGroup; }
set { SetPropertyValue(ref _selectedScriptGroup, value ); }
}
public Script SelectedScript
{
get { return _selectedScript; }
set { SetPropertyValue(ref _selectedScript, value); }
}
public bool IsScriptGroupActive
{
get { return _isScriptGroupActive; }
set { SetStructPropertyValue(ref _isScriptGroupActive, value); }
}
public string GroupName
{
get { return _groupName; }
set { SetStructPropertyValue(ref _groupName, value); }
}
public bool ShouldEnableScriptGroup
{
get { return _shouldEnableScriptGroup; }
set { SetStructPropertyValue(ref _shouldEnableScriptGroup, value); }
}
public bool ShouldShowGroupEditPanel
{
get { return _shouldShowGroupEditPanel; }
set { SetStructPropertyValue(ref _shouldShowGroupEditPanel, value); }
}
public bool ShouldUseDefaultScript
{
get { return _shouldUseDefaultScript; }
set { SetStructPropertyValue(ref _shouldUseDefaultScript, value); }
}
public bool ShouldShowScriptSelection
{
get { return _shouldShowScriptSelection; }
set { SetStructPropertyValue(ref _shouldShowScriptSelection, value); }
}
public bool IsEdit { get; set; }
public bool IsNew { get; set; }
protected override void RegisterForMessages()
{
MessengerService.Register<BeginSalesScriptEditorMessage>(OnBeginSalesScriptEditor);
EditSelectedCommand = new RelayCommand(OnEdit);
NewGroupCommand = new RelayCommand(OnNewGroup);
SaveCommand = new RelayCommand(OnSave);
ScriptGroupSelectedCommand = new RelayCommand(OnScriptGroupSelected);
}
private void OnBeginSalesScriptEditor(BeginSalesScriptEditorMessage message)
{
ScriptGroups = new ObservableCollection<ScriptHeader>(SalesService.GetAllScriptHeader());
Scripts = new ObservableCollection<Script>(SalesScriptCache.Scripts);
ShouldEnableScriptGroup = false;
ShouldShowGroupEditPanel = false;
ShouldShowScriptSelection = false;
IsEdit = false;
IsNew = false;
}
private void OnEdit()
{
if(SelectedScriptGroup == null) return;
IsEdit = true;
ShouldShowGroupEditPanel = true;
ShouldShowScriptSelection = true;
GroupName = SelectedScriptGroup.Name;
SelectedScript = SalesScriptCache.Scripts.FirstOrDefault(s => s.ScriptId == SelectedScriptGroup.StartScriptId);
}
private void OnNewGroup()
{
IsNew = true;
GroupName = string.Empty;
ShouldShowGroupEditPanel = true;
ShouldShowScriptSelection = false;
}
private void OnSave()
{
ThreadManagement.ExecuteInSeparateThread(ProcessScriptUpdate);
}
private void OnScriptGroupSelected()
{
if(SelectedScriptGroup == null) return;
MessengerService.Send(ScriptHeaderSelectedMessage.Create(SelectedScriptGroup));
ShouldEnableScriptGroup = true;
}
protected override void SetDesignTimeInfo(){}
protected void ResetValues()
{
ShouldEnableScriptGroup = false;
ShouldShowGroupEditPanel = false;
IsScriptGroupActive = false;
IsEdit = false;
IsNew = false;
ShouldShowScriptSelection = false;
}
private int CreateNewScriptGroup()
{
var scriptHeader = new ScriptHeader
{
Name = GroupName,
IsActive = IsScriptGroupActive
};
MessengerService.Send(ScriptHeaderSelectedMessage.Create(scriptHeader));
return SalesService.ScriptHeaderInsertUpdate(scriptHeader);
}
private int EditExistingScriptGroup()
{
if(SelectedScriptGroup == null) return 0;
var scriptHeader = new ScriptHeader
{
Name = GroupName,
IsActive = IsScriptGroupActive,
ScriptHeaderId = SelectedScriptGroup.ScriptHeaderId,
StartScriptId = SelectedScript.ScriptId
};
MessengerService.Send(ScriptHeaderSelectedMessage.Create(scriptHeader));
return SalesService.ScriptHeaderInsertUpdate(scriptHeader);
}
private void ProcessScriptUpdate()
{
var returnId = 0;
if (IsNew)
returnId = CreateNewScriptGroup();
else if (IsEdit)
returnId = EditExistingScriptGroup();
ScriptGroups = new ObservableCollection<ScriptHeader>(SalesService.GetAllScriptHeader());
SelectedScriptGroup = ScriptGroups.FirstOrDefault(h => h.ScriptHeaderId == returnId);
ResetValues();
}
}
}
Как мне исправить эту проблему?
РЕДАКТИРОВАТЬ:
Это метод SetPropertyValue, который вызывает уведомление:
public virtual void SetPropertyValue<T>(ref T currentValue, T newValue, Action<T> extraFunction = null, Action voidAfterSetAction = null) where T : class
{
if (currentValue == newValue) return;
currentValue = newValue;
PropertyHasChanged();
if (extraFunction != null) extraFunction(newValue);
if (voidAfterSetAction != null) voidAfterSetAction();
}
РЕДАКТИРОВАТЬ:
Это весь базовый класс, который содержит код изменения свойства:
using System;
using System.ComponentModel;
using System.Diagnostics;
using GalaSoft.MvvmLight;
using Hartville.Common.Controls.Messaging;
using Hartville.Common.Controls.Modules;
using Hartville.Common.Controls.ViewModels.Validation;
using Hartville.Common.Controls.WebServices;
using Hartville.Common.Threading;
namespace Hartville.Common.Controls.ViewModels
{
public abstract class CommonViewModelBase : ViewModelBase, IDataErrorInfo
{
public string this[string columnName]
{
get
{
var validationReturn = ValidationManager.Validate(columnName);
OnValidationComplete();
return validationReturn;
}
}
public string Error
{
get { return null; }
}
protected CommonViewModelBase()
{
ValidationManager = ValidationManager.Start(this);
RegisterForMessages();
if (IsInDesignMode) SetDesignTimeInfo();
}
public virtual void Reset()
{
IsProcessing = false;
}
public virtual void OnValidationComplete()
{
}
public virtual void SetPropertyValue<T>(ref T currentValue, T newValue, Action<T> extraFunction = null, Action voidAfterSetAction = null) where T : class
{
if (currentValue == newValue) return;
currentValue = newValue;
PropertyHasChanged();
if (extraFunction != null) extraFunction(newValue);
if (voidAfterSetAction != null) voidAfterSetAction();
}
public virtual void SetPropertyValue<T>(ref T currentValue, T newValue, Action extraFunction) where T : class
{
if (currentValue == newValue) return;
currentValue = newValue;
PropertyHasChanged();
if (extraFunction != null) extraFunction();
}
public virtual void SetStructPropertyValue<T>(ref T currentValue, T newValue, Action<T> extraFunction = null, Action voidActionAfterSetAction = null)
{
currentValue = newValue;
PropertyHasChanged();
if (extraFunction != null) extraFunction(newValue);
if (voidActionAfterSetAction != null) voidActionAfterSetAction();
}
public virtual void SetStructPropertyValue<T>(ref T currentValue, T newValue, Action extraFunction)
{
currentValue = newValue;
PropertyHasChanged();
if (extraFunction != null) extraFunction();
}
public virtual void SetValue<T>(ref T currentValue, T newValue, Action<T> voidOldValueAction = null, Action voidAfterSetAction = null) where T : class
{
var oldVal = currentValue;
if (currentValue == newValue) return;
currentValue = newValue;
PropertyHasChanged();
if (voidOldValueAction != null) voidOldValueAction(oldVal);
if (voidAfterSetAction != null) voidAfterSetAction();
}
protected abstract void RegisterForMessages();
protected abstract void SetDesignTimeInfo();
protected void SendModalCloseMessage()
{
MessengerService.Send(ModalCommandMessage.Create(ModalOptions.Close));
}
protected void SendModalOpenMessage(ModalName windowName, Guid? customID = null)
{
MessengerService.Send(ModalCommandMessage.Create(ModalOptions.Open, windowName, customID));
}
private void PropertyHasChanged()
{
var currentFrame = 2;
var frame = new StackFrame(currentFrame);
var propertyName = string.Empty;
if (frame.GetMethod().Name.Length > 4) propertyName = GetPropertyName(frame);
while (!frame.GetMethod().Name.StartsWith("set_"))
{
currentFrame++;
frame = new StackFrame(currentFrame);
if (frame.GetMethod().Name.Length > 4) propertyName = GetPropertyName(frame);
}
RaisePropertyChanged(propertyName);
}
private static string GetPropertyName(StackFrame frame)
{
return frame.GetMethod().Name.Substring(4);
}
}
}
2 ответа
Вам нужно реализовать iNotifyPropertyChanged и вызвать PropertyChanged в
public ScriptHeader SelectedScriptGroup
{
set { SetPropertyValue(ref _selectedScriptGroup, value ); }
}
В дополнение к тому, что Blam уже упомянул, вы должны реализовать интерфейс INotifyPropertyChanged в вашей CommonViewModelBase или текущей модели представления. И вы должны вызвать метод PropertyChanged для всех установщиков свойств, значения которых вы изменяете после того, как Datacontext был назначен представлению.
public ScriptHeader ScriptGroups
{
set { SetPropertyValue(ref _selectedScriptGroup, value );
PropertyChanged("SelectedScriptGroup ");
}
}
public ScriptHeader SelectedScriptGroup
{
set { SetPropertyValue(ref _selectedScriptGroup, value );
PropertyChanged("SelectedScriptGroup");
}
}
Иначе, ваш View не сможет узнать, что значение свойства, к которому привязан элемент управления, изменилось. Для реализации, пожалуйста, обратитесь к Свойству Измененная реализация