5

ユーザーがパスワードを入力する場所と、ユーザーが確認のためにそのパスワードを再入力する必要がある場所の 2 つのパスワード フィールドを持つフォームがあります。検証は、両方のパスワードが一致することを確認するために使用されます。一致する場合、ボタンが有効になり、ユーザーは続行できます。

<PasswordBox Name="P1Box" src:PasswordBoxAssistant.BindPassword="True">
    <src:PasswordBoxAssistant.BoundPassword>
        <Binding Source="{StaticResource mybinding}" Path="Password.P1" UpdateSourceTrigger="PropertyChanged" Mode="TwoWay">
            <Binding.ValidationRules>
                <DataErrorValidationRule ValidatesOnTargetUpdated="True"/>
            </Binding.ValidationRules>
        </Binding>
    </src:PasswordBoxAssistant.BoundPassword>
</PasswordBox>

ボタンのスタイル:

<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
    <MultiDataTrigger>
        <MultiDataTrigger.Conditions>
            <Condition Binding="{Binding ElementName=P1Box, Path=(Validation.HasError)}" Value="false"/>
        </MultiDataTrigger.Conditions>
        <Setter Property="IsEnabled" Value="true"/>
    </MultiDataTrigger>
</Style.Triggers>

私の問題は、ユーザーが最初のボックスでパスワードを変更でき、2 番目のパスワード ボックスの再検証を強制しないことです。たとえば、最初のパスワードが "password" として入力され、ユーザーが 2 番目のボックスに "password" を入力すると、検証が成功し、ボタンが有効になります。その後、ユーザーが元のパスワード ボックスを "PASSWORD" に変更すると、両方のボックスが検証されたままになります。元のボックスは空でないパスワードに制約がないため、2 つ目は検証を強制的に更新するものがないためです。

私のパスワード ボックスは、ここで概説されている添付プロパティを使用して、パスワードへのバインドを許可します。このため、コード ビハインドでアクセスする方法を見つけることができません (PasswordBox.Password 自体は依存関係プロパティではないため)、このソリューションで表現されている方法で。または、添付されたプロパティでは機能しない可能性があります - 以下のコードは何もしませんでした:

P2Box.GetBindingExpression(PasswordBoxAssistant.BoundPassword).UpdateSource();

IDataErrorInfo を継承して 2 つのコントロール間の検証を可能にするカスタム クラスがあります。バインディングは PasswordData オブジェクトであり、パスワード ボックスは PasswordData.P1 と PasswordData.P2 に設定されています。

public class PasswordData : IDataErrorInfo
{
    public string P1 { get; set; }
    public string P2 { get; set; }
    public string Error { get { return string.Empty; } }
    public string this[string propertyName]
    {
        get
        {
            string ret;
            if (propertyName == "P1")
            {
            if (P1 == null || P1 == "")
                ret = "Password cannot be null or empty.";
            else
                ret = "";
            }
            else if (propertyName == "P2")
            {
            if (P2 == null || P2 == "")
                ret = "Password cannot be null or empty.";
            else if (P2 != P1)
                ret = "The passwords do not match.";
            else
                ret = "";
            }
            return ret;
        }
    }
}

PasswordChanged イベント中にホップして、新しい PasswordData を作成し、バインディングを再割り当てしようとしました。これで検証の問題は解決しますが、パスワードボックスのキャレットは常に先頭にあり、入力されたデータが台無しになります。

xaml のみのソリューションが必要ですが、コード ビハインドは完全に受け入れられます。それが重要な場合、私は.Net 4.0を使用しています。

編集:

さて、xaml でイベント ハンドラーの入力を間違えたことがわかりました。解決策は実際に機能します。

private void PasswordChanged(object sender, RoutedEventArgs e)
{
    binding.Pass.P1 = ((PasswordBox)sender).Password;
    P2Box.GetBindingExpression(PasswordBoxAssistant.BoundPassword).UpdateSource();
}

バインディングが更新される前にイベントが発生するため、バインディングを手動で更新する必要があります。

イベントにフックして手動で更新することなく、検証ルール、IDataErrorInfo、またはその他の手段を使用してコントロール間のバインドを行う適切な XAML のみの方法があるかどうか、私は興味があります。

4

2 に答える 2

3

より複雑な検証では、多くの場合、検証をViewModel、または多くの場合モデルにプッシュする必要があります。IDataErrorInfoは良いスタートです。

このテーマに関する優れた記事へのリンクは次のとおりです。http:
//msdn.microsoft.com/en-us/magazine/ff714593.aspx

于 2012-07-04T04:29:01.640 に答える
2

これは役に立つかもしれません。モデル クラスでデータ注釈が使用されている投稿をよく見かけます。モデルを WPF のコンテキストにバインドすると、データが入力されない前に検証が開始されることがあります。

これは問題になる可能性があり、バインディング式を反復処理するのは苦痛になる可能性があります。そのため、これを行うためにいくつかの拡張機能を配線しました。

これにより、TextBox のような Enumerable 型が取得されます。DependencyObject は、ウィンドウまたはグリッドである可能性があります。ウィンドウを参照するには、var window = Window.GetWindow(this) を使用します。必要に応じて、グリッドのようなコントロールを名前で使用することもできます。

  public static IEnumerable<T> FindVisualChildren<T>(DependencyObject obj) where T : DependencyObject
    {
        if (obj != null)
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(obj, i);
                if (child != null && child is T)
                {
                    yield return (T)child;
                }

                foreach (T childOfChild in FindVisualChildren<T>(child))
                {
                    yield return childOfChild;
                }
            }
        }
    }   

コントロールのリストを取得したら、"Revalidate" 拡張機能を使用してそれらを反復処理し、バインディング式を起動します。フォームにエラーがあったかどうかを示す bool を返します。T は、検証するタイプであることに注意してください。TextBox、ListBox など。以下に TextBox の例を 1 つ示します。使用するその他のコントロールを作成してから、「obj」を正しい型にキャストする必要があります。

        public static Boolean Revalidate<T>(this Window depObj) where T : DependencyObject
    {
        bool isValid = true;
        foreach (T obj in FindVisualChildren<T>(depObj))
        {
            var name = typeof(T).Name.ToLower();
            BindingExpression exp = null;

            switch (name)
            {
                case "textbox":
                    var tb = obj as TextBox;
                    exp = tb.GetBindingExpression(TextBox.TextProperty);
                    exp.UpdateSource();
                    if (Validation.GetHasError(tb))
                        isValid = false;  
                    break;           
            }                             
        }

        return isValid;
    }

...そしてそれを呼び出すには:

 valid = this.window.Revalidate<TextBox>(); // where this.window is a reference to our window.

そこから、true かどうかを確認し、失敗した場合は返すことができます。

if(!valid){
   return; // you could update a message or something as well obviously.
}
于 2013-03-07T07:01:16.533 に答える