![]() |
温暖的海豚 · 罗技K380无线蓝牙键盘笔记本外接平板手机电 ...· 6 月前 · |
![]() |
文质彬彬的自行车 · 2024年第五届地质与地球科学国际会议(IC ...· 11 月前 · |
![]() |
奔跑的骆驼 · java integer long ...· 1 年前 · |
![]() |
谦虚好学的太阳 · 在Excel中查找和激活命名范围而不知道工作 ...· 1 年前 · |
![]() |
从容的饭卡 · 会话列表 - 融云开发文档· 1 年前 · |
我有一个
ListBox
,它的
ItemsSource
绑定到(正确)实现
INotifyCollectionChanged
的自定义类,而
SelectedItem
绑定到ViewModel中的字段。
问题是,当我从
SelectedItem
集合中删除当前的
ItemsSource
时,它会立即将所选内容更改为相邻的项。我非常希望它只是删除了选择。
对我来说这是个问题的原因是这样的。
ItemsSource
类包含来自其他集合的元素,这些元素要么满足某些(在运行时常量期间)谓词,要么满足
Active
。
Active
与is
SelectedItem
是“同步”的(原因是这样的)。因此,只有当项目被选中时,它才有可能在
ListBox
中被允许,这意味着当用户选择其他项目时,它可能会消失。
在更改
SelectedItem
时调用我的函数(在“模型”中很深):
//Gets old Active item
var oldActiveSchema = Schemas.FirstOrDefault(sch => sch.IsActive);
//Makes the new Item active (which triggers adding it into `ItemsSource` in case it didn't satisfy the Predicate)
((PowerSchema)newActiveSchema).IsActive = true;
//Triggers PropertyChanged on ViewModel with the new Active item
CurrentSchema = newActiveSchema;
RaisePropertyChangedEvent(nameof(CurrentSchema)); (#1)
//Changes the old item so it stops being Active -> gets removed from `ItemsSource` (#2)
if (oldActiveSchema != null) { ((PowerSchema)oldActiveSchema).IsActive = false; }
问题是,由于应该由(#1)触发的
ListBox
更改而导致的
SelectedItem
更新由于某种原因而被推迟(用于更新
ListBox
的消息可能在WPF消息循环中结束,并等待当前计算完成)。
另一方面,从
oldActiveSchema
中删除
ItemsSource
是即时的,并且会立即触发
SelectedItem
更改为旧项旁边的
SelectedItem
(当您删除所选的项时,会选择相邻的项)。而且,由于
SelectedItem
的更改触发了我的函数,将
CurrentSchema
设置为错误(相邻)项,因此它重写用户选择的
CurrentSchema
(#1),并且在运行要更新
ListBox
的消息时,只需使用相邻的消息更新。
任何帮助都是非常感谢的。
如果有人想深入挖掘实际代码:
SelectedItem
而不是一个用户选择时
SelectedItem
输入的方法应该是活动的
SelectedItem
不再是活动的->被从集合中删除(44-41)
MoveCurrencyOffDeletedElement
移动
SelectedItem
SelectedItem
更改为相邻的
发布于 2017-03-19 11:49:11
诊断
解决问题的关键是在
IsSynchronizedWithCurrentItem="True"
上设置
ListBox
。它所做的是使
ListBox.SelectedItem
和
ListBox.Items.CurrentItem
保持同步。此外,
ListBox.Items.CurrentItem
与源集合的默认集合视图的
ICollectionView.CurrentItem
属性同步(在您的示例中,此视图由
CollectionViewSource.GetDefaultView(Schemas)
返回)。现在,当您从
Schemas
集合中删除一个项时,它也恰好是相应集合视图的
CurrentItem
,视图默认将其
CurrentItem
更新为下一项(如果删除的项是最后一项,则更新前一项;如果删除的项是集合中的唯一项,则更新到
null
)。
问题的第二部分是,当更改
ListBox.SelectedItem
导致对视图模型属性进行更新时,
RaisePropertyChangedEvent(nameof(ActiveSchema))
在完成后被处理为
,特别是在从
ActiveSchema
设置器返回控件之后。您可以观察到,getter不是立即命中,而是在设置器完成之后才被击中。重要的是,也没有立即更新
Schemas
视图的
Schemas
以反映新选择的项。另一方面,当您在以前选择的项上设置
IsActive = false
时,它会立即从
Schemas
集合中“删除”该项,这反过来会导致更新集合视图的
CurrentItem
,并且链将立即继续更新
ListBox.SelectedItem
。您可以观察到,此时
ActiveSchema
设置器将再次被击中。因此,您的
ActiveSchema
将再次被更改(到前面选择的项旁边的项),甚至在您完成以前的更改之前(对用户选择的项)。
解决方案
有几种方法可以解决这一问题:
#1
在您的
IsSynchronizedWithCurrentItem="False"
上设置
ListBox
(或者保持不动)。这将使你的问题不顾一切地消失。但是,如果由于某种原因需要它,则使用任何其他解决方案。
#2
通过使用保护标志防止重入尝试设置
ActiveSchema
:
bool ignoreActiveSchemaChanges = false;
public IPowerSchema ActiveSchema
get { return pwrManager.CurrentSchema; }
if (ignoreActiveSchemaChanges) return;
if (value != null && !value.IsActive)
ignoreActiveSchemaChanges = true;
pwrManager.SetPowerSchema(value);
ignoreActiveSchemaChanges = false;
}
这将导致视图模型忽略对集合视图的
CurrentItem
的自动更新,最终
ActiveSchema
将保持预期值。
#3
在“删除”之前,手动将集合视图的
CurrentItem
更新到新选定的项。您将需要对
MainWindowViewModel.Schemas
集合的引用,因此可以将其作为参数传递给您的
setNewCurrSchema
方法,或者将代码封装在委托中并将其作为参数传递。我将只给出第二种选择:
在
PowerManager
类中:
//we pass the action as an optional parameter so that we don't need to update
//other code that uses this method
private void setNewCurrSchema(IPowerSchema newActiveSchema, Action action = null)
var oldActiveSchema = Schemas.FirstOrDefault(sch => sch.IsActive);
((PowerSchema)newActiveSchema).IsActive = true;
CurrentSchema = newActiveSchema;
RaisePropertyChangedEvent(nameof(CurrentSchema));
action?.Invoke();
if (oldActiveSchema != null)
((PowerSchema)oldActiveSchema).IsActive = false;
}
在
MainWindowViewModel
类中:
public IPowerSchema ActiveSchema
get { return pwrManager.CurrentSchema; }
if (value != null && !value.IsActive)
var action = new Action(() =>
//this will cause a reentrant attempt to set the ActiveSchema,
//but it will be ignored because at this point value.IsActive == true
CollectionViewSource.GetDefaultView(Schemas).MoveCurrentTo(value);
pwrManager.SetPowerSchema(value, action);
}
注意,这需要对
PresentationFramework
程序集的引用。如果您不希望视图模型程序集中存在该依赖项,则可以创建一个由视图订阅的事件,所需的代码将由视图运行(该视图已经依赖于
PresentationFramework
程序集)。这种方法通常被称为交互请求模式(请参阅Prism 5.0指南中关于MSDN的
用户交互模式
部分)。
#4
将以前选择的项的“删除”推迟到绑定更新完成。这可以通过使用
Dispatcher
对要执行的代码排队来实现。
private void setNewCurrSchema(IPowerSchema newActiveSchema)
var oldActiveSchema = Schemas.FirstOrDefault(sch => sch.IsActive);
((PowerSchema)newActiveSchema).IsActive = true;
CurrentSchema = newActiveSchema;
RaisePropertyChangedEvent(nameof(CurrentSchema));
if (oldActiveSchema != null)
//queue the code for execution
//in case this code is called due to binding update the current dispatcher will be
//the one associated with UI thread so everything should work as expected
![]() |
从容的饭卡 · 会话列表 - 融云开发文档 1 年前 |