![](/img/trans.png)
[英]INotifyPropertyChanged in singleton and binding to the WPF element
[英]WPF ~ Trouble with Binding & INotifyPropertyChanged
WPF n00bie在這里,試圖使他的UI正常工作。
所以我做了這個測試例子。 綁定到HeaderText1的文本塊在應用程序啟動時會正確更改,但是綁定到HeaderText2的文本塊在單擊按鈕后不會更新。
我究竟做錯了什么? 提前致謝!!
<Window x:Class="DataBinding.DataContextSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DataContextSample" Height="142.596" Width="310">
<StackPanel Margin="15">
<WrapPanel>
<TextBlock Text="Window title: " />
<TextBox Name="txtWindowTitle" Text="{Binding Title, UpdateSourceTrigger=Explicit}" Width="150" />
<Button Name="btnUpdateSource" Click="btnUpdateSource_Click" Margin="5,0" Padding="5,0">*</Button>
</WrapPanel>
<TextBlock Text="{Binding Path=DataContext.HeaderText}"></TextBlock>
<TextBlock Text="{Binding Path=DataContext.HeaderText2}"></TextBlock>
</StackPanel>
</Window>
主窗口類:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
namespace DataBinding
{
public partial class DataContextSample : Window
{
public string HeaderText { set; get; }
public DataContextSample()
{
HeaderText = "YES";
InitializeComponent();
this.DataContext = this;
}
private void btnUpdateSource_Click(object sender, RoutedEventArgs e)
{
BindingExpression binding = txtWindowTitle.GetBindingExpression(TextBox.TextProperty);
binding.UpdateSource();
Source source = new Source();
source.HeaderText2 = "YES2";
}
}
}
還有INotifyPropertyChanged類
using System.ComponentModel;
namespace DataBinding
{
public class Source : INotifyPropertyChanged
{
public string HeaderText2 { set; get; }
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
}
首先,您做錯了很多事情。
您不應該使用窗口,因為它是自己的數據上下文,應該設置一個視圖模型。
您不應在視圖中使用事件處理程序來操縱視圖模型。 您應該將按鈕綁定到命令。
您的源代碼似乎是“ viewmodel”,請考慮將其重命名為MainWindowViewModel(為清楚起見),然后執行此操作。
public class MainWindowViewModel : INotifyPropertyChanged
{
private string headerText;
private string headerText2;
private ICommand updateHeaderText2;
public string HeaderText
{
set
{
return this.headerText;
}
get
{
this.headerText = value;
// Actually raise the event when property changes
this.OnPropertyChanged("HeaderText");
}
}
public string HeaderText2
{
set
{
return this.headerText2;
}
get
{
this.headerText2 = value;
// Actually raise the event when property changes
this.OnPropertyChanged("HeaderText2");
}
}
public ICommand UpdateHeaderText2
{
get
{
// Google some implementation for ICommand and add the MyCommand class to your solution.
return new MyCommand (() => this.HeaderText2 = "YES2");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
並將此視圖模型設置為窗口的datacontext。
this.DataContext = new MainWindowViewModel();
然后在您的xaml中,您應該這樣綁定到viewmodel
<Window x:Class="DataBinding.DataContextSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DataContextSample" Height="142.596" Width="310">
<StackPanel Margin="15">
<WrapPanel>
<TextBlock Text="Window title: " />
<!-- Not sure what this binding is? -->
<TextBox Name="txtWindowTitle" Text="{Binding Title, UpdateSourceTrigger=Explicit}" Width="150" />
<Button Name="btnUpdateSource" Command="{Binding UpdateHeaderText2}" Margin="5,0" Padding="5,0">*</Button>
</WrapPanel>
<TextBlock Text="{Binding HeaderText}"></TextBlock>
<TextBlock Text="{Binding HeaderText2}"></TextBlock>
</StackPanel>
</Window>
您將DataContext
設置this
(窗口)。 您在DataContext
沒有名為HeaderText2
的屬性,因此第二個綁定將不起作用。
我會這樣做(無需過多更改您的代碼,實際上我會使用適當的MVVM方法):
public partial class DataContextSample : Window
{
public Source Source { get; set; }
public string HeaderText { set; get; }
public MainWindow()
{
InitializeComponent();
HeaderText = "YES";
Source = new Source { HeaderText2 = "YES" };
DataContext = this;
}
private void btnUpdateSource_Click(object sender, RoutedEventArgs e)
{
BindingExpression binding = txtWindowTitle.GetBindingExpression(TextBox.TextProperty);
if (binding != null)
{
binding.UpdateSource();
}
Source.HeaderText2 = "YES2";
}
}
我添加了一個名為Source
的新屬性,該屬性的類型為Source
。 在構造函數中將其初始HeaderText2
設置為相同的"YES"
,然后單擊按鈕將其更改為"YES2"
。
您還必須更改Source
類,以實際通知更改:
public class Source : INotifyPropertyChanged
{
private string _headerText2;
public string HeaderText2
{
get { return _headerText2; }
set
{
_headerText2 = value;
OnPropertyChanged("HeaderText2");
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
var e = new PropertyChangedEventArgs(propertyName);
handler(this, e);
}
}
}
然后在您的XAML中:
<StackPanel Margin="15">
<WrapPanel>
<TextBlock Text="Window title: " />
<TextBox Name="txtWindowTitle" Text="{Binding Title, UpdateSourceTrigger=Explicit}" Width="150" />
<Button Name="btnUpdateSource" Click="btnUpdateSource_Click" Margin="5,0" Padding="5,0">*</Button>
</WrapPanel>
<TextBlock Text="{Binding Path=HeaderText}"></TextBlock>
<TextBlock Text="{Binding Path=Source.HeaderText2}"></TextBlock>
</StackPanel>
好的,您的代碼存在一些問題。 首先,您永遠不會將“源”分配給數據上下文,因此第二個TextBlock無法找到“ HeaderText2”的值。
但是,如果您將“源”分配給文本塊數據上下文,那么我們可以獲取“ HeaderText2”的值。 考慮下面的代碼
<Window x:Class="DataBinding.DataContextSample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="DataContextSample" Height="142.596" Width="310">
<StackPanel Margin="15">
<WrapPanel>
<TextBlock Text="Window title: " />
<TextBox Name="txtWindowTitle" Text="{Binding Title, UpdateSourceTrigger=Explicit}" Width="150" />
<Button Name="btnUpdateSource" Click="btnUpdateSource_Click" Margin="5,0" Padding="5,0">*</Button>
</WrapPanel>
<TextBlock Text="{Binding Path=HeaderText}"></TextBlock>
<TextBlock Name="TextBlock2" Text="{Binding Path=HeaderText2}"></TextBlock>
</StackPanel>
</Window>
我們為您的第二個Textblock命名為“ TextBlock2”,並從綁定中刪除了“ Datacontext”部分。
然后,我們將“源”對象的創建從按鈕事件移到了Windows構造函數中(當我們要做的只是更新屬性時,無需每次單擊按鈕都創建一個新對象)
public partial class DataContextSample : Window
{
public string HeaderText { set; get; }
private Source source { get; set; }
public DataContextSample()
{
...
source = new Source();
TextBlock2.DataContext = source;
...
}
...
}
然后在您的按鈕單擊事件中,我們為您的數據綁定屬性分配一個值“ YES2”。
private void btnUpdateSource_Click(object sender, RoutedEventArgs e)
{
...
source.HeaderText2 = "YES2";
}
但是,還有更多細節。 您的類“ Source”確實實現了“ INotifyPropertyChanged”,但從未“使用”它。 我的意思是,當您為屬性“ HeaderText2”分配值時,您實際上從未“通知” UI發生了某些更改,因此UI不會獲取新值。 考慮下面的代碼:
public class Source : INotifyPropertyChanged
{
public string HeaderText2 { set
{
headerText2 = value;
OnPropertyChanged("HeaderText2");
}
get
{
return headerText2;
}
}
string headerText2;
...
}
因此,讓我們看一下對屬性“ HeaderText2”所做的工作。 每當“ HeaderText2”獲得分配的值時,它將首先將該值保存在privat屬性中(以便我們以后可以從中讀取)。 但是除此之外,我們還使用屬性名稱調用“ OnPropertyChanged”方法。 該方法將依次檢查是否有人“監聽”了我們的“ PropertyChanged”事件(並且由於我們對當前對象進行了數據綁定,因此有人正在監聽),並創建了一個新事件。
現在,我們已經為您的文本塊分配了一個帶有“ HeaderText2”路徑的數據源,當我們在數據源上更新“ HeaderText2”並且在按鈕click事件上更新“ HeaderText2”時,我們將通知所有偵聽器。
編碼愉快!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.