0

問題の説明を始める前に、ターゲットフレームワークが.NET3.5であることに注意してください。

テキストがviewmodelプロパティにバインドされているテキストボックスがあります。私の要件は、ユーザーがテキストボックスに(キーボードとマウスの貼り付けを介して)何かを入力するときに、その中のすべてのジャンク文字をクリーンアップし、テキストボックスを置き換えられた文字列で更新する必要があることです[以下の例では's'が置き換えられます'h'を使用]。

XAMLCode:

 <Style x:Key="longTextField" TargetType="{x:Type TextBoxBase}">
            <Setter Property="SnapsToDevicePixels" Value="True"/>
            <Setter Property="OverridesDefaultStyle" Value="True"/>
            <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
            <Setter Property="AcceptsReturn" Value="True"/>
            <Setter Property="AllowDrop" Value="true"/>
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type TextBoxBase}">
                        <Border      
                      Name="Border"    
                      Padding="2"    
                      Background="Transparent"    
                      BorderBrush="LightGray"    
                      BorderThickness="1">
                            <ScrollViewer Margin="0" x:Name="PART_ContentHost"/>
                        </Border>
                        <ControlTemplate.Triggers>
                            <Trigger Property="IsEnabled" Value="False">
                                <Setter Property="Foreground" Value="Black"/>
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
 <TextBox Grid.Column="1" Grid.Row="2" Text="{Binding Value, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay, NotifyOnSourceUpdated=True, NotifyOnTargetUpdated=True}"  MinLines="3"       TextWrapping="Wrap"
                 SpellCheck.IsEnabled="True" Style="{StaticResource longTextField}"></TextBox>

ViewModelプロパティ:

     private string _value;
            public string Value
            {
                get
                {
                    return _value;
                }
                set
                {
                    if (_value == value) 
                        return;

                    _value = value;
//replaces 's' with 'h' and should update the textbox.
                    _value = _value.Replace('s','h');
                    RaisePropertyChanged(() => Value);
                }
            }

上記は単に私のために働いていません。ビューモデルのプロパティセッターが起動しています...値が置き換えられていますが、テキストボックスが更新されていません。紛らわしいのは、これが.Net4.0で完全に機能することです。

なぜこれが機能しないのか、そしてもちろん.NET 4.0にアップグレードする以外に、この問題の潜在的な解決策は何か知っていますか?

私の要件:

  • ユーザーは、複数行のテキストボックスに何でも入力および貼り付けることができます。

  • テキストには、テキストボックスに表示される前に変更する必要のあるジャンクを含めることができます。

よろしくお願いします、-マイク

4

1 に答える 1

6

私は非常によく似た問題に遭遇し、双方向バインディングが必要で、ViewModel の値を変更していて、TextBox で更新が表示されることを期待していました。解決することができました。私は .NET 4.0 を使用していましたが、基本的には同じ問題を抱えていたので、3.5 の状況でも試してみる価値があるかもしれません。

簡潔な答え:

私が遭遇したのは、表示されたテキストがその独自のプロパティ TextBox'sの値と同期しなくなっていたというバグでした。同様の質問に対する Meleak の回答は、これに対する手がかりとなり、Visual Studio 2010 のデバッガーと Meleak の手法を使用して、これを確認することができました。TextBox'sTextTextBlock

明示的なバインディングを使用して解決できました。これには、コード ビハインド (または、再利用しやすくするために最終的に行ったように、カスタム コントロール コード) で自分で問題UpdateSource()を処理する必要がありました。UpdateTarget()

さらなる説明:

明示的なバインディング タスクをどのように処理したかを次に示します。まず、バインドのソースを更新する TextChanged イベントのイベント ハンドラーを用意しました。

// Push the text in the textbox to the bound property in the ViewModel
textBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();

次に、TextBox の Loaded イベントのイベント ハンドラーを用意しました。そのハンドラーで、ViewModel の PropertyChanged イベントのハンドラーを登録しました (ViewModel はここでは "DataContext" でした)。

private void ExplicitBindingTextBox_Loaded(object sender, RoutedEventArgs e)
{
    TextBox textBox = sender as TextBox;

    if (textBox.DataContext as INotifyPropertyChanged == null)
        throw new InvalidOperationException("...");

    (textBox.DataContext as INotifyPropertyChanged).PropertyChanged +=
                  new PropertyChangedEventHandler(ViewModel_PropertyChanged);
}

最後に、PropertyChanged ハンドラーで、(UpdateTarget() を開始することによって) TextBox が ViewModel から値を取得するようにします。 これにより、TextBox は ViewModel から変更された文字列を取得します(この場合、文字が置き換えられたもの)。私の場合、(UpdateTarget()から)テキストを更新した後、ユーザーのキャレット位置を復元することも処理する必要がありました。ただし、その部分はあなたの状況に当てはまる場合と当てはまらない場合があります。

    /// <summary>
    /// Update the textbox text with the value that is in the VM.
    /// </summary>
    void ViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
    {
        // This textbox only cares about the property it is bound to
        if (e.PropertyName != MyViewModel.ValueStrPropertyName)
            return;

        // "this" here refers to the actual textbox since I'm in a custom control
        //  that derives from TextBox
        BindingExpression bindingExp = this.GetBindingExpression(TextBox.TextProperty);
        // the version that the ViewModel has (a potentially modified version of the user's string)
        String viewModelValueStr;

        viewModelValueStr = (bindingExp.DataItem as MyViewModel).ValueStr;


        if (viewModelValueStr != this.Text)
        {
            // Store the user's old caret position (relative to the end of the str) so we can restore it
            //  after updating the text from the ViewModel's corresponding property.
            int oldCaretFromEnd = this.Text.Length - this.CaretIndex;

            // Make the TextBox's Text get the updated value from the ViewModel
            this.GetBindingExpression(TextBox.TextProperty).UpdateTarget();

            // Restore the user's caret index (relative to the end of the str)
            this.CaretIndex = this.Text.Length - oldCaretFromEnd;
        }

    }
于 2013-01-31T15:51:18.097 に答える