1

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

这是简单的代码示例:

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

4 回答 4

1

MVVM 和数据绑定示例。

我简单的 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 以将其粘合在一起的代码

公共部分类应用程序:应用程序{受保护的覆盖无效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)到同一视图模型类中的私有方法。我还有一个 Public ShowString 属性,它是绑定到屏幕上我的文本框的数据,用于控制文本框中显示的字符串。您将看到我在 viewmodel 类中的私有方法更改了此 ShowString 属性的值,从而更改了文本框中显示的数据。

我的 xaml 代码与您所拥有的不同(您没有提供代码),但这个概念应该适合您。

希望这可以帮助。

于 2013-02-26T22:45:38.477 回答
0

在使用WPF 进行开发时,您应该考虑使用MVVM 设计模式,并使用 MVVM 框架为您提供许多开箱即用的功能。

在 MVVM 模式中,您将拥有一个视图模型,它是您的视图的数据上下文。您的视图将包含您的 2 个文本框和按钮。

MVVM 框架将提供一些机制,用于在单击按钮时调用视图模型上的方法。

您的视图模型将公开 2 个公共字符串属性,这些属性绑定到您视图上的 2 个文本框。单击按钮时,将调用 viewmodel 方法,并设置 2 个字符串属性的值,然后通过 WPF 数据绑定更新文本框中的文本。

这就是概念,但我会阅读 MVVM,然后看看比较 MVVM 框架。

于 2013-02-21T23:29:48.060 回答
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";
    }
}

基本上这个概念是,如果你给你的控件一个名字,你可以在后面的代码中通过直接调用这个名字来操纵它的属性,如我的例子所示。

于 2013-02-26T22:39:53.247 回答
0

因此,假设您正在使用带有 DataBinding 的 MVVM,让我们来看看您的可视化树...

某处你有一个按钮......

<Button OnClick="MyButtonOnClick">
// Your Button Content Here
</Button>

在某处你有一个文本框,作为你的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 检索数据——其他任何事情都超出了 View 的范围。

ViewModel 负责在 Model 中发送/检索数据,并在数据发生更改时广播通知(通过用户交互或由于 ViewModel 另一部分的更新 - 其他任何事情都超出了 ViewModel的范围. ViewModel 不关心谁在听,它只关心通过通知广播变化。

当然,您的模型可以对数据库或其他存储库中的数据进行建模。它不关心 ViewModel 的存在;使用模型是 ViewModel 的工作。

于 2013-02-26T23:07:16.747 回答