C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[8]: ユーザーコントロールとPropertyChanged


(過去ログ 63 を表示中)

[トピック内 13 記事 (1 - 13 表示)]  << 0 >>

■36286 / inTopicNo.1)  ユーザーコントロールとPropertyChanged
  
□投稿者/ 倉田 有大 (604回)-(2009/05/26(Tue) 21:32:19)

分類:[.NET 全般] 

2009/05/26(Tue) 21:46:35 編集(投稿者)
こんばんは、倉田 有大です。

<UserControl x:Class="SkypeTRPGToolWPF.numericUpDown"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Height="25" Width="300">
    <Grid>
        <Grid.RowDefinitions>            
            <RowDefinition Height="*"/>            
            <RowDefinition Height="*"/>            
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
        </Grid.ColumnDefinitions>
        <TextBlock FontSize="20" Grid.RowSpan="2" Name="textBlock1" Grid.ColumnSpan="2" />
        <Button Grid.Row="0" Grid.Column="1" Height ="Auto" HorizontalAlignment="Right" Margin="0" Name="button1" Width="23" Click="button1_Click" VerticalAlignment="Top">↑</Button>
        <Button Grid.Row="1" Grid.Column="1" Height ="Auto" HorizontalAlignment="Right" Name="button2" Width="23" VerticalAlignment="Bottom" Click="button2_Click">↓</Button>
        <Border BorderThickness="1" Grid.RowSpan="2" BorderBrush ="#FF7F9DB9" Width="auto" Height="Auto"/>
    </Grid>
</UserControl>

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;

namespace SkypeTRPGToolWPF
{
    /// <summary>
    /// numericUpDown.xaml の相互作用ロジック
    /// </summary>
    public partial class numericUpDown : UserControl, INotifyPropertyChanged
    {
        public static readonly DependencyProperty ValueProperty =
    DependencyProperty.Register("Value", typeof(int), typeof(numericUpDown));

        public event PropertyChangedEventHandler PropertyChanged;
        protected virtual void OnPropertyChanged(string propertyName)
        {
            var h = PropertyChanged;
            if (h == null) return;

            h(this, new PropertyChangedEventArgs(propertyName));
        }

        private int _Value;
        public int Maximum
        {
            set;
            get;
        }
        public int Mininum
        {
            set;
            get;
        }
        public int Value
        {
            set
            {
                this.textBlock1.Text = value.ToString();
                _Value = value;
                OnPropertyChanged("Value");
                SetValue(ValueProperty, value);
            }
            get
            {
                return _Value;
            }
        }

        public numericUpDown()
        {
            Maximum = 100;
            Mininum = 0;            
            InitializeComponent();
            Value = 2;
        }

        private void button1_Click(object sender, RoutedEventArgs e)
        {
            if (Value < Maximum)
            {
                Value++;
                this.textBlock1.Text = Value.ToString();
            }
        }

        private void button2_Click(object sender, RoutedEventArgs e)
        {
            if (Value > Mininum)
            {
                Value--;
                this.textBlock1.Text = Value.ToString();
            }
        }
    }
}

上記のような、numericUpDownライクのコントロールを作成しました。
メインのウインドウに

<local:numericUpDown Value = "{Binding Path = DiceNum}" Width="auto" Grid.Row="2" Grid.Column="1" x:Name="numericUpDown1" />

上記のように貼り付けたのですが、ユーザーコントロールのPropertyChangedがnullのままでイベントが起こってくれません。
PropertyChangedにはどのタイミングで値が入るのでしょうか?

引用返信 編集キー/
■36287 / inTopicNo.2)  Re[1]: ユーザーコントロールとPropertyChanged
□投稿者/ 囚人 (361回)-(2009/05/26(Tue) 21:47:15)
DataContext が null なのでは?
今回の例では、
this.DataContext = this;
的なコードが必要です。

※Value っていうプロパティ名、あまりよろしくないですよね。
DependencyObject に SetValue と GetValue というメソッドがあるので、外からみたら「Value、GetValue、SetValue」というメンバがある。
引用返信 編集キー/
■36288 / inTopicNo.3)  Re[1]: ユーザーコントロールとPropertyChanged
□投稿者/ Hongliang (391回)-(2009/05/26(Tue) 21:49:37)
2009/05/26(Tue) 22:01:21 編集(投稿者)

// コードは流し読みですが。
バインディングの場合プロパティの set を呼び出したりしません。直接 DependencyObject.SetValue を呼び出します。
DependencyPropety.Register するときに、PropertyMetadata(とその派生クラス)のコンストラクタに PropertyChangedCallback デリゲートのインスタンスを渡してください。当然、このデリゲートの指すメソッドは static メソッドです。
// 依存関係プロパティの場合、INotifyPropertyChanged を使う場面ってあまりない気がします。


[追記]
んー、改めて見るとあんまり関係無かったかな。
PropertyChanged でなにが起こって欲しいんでしょうか?
たとえば、自分で
numericUpDown1.PropertyChanged += (s, e) => ....;
とか書けば普通に発生すると思いますが。
引用返信 編集キー/
■36292 / inTopicNo.4)  Re[2]: ユーザーコントロールとPropertyChanged
□投稿者/ 倉田 有大 (605回)-(2009/05/26(Tue) 22:19:38)
囚人さん、お返事ありがとうございます。

>this.DataContext = this;
>的なコードが必要です。

WVVCモデルなのでこれはおこなっているのですよー
他のコントロールのバインディングはちゃんとプロパティーに反映されるのですが。

>※Value っていうプロパティ名、あまりよろしくないですよね。

なるほど、numericUpDownからついValueを使ってしまいました。

Hongliangさん、お返事ありがとうございます。

public static readonly DependencyProperty ValueProperty =
    DependencyProperty.Register("Value", typeof(int), typeof(numericUpDown), new PropertyMetadata(new              PropertyChangedCallback(OnValuablePropertyChanged)));
上記のコードの変更と

private static void OnValuablePropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
{

}
を追加しました。
情けないことにここに何を書けばいいかわかりません。

<local:numericUpDown Value = "{Binding Path = DiceNum}" Width="auto" Grid.Row="2" Grid.Column="1" x:Name="numericUpDown1" />
上記のDiceNumプロパティーに値の変更時にイベントがこないんですよね。

<ComboBox Text = "{Binding Path = DiceKind}" Grid.Row="1" Grid.Column="1" FontSize="20" Name="comboBox1" VerticalAlignment="Top" />

コードを追加します。上記のように最初から用意されているコントロールは値の変更があると、バインドされているDiceKindプロパティーにイベントがくるのですが。
引用返信 編集キー/
■36293 / inTopicNo.5)  Re[3]: ユーザーコントロールとPropertyChanged
□投稿者/ 囚人 (362回)-(2009/05/26(Tue) 23:00:03)
「WVVCモデル」って何でしょ??「これはおこなっている」とは?
何がしたいコードかよく分からなくなってきた…。
Value プロパティは、DependencyProperty にしてるぐらいだから、このユーザーコントロールを使う側が設定するプロパティですよね?

><local:numericUpDown Value = "{Binding Path = DiceNum}" Width="auto" Grid.Row="2" Grid.Column="1" x:Name="numericUpDown1" />
>上記のDiceNumプロパティーに値の変更時にイベントがこないんですよね。

だったら、numericUpDown クラスが INotifyPropertyChanged を実装するんじゃなくて、DiceNum を持ってるクラスが実装するんじゃないですか?
Hongliang さんも仰ってますけど、numericUpDown クラスが INotifyPropertyChanged を実装する必要ないと思いますけど。

numericUpDown 自身がデータソースになるなら、INotifyPropertyChanged を実装して、最初に言った「this.DataSource = this;」が必要ですが。

引用返信 編集キー/
■36294 / inTopicNo.6)  Re[3]: ユーザーコントロールとPropertyChanged
□投稿者/ Hongliang (392回)-(2009/05/26(Tue) 23:00:27)
> <local:numericUpDown Value = "{Binding Path = DiceNum}" Width="auto" Grid.Row="2" Grid.Column="1" x:Name="numericUpDown1" />
> 上記のDiceNumプロパティーに値の変更時にイベントがこないんですよね。
>
> <ComboBox Text = "{Binding Path = DiceKind}" Grid.Row="1" Grid.Column="1" FontSize="20" Name="comboBox1" VerticalAlignment="Top" />
>
> コードを追加します。上記のように最初から用意されているコントロールは値の変更があると、バインドされているDiceKindプロパティーにイベントがくるのですが。

DiceKindプロパティにイベントがくる、というのは、この ComboBox にバインドしているオブジェクトの DiceKind 変更イベント(DiceKindChanged だか PropertyChanged だか知りませんが)が発生する、という意味でしょうか?
そして Value 変更時には DiceNum 変更イベントが発生しない、ということでいいのですか?

依存関係プロパティはデフォルトでは単方向(OneWay)バインディングです。バインディングソースの変更はバインディングターゲットに通知されますが、逆は通知されません。
NumericUpDown.Value がバインディングターゲット、DiceNum がバインディングソースですから、numericUpDown 側でいくら操作しても DiceNum には影響しません。
Binding を生成する際に明示的に Mode を TwoWay に指定すれば、双方向に変更が通知されるようになります。
そもそも NumericUpDown.Value が基本的に双方向にバインディングされるべきであるという場合、DependencyProperty.Register に渡す PropertyMetadata に、FrameworkPropertyMetadataOptions.BindsTwoWayDefault を追加した FrameworkPropertyMetadata を渡すことで、デフォルトのバインディングを双方向にすることができます。

いずれにせよ、INotifyPropertyChanged や PropertyChangedCallback の出番は(この局面では)ありません。

> private static void OnValuablePropertyChanged( DependencyObject d, DependencyPropertyChangedEventArgs e )
> {
>
> }
> を追加しました。
> 情けないことにここに何を書けばいいかわかりません。
DependencyObject がプロパティを持ってるオブジェクトです。つまり NumericUpDown オブジェクト。
INotifyPropertyChanged.PropertyChanged や ValueChanged を実装するなら、ここでイベントを発生させます。
// 私はこの静的メソッドからインスタンスメソッドに一旦移動するのを好みます。
引用返信 編集キー/
■36297 / inTopicNo.7)  Re[3]: ユーザーコントロールとPropertyChanged
□投稿者/ kazuto (8回)-(2009/05/26(Tue) 23:12:19)
kazuto さんの Web サイト
2009/05/26(Tue) 23:17:59 編集(投稿者)
2009/05/26(Tue) 23:14:58 編集(投稿者)

こんにちは。
依存関係プロパティはプロパティ変更通知をサポートしているので、今回の実装においてINotifyPropertyChangedの実装は必要ありません。まず、バインディングにおいてのプロパティ変更通知はどのような種類があるかを調べられた方が良いかと思います。

依存関係プロパティラッパー用のCLRプロパティですが、GetValueとSetValueの呼び出し以外の実装は行わないほうが良いです。
また今回のケースでPropertyChangedCallBackの実装は必要ないと思います。

それとボタンのClickイベントハンドラで行っているMaximumとMinimumの値の範囲チェックと値の設定はPropertyMetadataのCoerceValueCallBackで行うのが良いと思います。
今回ではTextBlockのTextプロパティとValueプロパティをバンドさせておく方が楽かと思います。

またGridコントロールは子要素を既定でストレッチする特性を持ったコントロールなので、Width="Auto"やHeight="Auto"なども必要ありません。RowDefinitionのHeightも既定で"*"です。XAMLはコード量がとても多くなるので、必要ない物は極力書かずにコードを見やすくする方が自分では良いかと思っています。
引用返信 編集キー/
■36309 / inTopicNo.8)  Re[4]: ユーザーコントロールとPropertyChanged
□投稿者/ 倉田 有大 (606回)-(2009/05/27(Wed) 01:52:51)
囚人さん、お返事ありがとうございます。

>「WVVCモデル」って何でしょ??「これはおこなっている」とは?

何かいてるんでしょうねすいません^^;
MODEL-VIEW-VIEWMODEL (MVVM) デザイン パターンのつもりでした。半分しかあってないw すいません、くたくただったもので、ぼけていました^^;

Hongliang さん

>依存関係プロパティはデフォルトでは単方向(OneWay)バインディングです。バインディングソースの変更はバインディングターゲットに通知されますが、逆は通知されません。

ありがとうございます。ビンゴでした。
<local:numericUpDown Value = "{Binding Path = DiceNum, Mode=TwoWay}" Width="auto" Grid.Row="2" Grid.Column="1" x:Name="numericUpDown1" />
これにて、動作してくれました。

http://blogs.wankuma.com/kazuki/archive/2008/04/28/135608.aspx

かずきさんのページ見て、デフォルトとTwoWay同じと、私が勝手に思い込んでいました。
ちゃんとかいてますね「そうじゃないTextBlockみたいな編集不可なものはOneWay的な動きをす」って^^;

デフォルトをTwoWayにすることもできるのでしょうか?ときこうとしたところ。

>そもそも NumericUpDown.Value が基本的に双方向にバインディングされるべきであるという場合、DependencyProperty.Register に渡す PropertyMetadata に、FrameworkPropertyMetadataOptions.BindsTwoWayDefault を追加した FrameworkPropertyMetadata を渡すことで、デフォルトのバインディングを双方向にすることができます。

できるんですね、先読みのお答えありがとうございます。
今回学んだことは、Bindingは依存プロパティーを使う。よってSetValueの実装ですね。

kazutoさん、おへんじありがとうございます。blogでおせわになっております。



>依存関係プロパティラッパー用のCLRプロパティですが、GetValueとSetValueの呼び出し以外の実装は行わないほうが良いです。

了解しました。内部のフィールドで持たさない方がいいと言うことですね。

>それとボタンのClickイベントハンドラで行っているMaximumとMinimumの値の範囲チェックと値の設定はPropertyMetadataのCoerceValueCallBackで行うのが良いと思います。

ありがとうございます、やってみます。

>RowDefinitionのHeightも既定で"*"です。XAMLはコード量がとても多くなるので、必要ない物は極力書かずにコードを見やすくする方が自分では良いかと思っています。

知りませんでしたありがとうございます。XAMLはコントロールをGUIで動かすと、またたくまにコードをたくさん挿入されてしまいますね。

みなさま、ありがとうございました。
一人でやってると後どれだけやって気づくかわからないし、さまざまなためになる情報がとてもありがたいです。掲示板のありがたさをひしひしと感じました。

このユーザーコントロールは完成したらblogのネタにしたいですね。少しでも還元になるかな?
引用返信 編集キー/
■36349 / inTopicNo.9)  Re[5]: ユーザーコントロールとPropertyChanged
□投稿者/ 倉田 有大 (607回)-(2009/05/27(Wed) 19:15:32)
こんにちは、倉田 有大です。
ユーザーコントロールのバインディングの方向を双方向にしようとし、下記のコードを書いたところ

public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(int), typeof(numericUpDown), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault),

window1.xaml' 行 29 位置 14 のオブジェクト 'System.Windows.Controls.Grid' にエラーがあります。

ユーザーコントロールを配置した行にエラーが表示されてしまいます。

nullのかわりに

public static readonly DependencyProperty ValueProperty =
DependencyProperty.Register("Value", typeof(int), typeof(numericUpDown), new FrameworkPropertyMetadata(5, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault),

数字を入れたところ、動作をするのですが、なぜか実行時に0になってしまいます。

ヒントをいただけたら幸いです。
引用返信 編集キー/
■36350 / inTopicNo.10)  Re[6]: ユーザーコントロールとPropertyChanged
□投稿者/ 囚人 (363回)-(2009/05/27(Wed) 19:27:12)
Value プロパティの型が int なのにデフォルト値に null を入れようとしているからとか?

>数字を入れたところ、動作をするのですが、なぜか実行時に0になってしまいます。

何がですか?デフォルト値が?
引用返信 編集キー/
■36353 / inTopicNo.11)  Re[7]: ユーザーコントロールとPropertyChanged
□投稿者/ 倉田 有大 (608回)-(2009/05/27(Wed) 19:36:35)
囚人さん。素早いお返事ありがとうございます。

>Value プロパティの型が int なのにデフォルト値に null を入れようとしているからとか?

その通りでした。いろんなところに、ストップさせたのですが、この場合コードからじゃ無理なのかな?

>何がですか?デフォルト値が?

もうしわけない、これも私の勘違いです。
プロパティーを捜したところ自分で0で設定し直してるところがありました。
解決済み
引用返信 編集キー/
■36354 / inTopicNo.12)  Re[6]: ユーザーコントロールとPropertyChanged
□投稿者/ Hongliang (397回)-(2009/05/27(Wed) 19:37:20)
2009/05/27(Wed) 19:38:13 編集(投稿者)

> public static readonly DependencyProperty ValueProperty =
> DependencyProperty.Register("Value", typeof(int), typeof(numericUpDown), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault),
>
> window1.xaml' 行 29 位置 14 のオブジェクト 'System.Windows.Controls.Grid' にエラーがあります。
>
> ユーザーコントロールを配置した行にエラーが表示されてしまいます。

int 型のプロパティに null を初期値として代入しようとすりゃ当然エラーでしょう。

> public static readonly DependencyProperty ValueProperty =
> DependencyProperty.Register("Value", typeof(int), typeof(numericUpDown), new FrameworkPropertyMetadata(5, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault),
>
> 数字を入れたところ、動作をするのですが、なぜか実行時に0になってしまいます。

バインディングソースの値が 0 なんじゃないですか?

// って完全に出遅れてた……。
引用返信 編集キー/
■36355 / inTopicNo.13)  Re[8]: ユーザーコントロールとPropertyChanged
□投稿者/ 倉田 有大 (609回)-(2009/05/27(Wed) 20:45:43)
Hongliangさん、すいません^^;お返事ありがとうございます。

>int 型のプロパティに null を初期値として代入しようとすりゃ当然エラーでしょう。

まったくですね。ただ、エラーがわかりにくいですね。XAMLの行ででるので。これは改善されないかな?

>バインディングソースの値が 0 なんじゃないですか?

はい、さがしたら自分で0にしていました^^;

しかし、わんくま掲示板に質問したら、その最中に答えが良く思い浮かびます。
もう、わからーーーーーーん!! とそのときは思うまで調べているのですが^^;
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -