2013-01-15 23 views
1

我有以下類:C#WPF類屬性標記

class MyTimer 
{ 
    class MyTimerInvalidType : SystemException 
    { 
    } 
    class MyTimerNegativeCycles : SystemException 
    { 
    } 

    private Timer timer = new Timer(1000); 
    private int cycles = 0; 

    public int Cycle 
    { 
     get 
     { 
      return this.cycles; 
     } 

     set 
     { 
      if(value >= 0) 
       this.cycles = value; 
      else 
       throw new MyTimerNegativeCycles(); 
     } 
    } 

    private void timer_Tick(object sender, ElapsedEventArgs e) 
    { 
     try 
     { 
      this.Cycle--; 
     } 
     catch 
     { 
      this.Cycle = 0; 
      timer.Stop(); 
     } 
    } 

    public MyTimer() 
    { 
     this.Cycle = 20; 

     timer.Elapsed += new ElapsedEventHandler(timer_Tick); 
     timer.Start(); 
    } 
} 

在我的主窗口類我有一個列表我一個MyTimer添加當按下按鈕:

private List<MyTimer> timers = new List<MyTimer>(); 

private void testbtn_Click(object sender, RoutedEventArgs e) 
{ 
    timers.Add(new MyTimer()); 
} 

我試着將一個標籤作爲參考傳遞給MyTimer類並更新它,但這不起作用(無法從另一個線程訪問UI元素)。

什麼是在標籤中顯示MyTimer.Cycle的好方法,以便每次更改值時都會更新?

我必須能夠將每個MyTimer「綁定」到代碼中的不同標籤(或者根本不將它綁定到標籤)。

+0

你是什麼意思與「傳遞一個標籤到'MyTimer'類」? –

+0

@ nico-schertler我向MyTimer提供了一個方法,它將一個標籤ref作爲參數,然後試圖通過它將一個ref傳遞給一個標籤。 – Kyto

回答

0

你應該用你的標籤的Dispatcher財產BeginInvokeInvoke方法來改變什麼在標籤上或調用任何它的方法:

private void timer_Tick(object sender, ElapsedEventArgs e) 
{ 
    try 
    { 
     this.Cycle--; 
     this.label.Dispatcher.BeginInvoke(new Action(
      () => { label.Text = this.Cycle.ToString(); })); 
    } 
    catch 
    { 
     this.Cycle = 0; 
     timer.Stop(); 
    } 
} 

Dispatcher classDispatcher property的註釋部分。

0

最簡單的解決方案是使用DispatchTimers。調度計時器使用Windows消息隊列而不是線程來調度計時器滴答事件。這將使它不會出現跨線程問題。請記住,您不再使用其他線程,並且可能會鎖定用戶界面,如果您執行的任何操作都很昂貴。同樣由於在消息隊列上調度的性質,時序不太準確。

0

在WPF中,您將擁有與View(XAML)關聯的ViewModel(C#)。
如果您不熟悉MVVM,請閱讀本文。

然後視圖模型會暴露屬性(我們稱之爲週期),該視圖將綁定:

<Label Content="{Binding Cycle}" /> 

那麼,如果在視圖模型的價值必須從另一個線程更新,像這樣做:

Application.Current.Dispatcher.Invoke(new Action(() => 
{ 
    //Update here 
})); 

這將在UI線程上執行更新邏輯。

0

如果您是WPF的新手,我強烈建議您閱讀一下DataBindingData Templating

要開始,最簡單的方法是在較老的UI模型(如Windows窗體)中顯示Windows數據,一直是要在代碼隱藏中設置一些UI屬性。 WPF發生了巨大的變化,現在的目標是讓用戶界面查看業務對象(如MyTimer)並相應地設置用戶界面。

首先,我們需要將您的業務對象公開給應用程序的xaml。

Me.DataContext = new MyTimer(); 

這設定了窗口/用戶控件是一個new MyTimer();由於DataContext屬性自動從父UI元素到子UI elelement基於(除非兒童定義它自己的DataContext),每當數據上下文您的Window/UserControl中的元素現在將具有此對象的DataContext

接下來我們可以創建一個綁定到這個對象的屬性。默認情況下,所有綁定都是相對於它所在控件的DataContext

<Label Content="{Binding Cycle}" /> 

所以在前面的例子中,綁定是在標籤的content屬性上。所以在這種情況下,它會自動將Content設置爲DataContext(MyTimer)中的「Cycle」屬性的值!

然而,有一個捕獲。如果你運行這個示例,因爲WPF將在表單加載時取值,但它不會再次更新標籤!這裏更新UI的關鍵是實現INotifyPropertyChanged接口。

只要屬性(如Cycles)發生更改,此接口就會告訴任何聽衆。最棒的是Bindings自動支持這個界面,並且當你的源代碼實現INotifyPropertyChanged時會自動傳播更改。

public class MyTimer : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    private int cycles; 

    public int Cycles 
    { 
     get 
     { 
      return cycles; 
     } 
     set 
     { 
      if (cycles < 0) 
      { 
       throw new ArgumentOutOfRangeException("value", "Cycles cannot be set to a number smaller than 0."); 
      } 
      else if(value <> cycles) 
      { 
       cycles = value; 

       if (PropertyChanged != null) 
       { 
        PropertyChanged(Me, new PropertyChangedEventArgs("Cycles")) 
       } 
      } 
     } 
    } 

    //insert your constructor(s) and timer code here. 
} 

瞧!您的計時器現在將用它的週期屬性更新UI。

但是您還注意到您正在將MyTimer對象存儲在列表中。如果你是不是把他們的ObservableCollection(默認的INotifyCollectionChanged實現 - INotifyPropertyChanged的收集變體)中,你可以做其他巧妙的技巧:

在窗口/用戶控件的構造函數:

ObservableCollection<MyTimer> timers = New ObservableCollection<MyTimer>(); 
timers.Add(New MyTimer()); 
DataContext = timers; 

然後你可以在你的XAML一次顯示他們都:

<ItemsControl ItemsSource="{Binding}"> 
    <ItemsControl.ItemTemplate> 
     <DataTemplate> 
      <Label> 
       <TextBlock Text="{Binding StringFormat='Cycles Remaining: {0}'}" /> 
      </Label> 
     </DataTemplate> 
    </ItemsControl.ItemTemplate> 
</ItemsControl>