如何在可视化树下发送通知?
这是一个简单的代码示例:
class MyButton1 : Button
{
....
}
Generic.XAML如下所示:
<Style TargetType="{x:Type local:MyButton1}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyButton1}">
<Button>
<StackPanel>
<Label>
<local:MyTextBlock1 Text="Not working yet."/>
</Label>
</StackPanel>
</Button>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
没有VIEWMODEL。它是一个自定义控件,当单击MyButton1时,它应该通知MyTextBlock1将文本更改为“它正在工作”。 MyButton1位于可视树的顶层,而MyTextblock1位于深处。
那么如何在可视树下发送通知并在特定元素处理它们?在我的情况下,它的MyButton1和点击时,通知应沿着可视树向下移动,直到MyTextBlock1。然后应该处理通知并将文本更改为“它正在工作”。
答案 0 :(得分:1)
MVVM和DataBinding示例。
我简单的Person类
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
我简单的PersonViewModel类
public class PersonViewModel : INotifyPropertyChanged
{
public PersonViewModel(Person person)
{
if (person == null)
throw new ArgumentNullException("person");
this.Model = person;
this.ShowFirstNameCommand = new DelegateCommand((o) => this.showFirstName());
this.ShowLastNameCommand = new DelegateCommand((o) => this.showLastName());
}
public ICommand ShowFirstNameCommand { get; private set; }
public ICommand ShowLastNameCommand { get; private set; }
public Person Model { get; private set; }
public string FirstName
{
get
{
return this.Model.FirstName;
}
set
{
this.Model.FirstName = value;
this.OnPropertyChanged("FirstName");
}
}
public string LastName
{
get
{
return this.Model.LastName;
}
set
{
this.Model.LastName = value;
this.OnPropertyChanged("LastName");
}
}
private string _showString;
public string ShowString
{
get
{
return this._showString;
}
set
{
this._showString = value;
this.OnPropertyChanged("ShowString");
}
}
private void showFirstName()
{
this.ShowString = this.FirstName;
}
private void showLastName()
{
this.ShowString = this.LastName;
}
#region INPC code - can create an abstract base view model class and put this there instead
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
var handler = this.PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}
#endregion
}
使用按钮命令的DelegateCommand类
// Copyright © Microsoft Corporation. All Rights Reserved.
// This code released under the terms of the
// Microsoft Public License (MS-PL, http://opensource.org/licenses/ms-pl.html.)
using System;
using System.Windows.Input;
namespace WpfApplication1
{
public class DelegateCommand : ICommand
{
/// <summary>
/// Action to be performed when this command is executed
/// </summary>
private Action<object> executionAction;
/// <summary>
/// Predicate to determine if the command is valid for execution
/// </summary>
private Predicate<object> canExecutePredicate;
/// <summary>
/// Initializes a new instance of the DelegateCommand class.
/// The command will always be valid for execution.
/// </summary>
/// <param name="execute">The delegate to call on execution</param>
public DelegateCommand(Action<object> execute)
: this(execute, null)
{
}
/// <summary>
/// Initializes a new instance of the DelegateCommand class.
/// </summary>
/// <param name="execute">The delegate to call on execution</param>
/// <param name="canExecute">The predicate to determine if command is valid for execution</param>
public DelegateCommand(Action<object> execute, Predicate<object> canExecute)
{
if (execute == null)
{
throw new ArgumentNullException("execute");
}
this.executionAction = execute;
this.canExecutePredicate = canExecute;
}
/// <summary>
/// Raised when CanExecute is changed
/// </summary>
public event EventHandler CanExecuteChanged
{
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
/// <summary>
/// Executes the delegate backing this DelegateCommand
/// </summary>
/// <param name="parameter">parameter to pass to predicate</param>
/// <returns>True if command is valid for execution</returns>
public bool CanExecute(object parameter)
{
return this.canExecutePredicate == null ? true : this.canExecutePredicate(parameter);
}
/// <summary>
/// Executes the delegate backing this DelegateCommand
/// </summary>
/// <param name="parameter">parameter to pass to delegate</param>
/// <exception cref="InvalidOperationException">Thrown if CanExecute returns false</exception>
public void Execute(object parameter)
{
if (!this.CanExecute(parameter))
{
throw new InvalidOperationException("The command is not valid for execution, check the CanExecute method before attempting to execute.");
}
this.executionAction(parameter);
}
}
}
进入MainWindow.xaml的代码。
<StackPanel>
<Button Command="{Binding Path=ShowFirstNameCommand}">Click to show first name</Button>
<Button Command="{Binding Path=ShowLastNameCommand}">Click to show last name</Button>
<TextBox Text="{Binding Path=ShowString}" HorizontalAlignment="Stretch"/>
</StackPanel>
进入App.xaml.cs以将其粘合在一起的代码
公共部分类App:应用程序 { protected override void OnStartup(StartupEventArgs e) { base.OnStartup(E);
var personvm = new PersonViewModel( new Person
{
FirstName = "John",
LastName = "Smith"
});
var window = new MainWindow
{
DataContext = personvm
};
window.Show();
}
}
基本上我在屏幕上显示2个按钮和一个文本框。其中一个按钮将在单击时在文本框中显示某人的名字,另一个按钮将在同一文本框中显示该人的姓氏。如果您检查代码,您将看到我使用WPF命令实现此目的。按钮的Command属性绑定到PersonViewModel类的ICommand属性。然后,这些属性(使用DelegateCommand)“绑定”到同一viewmodel类中的私有方法。我还有一个Public ShowString属性,该属性绑定到屏幕上的Textbox,用于控制显示在Textbox中的字符串。您将看到viewmodel类中的私有方法更改此ShowString属性的值以更改显示在文本框中的数据。
我的xaml代码与您拥有的代码不同(您没有提供代码),但这个概念应该适合您。
希望这有帮助。
答案 1 :(得分:0)
您应该考虑在使用WPF进行开发时使用MVVM design pattern,并using an MVVM framework为您提供开箱即用的许多功能。
在MVVM模式中,您将拥有一个viewmodel,它是视图的数据上下文。您的视图将包含您的2个文本框和按钮。
MVVM框架将提供一些机制,用于在单击按钮时调用viewmodel上的方法。
您的viewmodel会公开绑定到视图中2个文本框的2个公共字符串属性。单击该按钮时,将调用viewmodel方法,并设置2个字符串属性的值,然后通过WPF数据绑定更新文本框中的文本。
这就是概念,但我想读一下MVVM,然后看看比较MVVM框架。
答案 2 :(得分:0)
示例背后的代码
在你的xaml中,
<Button Click="Button_Click_1">Click me</Button>
<TextBox Name="myTextBox" HorizontalAlignment="Stretch" />
在您的代码中,
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Button_Click_1(object sender, RoutedEventArgs e)
{
this.myTextBox.Text = "Hello World";
}
}
基本上这个概念是,如果你给控件命名,你可以通过直接调用名字来操纵后面代码中的属性,如我的例子所示。
答案 3 :(得分:0)
所以,我们假设您正在使用MVVM和DataBinding,让我们来看看您的可视化树...
某处你有一个按钮......
<Button OnClick="MyButtonOnClick">
// Your Button Content Here
</Button>
某处有一个TextBox,作为Button
的后代(在树中)。
<TextBox Binding="FirstName" />
我只展示View XAML的一些内容,以便您知道如何使用DataBind - 在代码隐藏中更新文本框的内容不是视图的工作。 这应该通过适当的数据绑定来处理,这不需要您更改可视树或设计。
ViewModel中的person类负责警告UI数据已更改。它看起来像这样:
public class Person
{
private proeprty _firstName;
public property FirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
NotifyPropertyChanged("FirstName"); // This is required to notify the UI. Specific implementation will depend on your property notification pattern.
}
}
正确绑定控件,例如TextBox
,“监听”绑定属性何时通知他们已更改,例如FirstName
类的Person
属性。所以在你的代码背后,你不需要指示文本框更新;他们受约束。绑定的文本框已经知道从它们绑定的属性中侦听更改通知。
但您仍然需要让代码在ViewModel中更新属性。这是在按钮单击事件处理程序中完成的:
public partial class MyView
{
... // All your other code
// Event Handler for the Button Click
public void MyButtonOnClick(object sender, RoutedEventArgs e)
{
Person myPerson = this.DataContext as Person;
myPerson.FirstName = "Some New Value"; // When I set this property, it will automatically notify any listeners that its contents have changed.
}
...
}
MVVM的这个开始示例说明了如何在应用程序层之间分离您的关注点。
View负责处理用户交互,向ViewModel发送数据和从ViewModel检索数据 - 其他任何东西都是超出范围的视图。
ViewModel负责发送/检索模型中的数据,并在数据发生变化时广播通知(通过用户交互或从ViewModel的其他部分更新的结果 - 其他任何东西都是超出范围的ViewModel .ViewModel不关心谁在听,它只关心通过通知广播更改。
当然,您的模型可以在数据库或其他存储库中为数据建模。它并不关心ViewModel的存在;这是ViewModel使用Model的工作。