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>

ビューモデルはありません。これはカスタム コントロールであり、MyButton1 がクリックされると、MyTextBlock1 にテキストを "It is working" に変更するよう通知する必要があります。MyButton1 はビジュアル ツリーの最上位にあり、MyTextblock1 はどこか深いところにあります。

では、ビジュアル ツリーに通知を送信し、特定の要素でそれらを処理するにはどうすればよいでしょうか。私の場合、その MyButton1 をクリックすると、通知はビジュアル ツリーを MyTextBlock1 まで移動するはずです。その後、通知が処理され、テキストが「動作しています」に変更されます。

4

4 に答える 4

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に入るコード

public部分クラスApp:Application {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つのボタンとテキストボックスを表示します。ボタンの1つは、クリックするとテキストボックスに人の名を表示し、もう1つは、同じテキストボックスに人の姓を表示します。コードを調べると、WPFコマンドを使用してこれを実現していることがわかります。ボタンのCommandプロパティは、PersonViewModelクラスのICommandプロパティにバインドされています。これらのプロパティは、(DelegateCommandを使用して)同じビューモデルクラスのプライベートメソッドに「バインド」されます。また、画面上のテキストボックスにバインドされたデータであるPublic ShowStringプロパティがあります。これは、テキストボックスに表示される文字列を制御するために使用されます。ビューモデルクラスのプライベートメソッドがこのShowStringプロパティの値を変更して、テキストボックスに表示されるデータを変更していることがわかります。

私のxamlコードはあなたが持っているものとは異なります(あなたはあなたのコードを提供しませんでした)が、概念はあなたのために働くはずです。

お役に立てれば。

于 2013-02-26T22:45:38.477 に答える
0

それでは、DataBinding で MVVM を使用していると仮定して、ビジュアル ツリーを見てみましょう...

どこかにボタンがあります...

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

の子孫(ツリー内)として、どこかに TextBox がありますButton

  <TextBox Binding="FirstName" />

DataBind の方法がわかるように、View XAML の一部のみを示します。コード ビハインドでテキスト ボックスの内容を更新するのは、ビューの仕事ではありません。これはすべて、ビジュアル ツリーやデザインを変更する必要のない適切なデータ バインディングを通じて処理する必要があります。

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ように、バインドされているプロパティが変更されたことを通知するときに「リッスン」します。したがって、コード ビハインドでは、テキスト ボックスに更新を指示する必要はありません。彼らは縛られています。バインドされたテキスト ボックスは、バインド先のプロパティからの変更通知をリッスンすることを既に認識しています。FirstNamePerson

ただし、コード ビハインドで 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 の最初の例は、アプリケーション レイヤー間で懸念事項をどのように分離すべきかを示しています。

ビューは、ユーザー操作の処理、ViewModel へのデータの送信および ViewModel からのデータの取得を担当します。それ以外は、ビューの範囲外です。

ViewModel は、Model 内のデータを送信/取得し、データが変更されたときに通知をブロードキャストする役割を果たします (ユーザーの操作によるか、ViewModel の別の部分からの更新の結果として - それ以外はViewModelの範囲外です)。 . ViewModel は、誰がリッスンしているかを気にせず、通知を通じて変更をブロードキャストすることのみを気にします。

もちろん、モデルは、データベースや他のリポジトリにあるデータをモデル化するためにあります。ViewModel の存在は気にしません。モデルを利用するのは ViewModel の仕事です。

于 2013-02-26T23:07:16.747 に答える
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

WPF で開発する場合は MVVM デザイン パターンを使用、MVVM フレームワークを使用してすぐに使用できる多くの機能を提供することを検討する必要があります。

MVVM パターンでは、ビューのデータ コンテキストであるビューモデルがあります。ビューには、2 つのテキスト ボックスとボタンが含まれます。

MVVM フレームワークは、ボタンがクリックされたときにビューモデルでメソッドを呼び出すためのメカニズムを提供します。

ビューモデルは、ビューの 2 つのテキスト ボックスにバインドされた 2 つのパブリック文字列プロパティを公開します。ボタンをクリックすると、viewmodel メソッドが呼び出され、2 つの文字列プロパティの値が設定され、WPF データ バインディングを介してテキスト ボックス内のテキストが更新されます。

それがコンセプトですが、MVVM を読んでから、MVVM フレームワークを比較してみましょう。

于 2013-02-21T23:29:48.060 に答える