【发布时间】:2013-04-08 11:53:23
【问题描述】:
我正在开发一个简单的自定义控件,它应该充当菜单系统。它由 2 个按钮组成:返回和主页以及上面的菜单项。 ControlTemplate 看起来像这样:
<ControlTemplate x:Key="FancyColorPickerTemplate">
<menu:BusyDecorator x:Name="BusyDecorator" Style="{StaticResource BusyDecoratorStyle}">
<menu:BusyDecorator.IsBusyIndicatorShowing>
<PriorityBinding>
<Binding Path="IsBusy" RelativeSource="{RelativeSource AncestorType={x:Type CustomControls:Menu}}"/>
</PriorityBinding>
</menu:BusyDecorator.IsBusyIndicatorShowing>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.70*"/>
<RowDefinition Height="0.30*"/>
</Grid.RowDefinitions>
<ItemsControl Grid.Row="0" Name="Part_ItemControl"
ItemTemplateSelector="{StaticResource imgStringTemplateSelector}"
HorizontalAlignment="Center" VerticalAlignment="Center">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
<Button Grid.Row="1" Name="PART_BackButton" FontSize="20" Content="Back" HorizontalAlignment="Left" />
<Button Grid.Row="1" Name="PART_HomeButton" FontSize="20" Content="Home" HorizontalAlignment="Center" />
</Grid>
</menu:BusyDecorator>
</ControlTemplate>
在 ItemsControl 上有一个 ItemTemplateSelector 用于选择显示元素的 DataTemplate(可以是按钮或用户控件)。 示例:
<DataTemplate x:Key="ButtonTemplate">
<Grid Margin="10,0,10,0">
<Button Content="{Binding Title}" ></Button>
</Grid>
</DataTemplate>
<DataTemplate x:Key="UserControlTemplate">
<Grid Margin="10,0,10,0">
<CustomControls:ColorPickerUserControl Width="200" Height="200"/>
</Grid>
</DataTemplate>
在 Codebehind 中,如果需要,我会检查单击了哪个元素并加载子菜单(通过设置 ItemsControl 的 ItemsSource 属性):
void Menu_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
ItemsControl itemsControl = GetTemplateChild("Part_ItemControl") as ItemsControl;
object item = GetElementFromPoint(itemsControl, e.GetPosition(itemsControl));
if (item != null && item is MenuItem2)
{
MenuItem2 mi = item as MenuItem2;
if (mi.SubMenu != null)
{
IsBusy = true;
Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>
{
MenuItems = new ObservableCollection<MenuItem2>(mi.SubMenu.Items);
IsBusy = false;
}));
m_MenuStack.Push(mi.SubMenu);
}
else
{
if (!IsBusy)
{
ExecuteAction(mi.Ac);
}
}
}
else
Console.WriteLine("no item found");
}
上面代码中的 MenuItems 绑定到 ItemsSource 属性,该属性将重新评估 ItemsControl 并根据 DataTemplateSelector 应用相应的 DataTemplate。 我的代码中的问题是上面的 IsBusy 属性应该显示 BusyDecorator(参见 xaml),而 DataTemplate 是一个需要很长时间才能显示的 UserControl。它不起作用,因为我猜 UserControl 正在 UI 线程上加载,并且 IsBusy 属性触发了 UI 线程上的操作。
我是否采用了错误的方法?有什么办法可以做到这一点?
【问题讨论】:
-
我建议使用 .net 4.5,然后使用 Async/Await 功能实现它
-
那将是一个不错的解决方案。不幸的是,我们的嵌入式系统上只有框架 3.5。
-
你可以走很长的路(实际上是 Async/Await 所做的)并将它放在一个单独的线程中并实现一个处理程序,当加载线程完成时它会收到通知
-
我已经尝试过使用单独的线程,但它不起作用。我猜这个问题是 UserControl 是一个 UI 线程,我不知道如何避免这种情况。
标签: c# wpf xaml user-interface binding