很抱歉,标题是我能想到的最好的。让我解释一下我的问题:我有一个WPF应用程序,它的菜单看起来很像您的标准顶部菜单,它只占屏幕的5%。单击菜单项,下面的视图将更改。菜单控件是自制的,其原因如下:动态更改菜单项,不适合现有控件的怪异UI等。
使用绑定到“ CurrentMenuView”属性的ContentControl完成菜单更改视图部分。单击菜单项时,会发生这种情况(伪代码):
private async void Button_Pressed(...)
{
await MakeSomeVisualMenuChanges();
CurrentMenuView = new CarsView();
await MakeSomOtherStuff();
}
问题在于某些视图需要花费一些时间来加载,这使得其他步骤的执行速度也变慢。我想开始加载“ CarsView”,但同时(不要之后)继续进行其他更改。这样用户可以看到正在发生的事情。
我可以使用Task.Run()解决此问题,但这似乎是错误的,因为它将内容放置在不同的上下文中。
解决这个问题的正确/更好的方法是什么?
编辑:
感谢所有答案,因为我希望这不容易解释。让我尝试一个简单的示例:
MainWindows.xaml:
<Window x:Class="WpfApp32.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid ShowGridLines="True">
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="300" />
<RowDefinition Height="30" />
</Grid.RowDefinitions>
<Button Grid.Row="0" Width="50" Content="Click" Click="Button_Click" />
<ContentControl Grid.Row="1" Width="200" Height="200" Content="{Binding PageContent, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
<TextBlock Grid.Row="2" Text="Bottom" Width="300" x:Name="BottomText" />
</Grid>
</Window>
隐藏代码:
using System.Windows;
using System.ComponentModel;
namespace WpfApp32
{
public partial class MainWindow : Window, INotifyPropertyChanged
{
public MainWindow()
{
InitializeComponent();
DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
BottomText.Text = "AndNowforSomethingCompletelyDifferent";
PageContent = new UserControl1();
}
private object pageContent;
public object PageContent
{
get => pageContent;
set
{
pageContent = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(PageContent)));
}
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
UserControl1.xaml:
<UserControl x:Class="WpfApp32.UserControl1"
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"
mc:Ignorable="d"
Loaded="UserControl_Loaded"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<TextBlock x:Name="CtrlTextBlock" Width="100"/>
</Grid>
</UserControl>
UserControl1.cs:
using System.Windows;
using System.Windows.Controls;
namespace WpfApp32
{
public partial class UserControl1 : UserControl
{
public UserControl1()
{
InitializeComponent();
}
private void Init()
{
System.Threading.Thread.Sleep(2000);
CtrlTextBlock.Text = "Loaded";
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
Init();
}
}
}
因此,这已从异步等中删除,仅显示了问题。当按下按钮时,UserControl1已加载,但加载需要2秒钟。在此之前,“ BottomText”元素中的文本保持不变。
正如我之前所说,我可以通过在按钮单击中执行以下操作来解决此问题:
private void Button_Click(object sender, RoutedEventArgs e)
{
BottomText.Text = "AndNowforSomethingCompletelyDifferent";
System.Threading.Tasks.Task.Run(() => Application.Current.Dispatcher.Invoke(() => PageContent = new UserControl1()));
}
但不确定这是要走的路。因此,这里的基本问题是将ContentControl绑定到属性,并且设置该属性可能需要一些时间。在加载的同时,我不想停止MainWindow中的执行(我希望BottomText元素立即显示“ AndNowforSomethingCompletelyDifferent”)。
答案 0 :(得分:1)
这是一个模拟3秒钟加载的示例...这不是一个非常复杂的UI,绑定到复杂的DataTemplates
(尤其是嵌套的)可能会使速度变慢,但是通常,拖拽来自拉动数据。
诀窍是拥有一个聪明的UI,该UI可以使事情继续前进,同时还可以让用户知道它正在等待其他事情继续下去。例如,臭名昭著的加载栏...不是我会使用确切的过程,但这是正确的主意。
注意和免责声明:除非在某种类型的自定义控件中,否则我几乎会鄙视代码。永远都看不到。我总是更喜欢MVVM并使用ViewModel进行绑定;但不能仅针对UI使用的数据来构建或控制UI。这么说是因为这个例子不是全部。我只是简单地在视图上制作了控件,并通过示例尽可能简单地将代码放在后面以回答这个问题。
<Window x:Class="Question_Answer_WPF_App.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow"
Height="500"
Width="800">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition />
</Grid.RowDefinitions>
<ToggleButton x:Name="menuButton"
Content="Menu"
Click="MenuButton_Click" />
<!--I do not recommend binding in the view like this... Make a custom control that does this properly.-->
<Grid x:Name="menu"
Visibility="{Binding IsChecked, Converter={StaticResource BooleanToVisibilityConverter}, ElementName=menuButton}"
VerticalAlignment="Top"
Grid.Row="1"
Background="Wheat">
<StackPanel x:Name="menuItems">
<TextBlock Text="simulated...." />
<TextBlock Text="simulated...." />
<TextBlock Text="simulated...." />
<TextBlock Text="simulated...." />
<TextBlock Text="simulated...." />
<TextBlock Text="simulated...." />
<TextBlock Text="simulated...." />
<TextBlock Text="simulated...." />
<TextBlock Text="simulated...." />
<TextBlock Text="simulated...." />
</StackPanel>
<StackPanel Name="menuLoading">
<TextBlock Text="Loading..."
FontSize="21" />
<ProgressBar IsIndeterminate="True"
Height="3" />
</StackPanel>
</Grid>
</Grid>
</Window>
using System.Threading.Tasks;
using System.Windows;
namespace Question_Answer_WPF_App
{
public partial class MainWindow : Window
{
public MainWindow() => InitializeComponent();
private Task SimulateLoadingResourcesAsnyc() => Task.Delay(3000);
private async void MenuButton_Click(object sender, RoutedEventArgs e)
{
menuItems.Visibility = Visibility.Collapsed;
menuLoading.Visibility = Visibility.Visible;
await SimulateLoadingResourcesAsnyc();
menuItems.Visibility = Visibility.Visible;
menuLoading.Visibility = Visibility.Collapsed;
}
}
}