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);
}
}
}
}
答案 0 :(得分:2)
首先,你做了很多错事。
您不应该使用窗口作为自己的datacontext,您应该有一个您设置的视图模型。
您不应该在视图中使用事件处理程序来操作viewmodel。您应该将按钮绑定到命令。
您的源代码似乎是“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);
}
}
}
并将此viewmodel设置为窗口的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>
答案 1 :(得分:1)
您将DataContext
设置为this
(窗口)。 HeaderText2
中没有名为DataContext
的属性,因此第二个绑定不起作用。
我会这样做(不会过多地改变你的代码,实际上我会采用适当的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>
答案 2 :(得分:1)
您的代码存在一些问题。 首先,你永远不会分配你的&#34;来源&#34;对于datacontext,所以你的第二个TextBlock无法找到&#34; HeaderText2&#34;的值。
但是,如果您要分配您的&#34;来源&#34;到textblocks datacontext然后我们可以获取&#34; HeaderText2&#34;的值。请考虑以下代码
<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命名,&#34; TextBlock2&#34;并且还从绑定中删除了&#34; Datacontext&#34; -part。
然后我们移动了你的&#34; Source&#34;从按钮事件到windows构造函数的对象(当我们想要做的只是更新属性时,每次单击按钮时都不需要创建新的对象)
public partial class DataContextSample : Window
{
public string HeaderText { set; get; }
private Source source { get; set; }
public DataContextSample()
{
...
source = new Source();
TextBlock2.DataContext = source;
...
}
...
}
然后在你的按钮click-event中,我们为你的数据绑定属性赋值为&#34; YES2&#34;。
private void btnUpdateSource_Click(object sender, RoutedEventArgs e)
{
...
source.HeaderText2 = "YES2";
}
还有一个细节。你的班级&#34;来源&#34;确实实现&#34; INotifyPropertyChanged&#34;,但它永远不会&#34;使用&#34;它。我的意思是,当你为你的财产分配一个价值&#34; HeaderText2&#34;你实际上从来没有&#34;通知&#34;用户改变了某些内容的UI,因此UI不会获取新值。请考虑以下代码:
public class Source : INotifyPropertyChanged
{
public string HeaderText2 { set
{
headerText2 = value;
OnPropertyChanged("HeaderText2");
}
get
{
return headerText2;
}
}
string headerText2;
...
}
因此,让我们看看我们对该属性&#34; HeaderText2&#34;所做的事情。每次&#34; HeaderText2&#34;获取一个赋值,它将首先将值保存在privat属性中(以便我们稍后可以读取它)。但除此之外,我们还称之为&#34; OnPropertyChanged&#34;使用我们的Propertys名称的方法。这种方法将反过来检查是否有人正在倾听&#34;到我们的&#34; PropertyChanged&#34; -event(因为我们在当前对象上有一个数据绑定,有人正在侦听),并创建一个新事件。
现在我们已经为你的文本块分配了一个数据源,其中包含一个&#34; HeaderText2&#34;的路径,当我们更新&#34; HeaderText2&#34;时,我们正在通知所有的听众。在数据源上,我们正在更新&#34; HeaderText2&#34;在按钮上单击事件。
快乐的编码!