2013-04-16 137 views
2

我正在開發一個簡單的自定義控件,它應該充當菜單系統。它由2個按鈕組成:Back和Home以及上面的菜單項。 該控件模板看起來是這樣的:在UI線程上執行長任務

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

在代碼隱藏,我檢查被點擊該元件並加載子菜單(通過設置的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線程上觸發一個動作。

我遵循錯誤的方法嗎?有什麼辦法可以做到這一點?

+0

代碼我會建議使用.NET 4.5,然後用異步/等待功能 –

+0

這將是一個很好的解決方案實現它。不幸的是,我們的嵌入式系統只有框架3.5。 – LPL

+0

你可以走很長的路(實際上Async/Await是做什麼的),並把它放在一個單獨的線程中,並實現一個處理程序,當線程加載完成時收到通知 –

回答

0

您可以隨時設置繁忙標誌,並在繁忙設置完成呈現後在UI線程上安排剩餘工作。

這裏的魔術部分是使用優先System.Windows.Threading.DispatcherPriority.Render來調度線程,這使得任務與渲染同時運行。

<Window x:Class="WpfApplication1.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     DataContext="{Binding RelativeSource={RelativeSource Self}}" 
     Title="MainWindow" Height="350" Width="525"> 
    <Grid> 
     <Grid.RowDefinitions> 
      <RowDefinition/> 
      <RowDefinition/> 
     </Grid.RowDefinitions> 
     <Button Click="Button_Click_1">LongTask</Button> 
     <TextBlock Grid.Row="1" Text="{Binding IsBusy}"/> 
    </Grid> 
</Window> 

背後

using System; 
using System.Threading; 
using System.Windows; 

namespace WpfApplication1 
{ 
    public partial class MainWindow : Window 
    { 
     public bool IsBusy 
     { 
      get { return (bool)GetValue(IsBusyProperty); } 
      set { SetValue(IsBusyProperty, value); } 
     } 

     public static readonly DependencyProperty IsBusyProperty = DependencyProperty.Register("IsBusy", typeof(bool), typeof(MainWindow), new PropertyMetadata(false)); 

     public MainWindow() 
     { 
      InitializeComponent(); 
     } 

     private void Button_Click_1(object sender, RoutedEventArgs e) 
     { 
      IsBusy = true; 
      this.Dispatcher.BeginInvoke(new Action(() => 
      { 
       Thread.Sleep(2000); 

       IsBusy = false; 
      }), System.Windows.Threading.DispatcherPriority.Render, null); 
     } 
    } 
} 
相關問題