2013-05-30 26 views
1

我剛剛開始使用WPF,仍然沒有清楚所有關於它的事實。從數據綁定和多線程WPF中的用戶控件更新主界面

現在我正在搞數據綁定和線程更新數據並在時間基礎上表示它。我有一個帶有2個文本框的主窗口和一個usercontrol,它定義了一個線程,當我點擊它時開始運行。在這個線程中,我調用了在綁定到文本框的主窗口中設置的兩個屬性,一個由代碼創建,另一個由xaml創建。

問題是由代碼產生的數據綁定工作,而由xaml產生的數據綁定只在直接更新文本框時才起作用。我一直在想,爲什麼,有些改變後我開始工作,但我懷疑這個解決方案是「正確的」。

的代碼是這個

MainWindow.xaml

<Window x:Class="WpfApplication1.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="350" Width="525" Loaded="Window_Loaded" xmlns:my="clr-namespace:WpfApplication1.Elements"> 
    <Grid> 
     <TextBox Name="Textbox1" Height="23" HorizontalAlignment="Left" Margin="188,50,0,0" VerticalAlignment="Top" Width="120" Background="White" Text="{Binding Source=my:MainWindow, Path=datavalue}" /> 
     <TextBox Name="textBox2" Background="White" Height="23" HorizontalAlignment="Left" Margin="188,101,0,0" Text="{Binding Path=datavalue2, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}} }" VerticalAlignment="Top" Width="120" /> 

     <Label Content="Label" Height="28" HorizontalAlignment="Left" Margin="202,152,0,0" Name="label1" VerticalAlignment="Top" /> 
     <my:Test Margin="81,138,346,92" x:Name="test1" /> 
    </Grid> 
</Window> 

MainWindow.xaml.cs

namespace WpfApplication1 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : Window, INotifyPropertyChanged 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 
     private int MydataValue; 

     public int datavalue 
     { 
      get 
      { 
       return MydataValue; 
      } 
      set 
      { 
       MydataValue = value; 

       if (PropertyChanged != null) 
       { 
        PropertyChanged(this, 
         new PropertyChangedEventArgs(
         "datavalue")); 
       } 
      } 
     } 

     private int mydatavalue2; 

     public int datavalue2 
     { 
      get 
      { 
       return mydatavalue2; 
      } 
      set{ 
       mydatavalue2 = value; 
      } 
     } 



     private void Window_Loaded(object sender, RoutedEventArgs e) 
     { 
      datavalue2 = 10; 
      datavalue = 15; 
      Binding bind = new Binding(); 
      bind.Source = this; 
      bind.Mode = BindingMode.TwoWay; 
      bind.Path = new PropertyPath("datavalue"); 
      Textbox1.SetBinding(TextBox.TextProperty, bind); 
     } 
    } 

} 

這是用戶控件

test.xaml.cs

namespace WpfApplication1.Elements 
{ 
    /// <summary> 
    /// Interaction logic for Test.xaml 
    /// </summary> 
    public partial class Test : UserControl 
    { 
     private Thread t; 
     private int resolution; 
     private Stopwatch sw,sw2; 
     private delegate void changeIconDelegate(long ib); 
     private ImageBrush b; 
     private Boolean end=false; 
     private int count = 0; 

     public Test() 
     { 
      InitializeComponent(); 
      sw = new Stopwatch(); 
      sw2 = new Stopwatch(); 
      b = new ImageBrush(); 
      resolution = 100; 
     } 

     private void UserControl_Loaded(object sender, RoutedEventArgs e) 
     { 
      b.ImageSource = new BitmapImage(new Uri("pack://application:,,,/DataBinding;component/Images/249137482.png", UriKind.Absolute)); 
      this.Background = b; 
     } 

     private void UserControl_MouseUp(object sender, MouseButtonEventArgs e) 
     { 
      t = new Thread(new ThreadStart(ThreadTask)); 
      t.Start(); 
     } 

     private void ThreadTask() 
     { 
      while (!end) 
      { 
       count = count + 1; 
       Dispatcher.BeginInvoke(DispatcherPriority.Normal, (Action)delegate() 
       { 
        Application.Current.Windows.OfType<MainWindow>().First().datavalue = count; 
        Application.Current.Windows.OfType<MainWindow>().First().datavalue2 = count; 
       }); 

       Thread.Sleep((resolution - sw.Elapsed.TotalMilliseconds) > 0 ? TimeSpan.FromMilliseconds(resolution - sw.Elapsed.TotalMilliseconds):TimeSpan.FromMilliseconds(1)); 
       sw.Reset(); 
       sw.Start(); 
      } 
     } 
    } 
} 

如果我啓動應用程序,只要我點擊該控件,textbox1開始更新,而textbox2沒有,但如果我直接改變textbox2,它會拋出通知它已更改,所以數據綁定正常或者我認爲。

的問題是:

  • 如果與用戶控件的線程中直接調用,但是不能修改爲什麼它的工作?我認爲它與xaml文件中的textbox2聲明有關,因爲它搜索的是祖先,而usercontrol是一個孩子,但我不知道如何處理這個問題。

  • propertychanged屬性適用於該類的每個屬性,但是如果進行多線程處理不正確的值,並且錯誤地爲另一個事件處理,則不可能?

  • 我敢肯定,我不使用WPF的正確規範,也許這調用從用戶控件主窗口當的影響以某種方式的代碼,例如:S

對不起大代碼和問題的數量,但我開始喜歡WPF,並且當我沒有工作的時候,我有點受挫。我搜索了類似的問題,但只發現有人遇到問題時,他們不得不從主界面更新控件屬性,而不是像我一樣

回答

0

做了一些測試,我試圖向datavalue2屬性添加相同的條件在datavalue,多數民衆贊成它,更新

 public int datavalue2 
     { 
      get 
      { 
       return mydatavalue2; 
      } 
      set{ 
       mydatavalue2 = value; 
       if (PropertyChanged != null) 
       { 
        PropertyChanged(this, 
         new PropertyChangedEventArgs(
         "datavalue2")); 
       } 
      } 
     } 

令人驚訝它的工作althought它不應該是必要使用它的價值燒成後的屬性更改事件。我想知道,如果我直接修改它,拋出2個事件是否會造成一些不兼容問題,但是它運行良好,只有一個事件拋出了textbox2 ...但我確實對這種方法感到不安。

+0

這並不奇怪。引發'PropertyChanged'事件通知消費者單個*,*特定*屬性已經改變,並且只有該屬性的綁定會被更新。您的解決方案完全正確。 –

+0

是的,問題是我已經有了xaml中的路徑和updatesourcetrigger,並且如果直接從窗口修改它,它就可以完美工作,而無需親自觸發事件。如果我從usercontrol更新它,它不會做任何事情,除非我手動觸發事件 – Leucaruth

+0

是的:因爲當您的文本框控件更改時,它會觸發一個事件,以便綁定知道要更新。當你的財產發生變化時,你並沒有發起任何事件,所以綁定不知道要更新。 –

1

首先不要絕望! WPF對每個人都有一個陡峭的學習曲線。

一旦添加了屬性更改,您的方法就是正確的。要通知代碼中的變化,您必須提高它以讓UI知道。但是我發現代碼有點過於複雜。我想讓你知道另一種解決方案,以防您可能發現它有用

您可以將您的數據放入一個類,然後將其分配給您的xaml的DataContext。一旦你完成了這個工作,你就非常接近實現MVVM模式,我認爲這是使用WPF最好的方式。我把一些示例代碼

MainWindow.xaml

<Window x:Class="WpfApplication1.MainWindow" 
     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
     Title="MainWindow" Height="350" Width="525"> 
    <StackPanel> 
     <TextBox Text="{Binding Value1}"/> 
     <TextBox Text="{Binding Value2}"/> 
    </StackPanel> 
</Window> 

MainWindow.xaml.cs

using System.Windows; 

namespace WpfApplication1 
{ 
    public partial class MainWindow : Window 
    { 
     public MainWindow() 
     { 
      InitializeComponent(); 

      MyClass myClass = new MyClass(); 
      myClass.Value1 = 1; 
      myClass.Value2 = 2; 
      DataContext = myClass; 
     } 
    } 

    public class MyClass 
    { 
     public int Value1 { get; set; } 
     public int Value2 { get; set; } 
    } 
} 

對於孩子的用戶控件,您可以創建另一個類,並將其分配到該用戶控件的DataContext。這使事情保持在一起並且整齊。這也避免了需要執行「Application.Current.Windows.OfType()。First()。datavalue = count;」你可以直接修改你的綁定類

希望它有幫助

+0

如果要在UI中反映對「Value1」或「Value2」的更改,那麼您仍然需要爲「MyClass」實現'INotifyPropertyChanged' - 這是問題的具體問題。 –

+0

但是,我非常感謝你的答案,因爲它可以幫助我改進WPF的工作方式,因爲MVVM是基本的:) – Leucaruth

相關問題