2

学習中...firstNameなどのデータで正常に機能する単純なデータバインディングプロジェクトを作成しました。ただし、lastNameコンパイラを使用しようとすると、次のようにランタイムエラーがスローされます。

**現在のスレッドがスタックオーバーフロー状態にあるため、式を評価できません。**

これがコードです。ご覧のとおり、2番目のフィールド(姓)はスタックオーバーフローを引き起こしているため、コメントアウトされています。コメントをいただければ幸いです。

public partial class MainWindow : Window
{
    Person p;

    public MainWindow()
    {
        InitializeComponent();

        p = new Person();
        p.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(p_PropertyChanged);

        this.DataContext = p;

        p.FirstName = p.OriginalFirstName;
        p.LastName = p.OriginalLastName;
    }

    void p_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        stat1.Text = (p.OriginalFirstName == p.FirstName) ? "Original" : "Modified";
        //stat2.Text = (p.OriginalLastName == p.LastName) ? "Original" : "Modifined";
    }

}

編集:

 class Person : INotifyPropertyChanged 
    {

        public string OriginalFirstName = "Jim";
        public string OriginalLastName = "Smith";

        private string _firstName;

        #region FirstName
        public string FirstName
        {
            get { return _firstName; }
            set
            {
                if (value != null)
                {
                    _firstName = value;
                    NotifyTheOtherGuy(FirstName);
                }
            }
        }
        #endregion FirstName

        private string _lastName;

        #region LastName
        public string LastName
        {
            get { return _lastName; }
            set
            {
                if (value != null)
                {
                    _lastName = value;
                    NotifyTheOtherGuy(LastName);
                }
            }
        }
        #endregion LastName

        public Person()
        {

        }

        public event PropertyChangedEventHandler PropertyChanged;
        void NotifyTheOtherGuy(string msg)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(msg));
            }

        }

    }

XAML:

<Window x:Class="FullNameDataBinding.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="100"/>
            <ColumnDefinition Width="100"/>
        </Grid.ColumnDefinitions>

        <Label Grid.Column="0" Grid.Row="0" Content="First Name:"/>
        <Label Grid.Row="1" Content="Last Name:"/>
        <TextBox  Grid.Column="1" Grid.Row="0" Background="Yellow" Margin="5" FontWeight="Bold" Text="{Binding Path=FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
        <TextBlock x:Name="stat1" Grid.Column="2" />
        <TextBox x:Name="stat2" Grid.Column="1" Grid.Row="1" Background="Yellow" Margin="5" FontWeight="Bold" Text="{Binding Path=LastName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
        <TextBlock Grid.Column="2" Grid.Row="1" />
    </Grid>
</Window>
4

3 に答える 3

4

XAMLのこのチャンクにエラーがあると思います。

    <TextBox  Grid.Column="1" Grid.Row="0" Background="Yellow" Margin="5" FontWeight="Bold" Text="{Binding Path=FirstName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
    <TextBlock x:Name="stat1" Grid.Column="2" />
    <TextBox x:Name="stat2" Grid.Column="1" Grid.Row="1" Background="Yellow" Margin="5" FontWeight="Bold" Text="{Binding Path=LastName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
    <TextBlock Grid.Column="2" Grid.Row="1" />

前ではなく、最後TextBlockに持ってほしいと思います。x:Name="stat2"TextBox

を変更するLastNameと、PropertyChangedイベントハンドラーが呼び出され、のテキスト値が変更されますstat2stat2はバインディングを使用してTextBox値がバインドされるため、これにより、バインドメカニズムは設定した値をビューモデルに送り返します。これにより、別のPropertyChangedイベントが発生し、その値が変更されて、別のPropertyChangedイベントが発生します。この無限のサイクルは停止しないため、スタックオーバーフローエラーが発生します。LastNameTwoWaystat2

はプロパティにバインドされていないFirstNameため、このようなスタックオーバーフローstat1は発生しません。TextBlockText

于 2012-07-27T21:42:43.787 に答える
1

プロパティが変更されるたびに、別のプロパティ変更イベントを発生させるプロパティ(テキスト値)を変更します。これにより、テキストプロパティが変更され、...が発生します。

これがどこに向かっているのかわかりますか?

textプロパティを変更するときにイベントの発生を無効にするか、プロパティ変更イベントハンドラーのコンテキストでイベントを変更しないようにする必要があります。

クラスの詳細がPersonわからないため、イベントの発生を無効にするメカニズム、またはイベントを発生させずに値を変更するメカニズムがすでにサポートされているかどうかはわかりません。存在しない場合は、独自に作成する必要があります。それをどのように行うかは、そのクラスの実装によって異なります。

于 2012-07-27T19:20:34.487 に答える
0

p_PropertyChangedメソッドは静的である必要があり、メソッドに対する次の変更は問題なく機能すると思います。

static void p_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    MainWindow w = (MainWindow) sender;
    w.stat1.Text = (w.p.OriginalFirstName == w.p.FirstName) ? "Original" : "Modified";
    //stat2.Text = (p.OriginalLastName == p.LastName) ? "Original" : "Modifined";
}

ただし、XAMLコードの一部を投稿すると、よりクリーンな方法で同じ結果が得られる可能性があるため、より適切です。

WPFを使用する場合、WinFormプログラミングコードの慣例をほとんど忘れる必要があります。主にBindingsとDependenciesPropertyを使用してコードを作成する必要があると思います。

編集

  1. 他の誰かが言ったように、あなたはおそらくstat1名を間違ったオブジェクトに割り当て、これがStackOverflowExceptionをスローする無限の再帰を開始しました。
  2. Personクラスでは、PropertyChangedは、値ではなくプロパティの名前で呼び出す必要があります。

これは、その問題にとどまらない作業サンプルです。また、WPFプラットフォームを使用して、すべての主要な詳細タスクをViewクラスから引き出し、Modelクラスでそのフェーズを次のように移動できるようにする方法も示したいと思います。MVVMパラダイム

personクラスには、Checkプロパティが追加されています。これは、例外を発生させていた元のpropertychangedメソッドの状態を評価します。CheckFirstNameまたはCheckLastNameの変更に対してchangedイベントが発生した場合、クラス内で毎回FirstNameまたはLastNameプロパティが変更されます。このように、この目的でViewクラスの変更イベントを処理する必要はありません。これは、条件の評価がこのモデルクラスによってすでに行われており、結果が利用可能であり、バインドされたオブジェクトの準備ができているためです。

public class Person : INotifyPropertyChanged 
{

    public string OriginalFirstName = "Jim";
    public string OriginalLastName = "Smith";

    private string _firstName;

    #region FirstName
    public string FirstName
    {
        get { return _firstName; }
        set
        {
            if (value != null)
            {
                _firstName = value;
                NotifyTheOtherGuy("CheckFirstName");
            }
        }
    }
    #endregion FirstName

    private string _lastName;

    #region LastName
    public string LastName
    {
        get { return _lastName; }
        set
        {
            if (value != null)
            {
                _lastName = value;
                NotifyTheOtherGuy("CheckLastName");
            }
        }
    }
    #endregion LastName


    public string CheckFirstName
    {
        get
        {
            return (FirstName==OriginalFirstName) ? "Original": "Modified";
        }
    }
    public string CheckLastName
    {
        get
        {
            return (LastName==OriginalLastName) ? "Original": "Modified";
        }
    }

    public Person()
    {

    }

    public event PropertyChangedEventHandler PropertyChanged;
    void NotifyTheOtherGuy(string msg)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(msg));
        }

    }

}

MainWindowクラス:すべてのエラボレーションタスクがこのクラスから削除され、PersonオブジェクトのDependecyPropertyの定義のみがあります。

public partial class MainWindow : Window
{
    public static readonly DependencyProperty MyPersonProperty;
    static MainWindow()
    {
        MyPersonProperty = DependencyProperty.Register("MyPerson", typeof(Person), typeof(MainWindow));
    }
    Person MyPerson
    {
        set
        {
            SetValue(MyPersonProperty,value);
        }
        get
        {
            return GetValue(MyPersonProperty) as Person;
        }
    }
    public MainWindow()
    {
        MyPerson = new Person();

        InitializeComponent();
    }
}

MainWindow XAML:各コンポーネントはPersonDependencyPropertyに正しい方法でバインドされます。TextBoxesはPersonプロパティ値を更新するようにバインドされ、TextBlocksは(前述のように)Personクラスの他のプロパティが変更された後に変更を通知するCheckプロパティの結果をフェッチするようにバインドされます。

<?xml version="1.0" encoding="utf-8"?>
<Window
    x:Class="TryPrj.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:prj="clr-namespace:TryPrj"
    Title="TryPrj"
    Height="300"
    Width="300"
    x:Name="myWindow">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition
                Height="Auto" />
            <RowDefinition
                Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition
                Width="100" />
            <ColumnDefinition
                Width="100" />
            <ColumnDefinition
                Width="100" />
        </Grid.ColumnDefinitions>
        <Label
            Grid.Column="0"
            Grid.Row="0"
            Content="First Name:" />
        <Label
            Grid.Row="1"
            Content="Last Name:" />
        <TextBox
            Grid.Column="1"
            Grid.Row="0"
            Background="Yellow"
            Margin="5"
            FontWeight="Bold"
            Text="{Binding Path=MyPerson.FirstName, Mode=OneWayToSource, ElementName=myWindow, UpdateSourceTrigger=PropertyChanged}" />
        <TextBlock
            Grid.Column="2"
            Text="{Binding Path=MyPerson.CheckFirstName, Mode=OneWay, ElementName=myWindow}"
        />
        <TextBox
            Grid.Column="1"
            Grid.Row="1"
            Background="Yellow"
            Margin="5"
            FontWeight="Bold"
            Text="{Binding Path=MyPerson.LastName, Mode=OneWayToSource, ElementName=myWindow, UpdateSourceTrigger=PropertyChanged}" />
        <TextBlock
            Grid.Column="2"
            Grid.Row="1"
            Text="{Binding Path=MyPerson.CheckLastName, Mode=OneWay, ElementName=myWindow}" />
    </Grid>
</Window>
于 2012-07-27T19:31:10.713 に答える