向下发送VisualTree通知

时间:2013-02-21 23:23:19

标签: wpf

如何在可视化树下发送通知?

这是一个简单的代码示例:

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。然后应该处理通知并将文本更改为“它正在工作”。

4 个答案:

答案 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的工作。