Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#MVVM在多个RaiseProperty上无响应的UI已更改_C#_Wpf_Mvvm - Fatal编程技术网

C#MVVM在多个RaiseProperty上无响应的UI已更改

C#MVVM在多个RaiseProperty上无响应的UI已更改,c#,wpf,mvvm,C#,Wpf,Mvvm,我是C#的一个新手,最近开始使用MVLight。下面是我当前项目和问题的基本概述。我的应用程序将读取文件系统/目录结构,并在树视图中重新创建它。然后,用户选中复选框以指定要处理的文件。如果用户检查目录,则该目录的所有子项都将被递归检查 目录通常包含数百个文件,因此在给定实例中会创建数千个文件对象。复选框双向绑定到要在我的视图模型上处理的属性。下面是该代码的一个片段(目录和文件对象都源自TreeViewModelBase) 此外,我在MainViewModel上有一个方法可以检查/取消选中所有内

我是C#的一个新手,最近开始使用MVLight。下面是我当前项目和问题的基本概述。我的应用程序将读取文件系统/目录结构,并在树视图中重新创建它。然后,用户选中复选框以指定要处理的文件。如果用户检查目录,则该目录的所有子项都将被递归检查

目录通常包含数百个文件,因此在给定实例中会创建数千个文件对象。复选框双向绑定到要在我的视图模型上处理的属性。下面是该代码的一个片段(目录和文件对象都源自TreeViewModelBase)

此外,我在MainViewModel上有一个方法可以检查/取消选中所有内容。此属性为CheckAll,是绑定到复选框的布尔值。选中“全选”复选框后,将启动一个命令,将所有子级的TobeProcessed属性设置为CheckAll的值

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同步上下文
这两种操作都会降低程序的响应速度,因为它们是在UI上执行的

如果您所做的只是更新UI,并且不需要对这些信息采取实际行动,那么就不需要创建任务。 这只会影响性能,因为创建任务也会分配内存

你可以做的是“重新设计”你如何看待问题。 您不需要在逻辑上更新所有子项。 您可以让WPF在视觉上切换它们,甚至不需要调用INPC,这也会产生反射成本

这听起来像是一个触发器的工作,大致如下:

<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"