C#MVVM在多个RaiseProperty上无响应的UI已更改
我是C#的一个新手,最近开始使用MVLight。下面是我当前项目和问题的基本概述。我的应用程序将读取文件系统/目录结构,并在树视图中重新创建它。然后,用户选中复选框以指定要处理的文件。如果用户检查目录,则该目录的所有子项都将被递归检查 目录通常包含数百个文件,因此在给定实例中会创建数千个文件对象。复选框双向绑定到要在我的视图模型上处理的属性。下面是该代码的一个片段(目录和文件对象都源自TreeViewModelBase) 此外,我在MainViewModel上有一个方法可以检查/取消选中所有内容。此属性为CheckAll,是绑定到复选框的布尔值。选中“全选”复选框后,将启动一个命令,将所有子级的TobeProcessed属性设置为CheckAll的值C#MVVM在多个RaiseProperty上无响应的UI已更改,c#,wpf,mvvm,C#,Wpf,Mvvm,我是C#的一个新手,最近开始使用MVLight。下面是我当前项目和问题的基本概述。我的应用程序将读取文件系统/目录结构,并在树视图中重新创建它。然后,用户选中复选框以指定要处理的文件。如果用户检查目录,则该目录的所有子项都将被递归检查 目录通常包含数百个文件,因此在给定实例中会创建数千个文件对象。复选框双向绑定到要在我的视图模型上处理的属性。下面是该代码的一个片段(目录和文件对象都源自TreeViewModelBase) 此外,我在MainViewModel上有一个方法可以检查/取消选中所有内
public class MainViewModel : ViewModelBase
{
...
private async void ToggleAll()
{
// Prevent tree from being modified while the values are being updated.
TreeIsEnabled = false; // Bound to IsEnabled on TreeView
// My attempt to update the UI from another thread.
await Task.Run(() =>
{
// Update each child's ViewModel
foreach (TreeViewModelBase child in GetChildren())
{
child.ToBeProcessed = CheckAll;
}
});
// Re-enable the tree when the await is finished.
TreeIsEnabled = true;
}
...
}
一切正常,但主要问题是,当UI呈现数千个RaisePropertyChanged事件时,我的UI完全没有响应。我有点确定我在这里等待的任务没有做任何事情,因为UI线程仍然需要呈现所有内容,这就是我的瓶颈所在。我已经对一个解决方案进行了广泛的搜索,但是我找到的解决方案要么对我不起作用,要么我找不到一种方法来在我的案例中实现它们的解决方案
谢谢你抽出时间 这里的Parallel.ForEach循环对您有用吗 与此相反:
foreach (TreeViewModelBase child in GetChildren())
{
child.ToBeUploaded = CheckAll;
}
成千上万的UI元素太多了。在设计UI系统时没有考虑到这一点,因为用户根本无法在一个表单上与数千个控件交互 我已经对一个解决方案进行了广泛的搜索,但是我找到的解决方案要么对我不起作用,要么我找不到一种方法来在我的案例中实现它们的解决方案 听起来你应该用一种新的方法。其思想是,您只需构建需要显示的部分,而不是立即构建整个树。当用户展开一个节点时,您只需将该文件夹中的文件/子文件夹添加到树中。试试这个方法
private void ToggleAll()
{
// Prevent tree from being modified while the values are being updated.
TreeIsEnabled = false; // Bound to IsEnabled on TreeView
// My attempt to update the UI from another thread.
Task task = new Task(() =>
{
// Update each child's ViewModel
foreach (TreeViewModelBase child in GetChildren())
{
child.ToBeProcessed = CheckAll;
}
});
task.Start();
// Re-enable the tree when the await is finished.
TreeIsEnabled = true;
}
请理解,使用等待关键字会导致:
- 内存分配
- 返回到UI同步上下文
<Checkbox x:Name="SelectAll"/>
。。。树元素复选框:
<CheckBox IsChecked="{Binding IsSelected, Mode=OneWayToSource}">
<CheckBox.Style>
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=SelectAll, Path=IsChecked}" Value="True">
<Setter Property="IsChecked" Value="True" />
</DataTrigger>
</Style.Triggers>
</CheckBox.Style>
</CheckBox>
触发器只需切换复选框的选中状态,然后切换子体名为IsSelected的视图模型布尔属性,而IsSelected则不执行任何操作,只是保持选择的状态
另外,当取消选中“全选”复选框时,每个文本框都会恢复到以前的状态。
如果不需要,只需在每个子项和“全选”复选框之间绑定,就可以覆盖该选项
当实际需要选择信息时,您所需要做的就是循环选择的项目并对其进行处理。感谢Stephen Cleary,我找到了解决方案 我只是在我的树视图中添加了:
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"
就我而言,我的树视图也有一个边框。边框似乎会导致WPF忽略TreeView的虚拟化,并导致视图外的项目被呈现,除非我也将上面的虚拟化属性添加到边框中。添加这些虚拟化属性导致UI仅呈现屏幕上项目的复选框更改。其余复选框在进入视图时按需呈现。结果非常好,最终只需对现有代码进行很少的修改。我从ToggleAll方法中删除了等待。我还可能会删除TreeIsEnabled,因为现在的性能非常快,以至于您甚至无法感知禁用/启用。我尝试了以下操作,但没有成功:Parallel.ForEach(GetChildren(),(child)=>{child.ToBeUploaded=CheckAll;});这是我的担心。到目前为止,我一直依赖WPF的自动延迟加载。我的目录结构目前实际上是固定的,目录>子目录>文件。加载树后,在运行代码时,默认情况下不会展开子目录。当子目录未展开时,它几乎立即运行。问题是,当我展开一个节点时(甚至当隐藏它并且WPF没有处理元素时),它就会挂断。也许我可以在继续之前自动折叠并释放UI元素?谢谢,Kevin。不幸的是,这是我以前的一次尝试。事实上,我已经尝试了大量不同的线程/任务解决方案,但都无济于事。@SolidSloth我真的鼓励您不要使用任务构造函数,这实际上会带来更多的开销。对于.NET 4.5及更高版本,除非确实需要配置任务,否则请使用Task.Run,在这种情况下,请使用Task.Factory.StartNew,但不要使用构造函数。此外,此代码不会按预期工作,因为在某些情况下,它会在实际完成工作之前启用树。谢谢,Eyal。我非常喜欢这个解决方案,如果不是因为它看起来比触发事件更直观的话。不幸的是
VirtualizingStackPanel.IsVirtualizing="True"
VirtualizingStackPanel.VirtualizationMode="Recycling"