none
文字列の双方向バインディング? RRS feed

  • 質問

  •  

    Bindingに関する質問です。

     

    「ObservableCollection」をリスト表示して内容の変更も行う、という処理をBindingを使って実現しようとしていたのですが、データの表示はできるようになったのですが変更ができなくて困っています。

     

    実際のコードは以下のような感じです。

    Code Snippet

    XAML
        <Window.Resources>
            <Style TargetType="{x:Type ListBoxItem}">
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ListBoxItem}">
         <TextBox Text="{Binding Path=.,Mode=TwoWay}"/>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
            </Style>
        </Window.Resources>
     <Grid x:Name="LayoutRoot">
      <ListBox Margin="40" ItemsSource="{Binding}" >
      </ListBox>
      <Button HorizontalAlignment="Left" VerticalAlignment="Top" Width="94" Height="33" Content="Button" Click="Button_Click"/>
     </Grid>

     

     C#
     public Window1(){
      this.InitializeComponent();
      ObservableCollection<string> collection = new ObservableCollection<string>();
      collection.Add("AAAA");
      collection.Add("BBBB");
      this.DataContext = collection;
      }

     private void Button_Click(object sender, RoutedEventArgs e) {
      string mes = "";
      ObservableCollection<string> collection = (ObservableCollection<string>)this.DataContext;
      foreach (string s in collection) {
       mes += s + "\n";
      }
      MessageBox.Show(mes);
     }

     

     

     

    テキストボックスの内容を変更してボタンを押すと現在リストに表示されている内容がメッセージボックスに出る、という動作を期待しているのですが・・。
    stringを「stringを返すクラス」にしてあげれば実現できることは分かったのですが、できればstringのままで実現したいのです。

    実現方法がありましたら、教えていただけますでしょうか。
    よろしくお願いします。

     

    2008年4月2日 2:35

回答

  •  

    INotifyPropertyChanged インターフェース を実装することになってます。

     

     

    2008年4月3日 10:05
  •  

    MS の WPF のコードを覗いてみると,

    INotifyPropertyChanged の実装は,以下のパターンになっている(揃えている?)ようです。 

     

    Code Snippet

     

        class StringWrapper : INotifyPropertyChanged

        {

            public event PropertyChangedEventHandler PropertyChanged;

     

            public String Text

            {

                get { return this._string; }

                set

                {

                    if (this._string == value) { return; }

                    this._string = value;

                    OnPropertyChanged("Text");

                }

            }

     

            protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)

            {

                if (PropertyChanged != null)

                {

                    PropertyChanged(this, e);

                }

            }

     

            private void OnPropertyChanged(string propertyName)

            {

                OnPropertyChanged(new PropertyChangedEventArgs(propertyName));

            }

     

            private String _string;

        }

     

        class StringWrapperCollection : ObservableCollection<StringWrapper> { }

     

     

     

     

     

     

     

    2008年4月4日 2:51
  • プロパティを依存関係プロパティにしても実現可能だと思います。

    Code Snippet

    public class StringClass : DependencyObject
    {
        #region ValueProperty

        /// <summary>
        /// Value 依存関係プロパティを識別します。
        /// </summary>
        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register
        (
            "Value",
            typeof(string),
            typeof(StringClass)
        );

        /// <summary>
        /// 文字列の値を取得または設定します。
        /// </summary>
        public string Value
        {
            get { return (string)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        #endregion
    }


    ところで、モデルを上記のように DependencyObject ベースにしたほうがよい場合は、どのような場合でしょうか?
    ご存知の方は教えてください。
    2008年4月8日 1:12
  •  tngar さんからの引用

    ところで、モデルを上記のように DependencyObject ベースにしたほうがよい場合は、どのような場合でしょうか?
    ご存知の方は教えてください。

     

    個人的感覚と言えるかもしれませんが、

    ・BindingのTargetになるには、依存プロパティである必要があります。

    ・添付プロパティの値を持つ必要があるオブジェクトはDependencyObject である必要があります。

    この機能が必要な場合には、DependencyObject ベースが良いと思います。

    2008年4月9日 7:41
  • FC-Shiroさんのおっしゃってるような機能が必要ない場合は、逆にDependencyObjectにはしないほうがいいです。

    依存プロパティ等に対する操作は、インスタンスを作成したスレッドからじゃないとアクセス出来ないといった制限があるので。

     

    2008年4月10日 21:49

すべての返信

  •  

    自己レスです。

    今は以下のような感じでやってます。

    Code Snippet

    XAML
    <!--TextBoxのBinding Pathを変更-->
    <TextBox Text="{Binding Path=Value,Mode=TwoWay}"/>


    C#
    public Window1() {
        this.InitializeComponent();

        StringCollection collection = new StringCollection();
        collection.Add("AAAA");
        collection.Add("BBBB");

        this.DataContext = collection;
    }

    private void Button_Click(object sender, RoutedEventArgs e) {
        string mes = "";
        StringCollection collection = (StringCollection)this.DataContext;
        foreach (StringClass s in collection) {
            mes += s.Value + "\n";
        }
        MessageBox.Show(mes);
    }

    public class StringClass {
        private string _value;
        public StringClass(string s) {
            _value = s;
        }
        public string Value {
            get { return this._value; }
            set { this._value = value; }
        }
    }

    public class StringCollection : ObservableCollection<StringClass> {
        public void Add(string s) {
            base.Add(new StringClass(s));
        }
    }

     

     


    StringClassというクラスを作ってそのコレクションをBindingしてます。
    一応実現できてはいるのですが、これでいいのか悪いのか・・・自信がありません。

     

    上記方法に対する意見やその他の解決策等ありましたらぜひお願いします。

    2008年4月3日 0:57
  •  

    INotifyPropertyChanged インターフェース を実装することになってます。

     

     

    2008年4月3日 10:05
  • yayadonさんありがとうございます。

     

    Code Snippet

    public class StringClass : INotifyPropertyChanged {
        private string _value;
        public event PropertyChangedEventHandler PropertyChanged;

        public StringClass(string s) {
            _value = s;
        }
        public string Value {
            get { return this._value; }
            set {
                this._value = value;
                if (PropertyChanged != null) {
                    PropertyChanged(this, new PropertyChangedEventArgs(this._value));
                }
            }
        }
    }

     

     

    こんな感じでしょうか?

    2008年4月4日 1:32
  •  

    MS の WPF のコードを覗いてみると,

    INotifyPropertyChanged の実装は,以下のパターンになっている(揃えている?)ようです。 

     

    Code Snippet

     

        class StringWrapper : INotifyPropertyChanged

        {

            public event PropertyChangedEventHandler PropertyChanged;

     

            public String Text

            {

                get { return this._string; }

                set

                {

                    if (this._string == value) { return; }

                    this._string = value;

                    OnPropertyChanged("Text");

                }

            }

     

            protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)

            {

                if (PropertyChanged != null)

                {

                    PropertyChanged(this, e);

                }

            }

     

            private void OnPropertyChanged(string propertyName)

            {

                OnPropertyChanged(new PropertyChangedEventArgs(propertyName));

            }

     

            private String _string;

        }

     

        class StringWrapperCollection : ObservableCollection<StringWrapper> { }

     

     

     

     

     

     

     

    2008年4月4日 2:51
  • yayadonさんありがとうございます。

    ソースコードまで載せていただいて助かります。

    参考にさせていただきます。

     

    2008年4月4日 4:27
  • プロパティを依存関係プロパティにしても実現可能だと思います。

    Code Snippet

    public class StringClass : DependencyObject
    {
        #region ValueProperty

        /// <summary>
        /// Value 依存関係プロパティを識別します。
        /// </summary>
        public static readonly DependencyProperty ValueProperty = DependencyProperty.Register
        (
            "Value",
            typeof(string),
            typeof(StringClass)
        );

        /// <summary>
        /// 文字列の値を取得または設定します。
        /// </summary>
        public string Value
        {
            get { return (string)GetValue(ValueProperty); }
            set { SetValue(ValueProperty, value); }
        }

        #endregion
    }


    ところで、モデルを上記のように DependencyObject ベースにしたほうがよい場合は、どのような場合でしょうか?
    ご存知の方は教えてください。
    2008年4月8日 1:12
  •  tngar さんからの引用

    ところで、モデルを上記のように DependencyObject ベースにしたほうがよい場合は、どのような場合でしょうか?
    ご存知の方は教えてください。

     

    個人的感覚と言えるかもしれませんが、

    ・BindingのTargetになるには、依存プロパティである必要があります。

    ・添付プロパティの値を持つ必要があるオブジェクトはDependencyObject である必要があります。

    この機能が必要な場合には、DependencyObject ベースが良いと思います。

    2008年4月9日 7:41
  • FC-Shiroさんのおっしゃってるような機能が必要ない場合は、逆にDependencyObjectにはしないほうがいいです。

    依存プロパティ等に対する操作は、インスタンスを作成したスレッドからじゃないとアクセス出来ないといった制限があるので。

     

    2008年4月10日 21:49
  • こんにちは。森田 知良です。

    回答を寄せてくださったみなさん、大変有用な情報をありがとうございました。

     

    puniさん、フォーラムのご利用ありがとうございます。

    私の方で回答をくださったみなさんの情報は、有用な情報と判断しましたので、
    勝手ながら回答済みチェックをつけさせていただきました。

     

    回答済みチェックがつくことにより、有用な情報を探している方が情報を見つけやすくなります。
    有効な回答があった場合は、なるべく回答済みボタンを押してチェックを付けてくださるようお願いします。

     

    なお、puniさんの意図しない回答にチェックが付いていた場合は、
    回答済みチェックを解除することもできますのでご確認ください。


    それでは、これからもフォーラムのご利用をよろしくお願いいたします。

    2008年4月16日 6:05